diff --git a/CHANGELOG.md b/CHANGELOG.md index 8229a2069..4d4c4db4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ All notable changes to Bambuddy will be documented in this file. - **PyJWT CVE-2025-45768 (PYSEC-2025-183 / GHSA-65pc-fj4g-8rjx): permanently ignored in pip-audit** — Advisory is disputed by the PyJWT maintainers, with the advisory description literally noting *"this is disputed by the Supplier because the key length is chosen by the application that uses the library."* `fix_versions=[]` on the advisory confirms no PyJWT patch exists or will exist. Bambuddy is not affected: `backend/app/core/auth.py:184` auto-generates secrets via `secrets.token_urlsafe(64)` (~86 chars of entropy, far above any sane minimum) and the file-loaded path at `:177` rejects secrets shorter than 32 chars. Added a permanent `--ignore-vuln CVE-2025-45768` to `.github/workflows/security.yml` with an inline comment citing the file:line evidence so a future maintainer reviewing the ignore list sees why it's load-bearing. Also dropped the stale `--ignore-vuln CVE-2026-4539` for Pygments — Pygments has since shipped a patched version and the ignore is no longer load-bearing (verified: `pip-audit --ignore-vuln CVE-2025-45768` alone reports clean). ### Fixed +- **File Manager no longer polls the printer over FTPS every 30 seconds while open (#1480, reported by @OscarsWorldTech)** — The reporter's P1S churned through MQTT disconnect/reconnect cycles and timelapse downloads silently failed. The support bundle showed the real picture: during the churn windows, MQTT (`Connection stale - no message for 60.2s`), FTPS (`_ssl.c:1015: The handshake operation timed out`) and the camera all timed out *together* and recovered together — the P1S's embedded controller saturating, not a network fault (wifi -44 dBm, Docker host networking). A visible contributor on Bambuddy's side: `FileManagerModal.tsx` ran its `getPrinterFiles` query with `refetchInterval: 30000`, so every 30 s while the File Manager modal sat open it opened a *fresh* FTPS connection — full TLS handshake — to re-list the current directory. A printer's file list doesn't change on its own; it only changes on upload / delete (the modal's mutations already `invalidateQueries`) or when a print finishes. The blind 30 s poll was pure load, and on a fragile controller like the P1S it was enough to tip MQTT and FTP into the timeouts above. **Fix**: the `refetchInterval` is removed. The listing still refreshes on modal open, on directory / tab change (the path is in the query key), after every upload / delete, and via the existing manual Refresh button — so nothing stops updating, the printer just isn't hammered. Reduces steady-state FTPS connection load while the modal is open from one handshake every 30 s to zero. 19 FileManagerModal tests green; frontend build clean. +- **STL thumbnail generation failures now log a full traceback** — Surfaced by the #1480 support bundle: every STL in the reporter's library failed thumbnail generation with `unsupported operand type(s) for /: 'str' and 'str'`, but `generate_stl_thumbnail`'s `except` handler logged only the bare exception message — no traceback, no line number. The fault could not be reproduced from a clean STL across path shapes (`#` and spaces in the path), `str` vs `Path` arguments, or large meshes that exercise `simplify_quadric_decimation`, so it is data- or environment-specific and the message alone is not enough to locate it. `stl_thumbnail.py` now passes `exc_info=True` on that warning, so the next support bundle carries the traceback and the exact failing line. No behaviour change to thumbnail generation itself. - **Slicer: the Process / Filament dropdowns now filter by printer using the uploaded Slicer Bundles instead of guessing from preset names (#1325, reported by @IndividualGhost1905)** — After the printer-preset pre-selection landed, the reporter found the Process Profile dropdown still showed a flat mix of `@BBL X1C` and `@BBL P2S` presets with the printer set to X1C — P2S presets that should have dropped into the trailing "Other printers" group sat in the main list. **Root cause**: `frontend/src/utils/slicerPrinterMatch.ts` resolved each cloud / standard preset's printer by parsing the `@BBL ` suffix of its name against a hard-coded `KNOWN_MODEL_CODES` allow-list. That list (`X1C, X1E, X1, P1S, P1P, A1M, A1, H2D, H2S`) was missing `P2S` (and `H2C`, `X2D`), so every `@BBL P2S` preset parsed to an empty model-code set, `presetCompatibility` returned `unknown`, and the dropdown keeps `unknown` presets in the main list (only `mismatch` moves to "Other printers"). It was a maintenance trap by construction: every new Bambu model silently broke filtering until someone edited the list. **Fix**: the name-suffix heuristic and both hard-coded model tables (`KNOWN_MODEL_CODES`, `PRINTER_NAME_PATTERNS`) are removed. Compatibility is now read from ground truth — the user's uploaded Slicer Bundles (`.bbscfg`). Each bundle is scoped to one printer and lists the process / filament presets it ships, so "process P works with printer X" holds exactly when some uploaded bundle for printer X contains P. `buildCompatibilityIndex` builds a `presetName → {printer names}` index per slot from `GET /slicer/bundles` (already fetched by the modal), and `presetCompatibility` consults it — still preferring an imported preset's own `compatible_printers` list when present. A newly released Bambu model is covered the moment its bundle is uploaded, with no code change. Presets no bundle covers stay in the main list (`unknown` is never hidden), so a user with no bundle imported sees the un-filtered list rather than a wrong one. `printerPresetCode` / `presetModelCodes` are gone; `SliceModal` passes the bundle-derived index to `PresetDropdown`, `pickProcessDefault`, and `pickFilamentForSlot` in place of the old model code. **Tests**: `slicerPrinterMatch.test.ts` rewritten — 12 tests covering `buildCompatibilityIndex` (per-printer mapping, multi-bundle union, `# ` user-clone-prefix stripping, empty-printer skip) and `presetCompatibility` (imported-tier `compatible_printers` exact match, bundle-driven match / mismatch / unknown, the #1325 P2S-into-X1C repro, no-bundles and no-printer-selected cases). 32 SliceModal tests green; frontend build clean; backend ruff clean. - **PWA: Bambuddy can now be installed as an app on Android, and the font is self-hosted (#1460, reported by @Soopahfly)** — Reporter could install Bambuddy as a PWA on desktop but not on an Android phone (Pixel 9 Pro XL, failing in both Chrome and Edge). A thorough remote-DevTools trace confirmed the manifest was valid, all icons/screenshots present, and the service worker activated, running, and controlling the page — DevTools reported no installability blockers — yet no install prompt ever appeared. **Two root causes, both Bambuddy-side.** **(1) No in-app install trigger.** Chrome for Android removed the automatic install mini-infobar in Chrome 108 (2022); since then a site must either listen for `beforeinstallprompt` and surface its own button, or the user must dig into the browser's ⋮ menu. Desktop Chrome still auto-shows the omnibox install icon, which is exactly why desktop "worked" and Android didn't. Bambuddy had no `beforeinstallprompt` handler at all, so on Android there was no discoverable install path. New `frontend/src/components/InstallAppButton.tsx` captures the `beforeinstallprompt` event (calling `preventDefault()` so the button is the single predictable entry point), re-fires it on click, shows a success toast on accept, and clears the captured prompt afterwards (a prompt can only be used once). It renders nothing when there is no pending prompt — already installed, unsupported browser, or iOS Safari (no programmatic install) — so it never shows a dead button. Added to both the expanded and compact sidebar-footer rows in `Layout.tsx`, next to the GitHub link. New `nav.installApp` / `nav.installAppSuccess` i18n keys with real translations in all 9 locales (en/de/es/fr/it/ja/pt-BR/zh-CN/zh-TW). **(2) Inter font loaded from the Google Fonts CDN.** `frontend/src/index.css` pulled Inter via `@import url('https://fonts.googleapis.com/css2?...')`. For a local-first, offline-capable PWA this is wrong: it leaks a request to Google on every load, breaks the UI font when offline, and — as the reporter's trace showed — triggered CSP console errors (`connect-src` doesn't allow `fonts.googleapis.com`). The service worker made it worse: a request to `fonts.googleapis.com/css2` has path `/css2` (no `.css` extension), so it missed the CSS branch, fell through to the catch-all HTML branch, and on failure was answered with the cached `index.html` — which is why the font request came back as `text/html`. **Fix**: the two Inter variable woff2 files (latin + latin-ext, one file covers every weight via the variable axis) are now bundled in `frontend/public/fonts/` and declared with `@font-face` in `index.css`, served same-origin. The service worker now (a) skips all cross-origin requests entirely — letting the browser handle them so a failed cross-origin fetch can never be answered with `index.html` again — (b) caches the font files in `STATIC_ASSETS` and via a `.woff2` / `/fonts/` match in the cache-first branch so the UI renders offline, and (c) bumps its cache version. With Inter self-hosted, `fonts.googleapis.com` and `fonts.gstatic.com` were dropped from the SPA and gcode-viewer CSP directives in `backend/app/main.py` (the `/docs` ReDoc/Swagger CSP keeps them — that third-party UI genuinely loads Google Fonts). Frontend build clean, i18n parity green across 9 locales, backend ruff clean, 17 security-header tests green. - **Flow Calibration now actually runs when the print option is enabled (#1478, reported by @andreirusu99)** — Reporter on an H2S saw poor extrusion around corners (classic too-high K factor); the printer's flow-dynamics calibration step never appeared in the pre-print checklist even with **Flow Calibration** toggled on in the Re-print dialog, while the same 3MF printed from Bambu Studio did calibrate. **Root causes, both in the `project_file` command built by `start_print` (`backend/app/services/bambu_mqtt.py`)**: (1) `extrude_cali_flag` was hardcoded to `0`. A BambuStudio request-topic capture from a real H2D (plus X1C and P2S captures) shows BambuStudio always sends `1` (run flow-dynamics calibration) or `2` (skip, reuse the stored PA value), paired with `flow_cali`, and **never** `0` — so the printer skipped calibration regardless of the toggle. (2) An earlier revision integer-encoded the calibration/leveling fields (`timelapse`, `bed_leveling`, `flow_cali`, `vibration_cali`, `layer_inspect`) for the H2 family (H2D/H2S/H2C/X2D) on the belief that H2 firmware required `0/1`; the same H2D capture disproves this — BambuStudio sends plain JSON booleans for every model. The "integer required" claim conflated these fields with `use_ams`, which genuinely must stay boolean (H2D Pro reads an integer `use_ams` as a nozzle index — the actual #1386 cause). **Fix**: `extrude_cali_flag` is now `1 if flow_cali else 2`, and the five calibration/leveling fields are sent as JSON booleans for all models, matching BambuStudio's wire format exactly. The H-family integer-conversion branch (`is_h_family`) is removed. `use_ams` is unchanged. This affects only the outbound print command; the virtual-printer inbound coercion of slicer integer `0/1` flags (#1403) is a separate path and untouched. **Tests**: in `test_bambu_mqtt.py`, the two tests that asserted the integer format (`test_x2d_uses_integer_format_for_calibration_fields`, `test_h2s_keeps_integer_format_for_calibration_fields`) are corrected to assert booleans and renamed; all three model tests (X2D/H2S/P2S) now also assert `extrude_cali_flag` is `1` when flow cali is on and `2` when off. 381 mqtt + virtual-printer tests green; backend ruff clean. diff --git a/backend/app/services/stl_thumbnail.py b/backend/app/services/stl_thumbnail.py index 7d56e296b..d383f1aa0 100644 --- a/backend/app/services/stl_thumbnail.py +++ b/backend/app/services/stl_thumbnail.py @@ -142,5 +142,10 @@ def generate_stl_thumbnail( logger.warning("STL thumbnail generation unavailable (missing dependencies): %s", e) return None except Exception as e: - logger.warning("Failed to generate STL thumbnail for %s: %s", stl_path, e) + # Log the traceback, not just the message: a bare + # "unsupported operand type(s) for /: 'str' and 'str'" gives no clue + # which line failed, and the fault is data-/environment-specific + # enough that it can't be reproduced from a clean STL — the traceback + # in the next support bundle is what pinpoints it (#1480). + logger.warning("Failed to generate STL thumbnail for %s: %s", stl_path, e, exc_info=True) return None diff --git a/frontend/src/components/FileManagerModal.tsx b/frontend/src/components/FileManagerModal.tsx index 94d77a100..c33560a12 100644 --- a/frontend/src/components/FileManagerModal.tsx +++ b/frontend/src/components/FileManagerModal.tsx @@ -300,10 +300,15 @@ export function FileManagerModal({ printerId, printerName, onClose }: FileManage return () => window.removeEventListener('keydown', handleKeyDown); }, [onClose]); + // No auto-poll: every refetch opens a fresh FTPS connection (TLS handshake + // and all) to the printer, and a 30s interval saturated fragile printer + // controllers like the P1S — MQTT, FTP and the camera all timed out + // together while this modal sat open (#1480). A printer's file list only + // changes on upload / delete (the mutations below invalidate the query) + // or when a print finishes; the manual Refresh button covers the rest. const { data, isLoading, refetch } = useQuery({ queryKey: ['printerFiles', printerId, currentPath], queryFn: () => api.getPrinterFiles(printerId, currentPath), - refetchInterval: 30000, }); const { data: storageData } = useQuery({ diff --git a/static/assets/index-B2ysIPjO.js b/static/assets/index-B7aP7po9.js similarity index 99% rename from static/assets/index-B2ysIPjO.js rename to static/assets/index-B7aP7po9.js index cb67f37f5..17c9a2988 100644 --- a/static/assets/index-B2ysIPjO.js +++ b/static/assets/index-B7aP7po9.js @@ -8437,7 +8437,7 @@ void main() { `};class zee extends Xh{constructor(e){super({type:"LineMaterial",uniforms:s4.clone(Go.line.uniforms),vertexShader:Go.line.vertexShader,fragmentShader:Go.line.fragmentShader,clipping:!0}),this.isLineMaterial=!0,this.setValues(e)}get color(){return this.uniforms.diffuse.value}set color(e){this.uniforms.diffuse.value=e}get worldUnits(){return"WORLD_UNITS"in this.defines}set worldUnits(e){e===!0?this.defines.WORLD_UNITS="":delete this.defines.WORLD_UNITS}get linewidth(){return this.uniforms.linewidth.value}set linewidth(e){this.uniforms.linewidth&&(this.uniforms.linewidth.value=e)}get dashed(){return"USE_DASH"in this.defines}set dashed(e){e===!0!==this.dashed&&(this.needsUpdate=!0),e===!0?this.defines.USE_DASH="":delete this.defines.USE_DASH}get dashScale(){return this.uniforms.dashScale.value}set dashScale(e){this.uniforms.dashScale.value=e}get dashSize(){return this.uniforms.dashSize.value}set dashSize(e){this.uniforms.dashSize.value=e}get dashOffset(){return this.uniforms.dashOffset.value}set dashOffset(e){this.uniforms.dashOffset.value=e}get gapSize(){return this.uniforms.gapSize.value}set gapSize(e){this.uniforms.gapSize.value=e}get opacity(){return this.uniforms.opacity.value}set opacity(e){this.uniforms&&(this.uniforms.opacity.value=e)}get resolution(){return this.uniforms.resolution.value}set resolution(e){this.uniforms.resolution.value.copy(e)}get alphaToCoverage(){return"USE_ALPHA_TO_COVERAGE"in this.defines}set alphaToCoverage(e){this.defines&&(e===!0!==this.alphaToCoverage&&(this.needsUpdate=!0),e===!0?(this.defines.USE_ALPHA_TO_COVERAGE="",this.extensions.derivatives=!0):(delete this.defines.USE_ALPHA_TO_COVERAGE,this.extensions.derivatives=!1))}}const jV=new yc,A1=new yt;class Uee extends KEe{constructor(){super(),this.isLineSegmentsGeometry=!0,this.type="LineSegmentsGeometry",this.setIndex([0,2,1,2,3,1,2,4,3,4,5,3,4,6,5,6,7,5]),this.setAttribute("position",new bi([-1,2,0,1,2,0,-1,1,0,1,1,0,-1,0,0,1,0,0,-1,-1,0,1,-1,0],3)),this.setAttribute("uv",new bi([-1,2,1,2,-1,1,1,1,-1,-1,1,-1,-1,-2,1,-2],2))}applyMatrix4(e){const a=this.attributes.instanceStart,r=this.attributes.instanceEnd;return a!==void 0&&(a.applyMatrix4(e),r.applyMatrix4(e),a.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this}setPositions(e){let a;e instanceof Float32Array?a=e:Array.isArray(e)&&(a=new Float32Array(e));const r=new dR(a,6,1);return this.setAttribute("instanceStart",new wh(r,3,0)),this.setAttribute("instanceEnd",new wh(r,3,3)),this.computeBoundingBox(),this.computeBoundingSphere(),this}setColors(e){let a;e instanceof Float32Array?a=e:Array.isArray(e)&&(a=new Float32Array(e));const r=new dR(a,6,1);return this.setAttribute("instanceColorStart",new wh(r,3,0)),this.setAttribute("instanceColorEnd",new wh(r,3,3)),this}fromWireframeGeometry(e){return this.setPositions(e.attributes.position.array),this}fromEdgesGeometry(e){return this.setPositions(e.attributes.position.array),this}fromMesh(e){return this.fromWireframeGeometry(new BEe(e.geometry)),this}fromLineSegments(e){const a=e.geometry;return this.setPositions(a.attributes.position.array),this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new yc);const e=this.attributes.instanceStart,a=this.attributes.instanceEnd;e!==void 0&&a!==void 0&&(this.boundingBox.setFromBufferAttribute(e),jV.setFromBufferAttribute(a),this.boundingBox.union(jV))}computeBoundingSphere(){this.boundingSphere===null&&(this.boundingSphere=new iu),this.boundingBox===null&&this.computeBoundingBox();const e=this.attributes.instanceStart,a=this.attributes.instanceEnd;if(e!==void 0&&a!==void 0){const r=this.boundingSphere.center;this.boundingBox.getCenter(r);let i=0;for(let s=0,o=e.count;sP&&js.z>P)continue;if(As.z>P){const I=As.z-js.z,z=(As.z-P)/I;As.lerp(js,z)}else if(js.z>P){const I=js.z-As.z,z=(js.z-P)/I;js.lerp(As,z)}As.applyMatrix4(g),js.applyMatrix4(g),As.multiplyScalar(1/As.w),js.multiplyScalar(1/js.w),As.x*=v.x/2,As.y*=v.y/2,js.x*=v.x/2,js.y*=v.y/2,Ms.start.copy(As),Ms.start.z=0,Ms.end.copy(js),Ms.end.z=0;const F=Ms.closestPointToPointParameter(F2,!0);Ms.at(F,DV);const k=xee.lerp(As.z,js.z,F),E=k>=-1&&k<=1,U=F2.distanceTo(DV)<.5*sg;if(E&&U){Ms.start.fromBufferAttribute(S,T),Ms.end.fromBufferAttribute(_,T),Ms.start.applyMatrix4(b),Ms.end.applyMatrix4(b);const I=new yt,z=new yt;Fd.distanceSqToSegment(Ms.start,Ms.end,z,I),f.push({point:z,pointOnLine:I,distance:Fd.origin.distanceTo(z),object:m,face:null,faceIndex:T,uv:null,uv1:null})}}})(this,i,a)))}}class m2e extends $T{constructor(e,a,r,i,s=4473924,o=8947848){s=new La(s),o=new La(o);const l=Math.round(e/a);r=Math.round(r/i)*i/2;const c=[],d=[];let u=0;for(let p=-1*(e=l*a/2);p<=e;p+=a){c.push(p,0,-1*r,p,0,r);const f=p===0?s:o;f.toArray(d,u),u+=3,f.toArray(d,u),u+=3,f.toArray(d,u),u+=3,f.toArray(d,u),u+=3}for(let p=-1*r;p<=r;p+=i){c.push(-1*e,0,p,e,0,p);const f=p===0?s:o;f.toArray(d,u),u+=3,f.toArray(d,u),u+=3,f.toArray(d,u),u+=3,f.toArray(d,u),u+=3}const m=new co;m.setAttribute("position",new bi(c,3)),m.setAttribute("color",new bi(d,3)),super(m,new $S({vertexColors:!0,toneMapped:!1}))}}function p2e(t,e,a,r){const i=(function(o,l,c){o*=.5,l*=.5,c*=.5;const d=new co,u=[];return u.push(-o,-l,-c,-o,l,-c,-o,l,-c,o,l,-c,o,l,-c,o,-l,-c,o,-l,-c,-o,-l,-c,-o,-l,c,-o,l,c,-o,l,c,o,l,c,o,l,c,o,-l,c,o,-l,c,-o,-l,c,-o,-l,-c,-o,-l,c,-o,l,-c,-o,l,c,o,l,-c,o,l,c,o,-l,-c,o,-l,c),d.setAttribute("position",new bi(u,3)),d})(t,e,a),s=new $T(i,new qEe({color:new La(r),dashSize:3,gapSize:1}));return s.computeLineDistances(),s}var sw=function(){var t=0,e=document.createElement("div");function a(u){return e.appendChild(u.dom),u}function r(u){for(var m=0;m=s+1e3&&(l.update(1e3*o/(u-s),100),s=u,o=0,d)){var m=performance.memory;d.update(m.usedJSHeapSize/1048576,m.jsHeapSizeLimit/1048576)}return u},update:function(){i=this.end()},domElement:e,setMode:r}};sw.Panel=function(t,e,a){var r=1/0,i=0,s=Math.round,o=s(window.devicePixelRatio||1),l=80*o,c=48*o,d=3*o,u=2*o,m=3*o,p=15*o,f=74*o,g=30*o,v=document.createElement("canvas");v.width=l,v.height=c,v.style.cssText="width:80px;height:48px";var b=v.getContext("2d");return b.font="bold "+9*o+"px Helvetica,Arial,sans-serif",b.textBaseline="top",b.fillStyle=a,b.fillRect(0,0,l,c),b.fillStyle=e,b.fillText(t,d,u),b.fillRect(m,p,f,g),b.fillStyle=a,b.globalAlpha=.9,b.fillRect(m,p,f,g),{dom:v,update:function(x,S){r=Math.min(r,x),i=Math.max(i,x),b.fillStyle=a,b.globalAlpha=1,b.fillRect(0,0,l,p),b.fillStyle=e,b.fillText(s(x)+" "+t+" ("+s(r)+"-"+s(i)+")",d,u),b.drawImage(v,m+o,p,f-o,g,m,p,f-o,g),b.fillRect(m+f-o,p,o,g),b.fillStyle=a,b.globalAlpha=.9,b.fillRect(m+f-o,p,o,s((1-x/S)*g))}}};var h2e=sw;class LV{constructor(e,a){this.openFolders=[],this.watchedObject=e,this.options=a,this.gui=new NC,this.gui.title("Dev info"),this.setup()}setup(){this.loadOpenFolders(),this.options&&!this.options.renderer||this.setupRedererFolder(),this.options&&!this.options.camera||this.setupCameraFolder(),this.options&&!this.options.parser||this.setupParserFolder(),this.options&&!this.options.buildVolume||this.setupBuildVolumeFolder(),this.options&&!this.options.devHelpers||this.setupDevHelpers()}reset(){this.gui.destroy(),this.gui=new NC,this.gui.title("Dev info"),this.setup()}loadOpenFolders(){this.openFolders=JSON.parse(localStorage.getItem("dev-gui-open")||"{}").open||[]}saveOpenFolders(){this.openFolders=this.gui.foldersRecursive().filter((e=>!e._closed)).map((e=>e._title)),console.log(this.openFolders),localStorage.setItem("dev-gui-open",JSON.stringify({open:this.openFolders}))}setupRedererFolder(){const e=this.gui.addFolder("Render Info");this.openFolders.includes("Render Info")||e.close(),e.onOpenClose((()=>{this.saveOpenFolders()})),e.add(this.watchedObject.renderer.info.render,"triangles").listen(),e.add(this.watchedObject.renderer.info.render,"calls").listen(),e.add(this.watchedObject.renderer.info.render,"lines").listen(),e.add(this.watchedObject.renderer.info.render,"points").listen(),e.add(this.watchedObject.renderer.info.memory,"geometries").listen(),e.add(this.watchedObject.renderer.info.memory,"textures").listen(),e.add(this.watchedObject,"_lastRenderTime").listen()}setupCameraFolder(){const e=this.gui.addFolder("Camera");this.openFolders.includes("Camera")||e.close(),e.onOpenClose((()=>{this.saveOpenFolders()}));const a=e.addFolder("Camera position");a.add(this.watchedObject.camera.position,"x").listen(),a.add(this.watchedObject.camera.position,"y").listen(),a.add(this.watchedObject.camera.position,"z").listen();const r=e.addFolder("Camera rotation");r.add(this.watchedObject.camera.rotation,"x").listen(),r.add(this.watchedObject.camera.rotation,"y").listen(),r.add(this.watchedObject.camera.rotation,"z").listen()}setupParserFolder(){const e=this.gui.addFolder("Parser");this.openFolders.includes("Parser")||e.close(),e.onOpenClose((()=>{this.saveOpenFolders()})),e.add(this.watchedObject.parser,"curZ").listen(),e.add(this.watchedObject.parser,"maxZ").listen(),e.add(this.watchedObject.parser,"tolerance").listen(),e.add(this.watchedObject.parser.layers,"length").name("layers.count").listen(),e.add(this.watchedObject.parser.lines,"length").name("lines.count").listen()}setupBuildVolumeFolder(){const e=this.gui.addFolder("Build Volume");this.openFolders.includes("Build Volume")||e.close(),e.onOpenClose((()=>{this.saveOpenFolders()})),e.add(this.watchedObject.buildVolume,"x").min(0).max(600).listen().onChange((()=>{this.watchedObject.render()})),e.add(this.watchedObject.buildVolume,"y").min(0).max(600).listen().onChange((()=>{this.watchedObject.render()})),e.add(this.watchedObject.buildVolume,"z").min(0).max(600).listen().onChange((()=>{this.watchedObject.render()}))}setupDevHelpers(){const e=this.gui.addFolder("Dev Helpers");this.openFolders.includes("Dev Helpers")||e.close(),e.onOpenClose((()=>{this.saveOpenFolders()})),e.add(this.watchedObject,"_wireframe").listen().onChange((()=>{this.watchedObject.render()})),e.add(this.watchedObject,"render").listen(),e.add(this.watchedObject,"clear").listen()}}class f2e extends co{constructor(e=[new yt],a=.6,r=.2,i=8){super(),this.type="ExtrusionGeometry",this.parameters={points:e,lineWidth:a,lineHeight:r,radialSegments:i,closed:!1};const s=new yt,o=new yt,l=new Ga,c=[],d=[],u=[],m=[];function p(f){const[g,v,b]=(function(x){const S=e[x],_=new yt,C=new yt,P=new yt,T=new yt;_.copy(S).sub(e[x-1]||S).normalize().add((e[x+1]||S).clone().sub(S).normalize()).normalize();let A=Number.MAX_VALUE;const F=Math.abs(_.x),k=Math.abs(_.y),E=Math.abs(_.z);return F<=A&&(A=F,C.set(1,0,0)),k<=A&&(A=k,C.set(0,1,0)),E<=A&&C.set(0,0,1),T.crossVectors(_,C).normalize(),C.crossVectors(_,T),P.crossVectors(_,C),[S,C,P]})(f);for(let x=0;x<=i;x++){const S=x/i*Math.PI*2,_=Math.sin(S),C=-Math.cos(S);o.x=C*v.x+_*b.x,o.y=C*v.y+_*b.y,o.z=C*v.z+_*b.z,o.normalize(),d.push(o.x,o.y,o.z),s.x=g.x+a*o.x*.5,s.y=g.y+a*o.y*.5,s.z=g.z+r*o.z*.5,c.push(s.x,s.y,s.z)}}(function(){for(let f=0;fthis.animate())),this.controls.update(),this.renderer.render(this.scene,this.camera),(e=this.stats)===null||e===void 0||e.update()}processGCode(e){this.parser.parseGCode(e),this.render()}initScene(){for(;this.scene.children.length>0;)this.scene.remove(this.scene.children[0]);for(;this.disposables.length>0;){const e=this.disposables.pop();e&&e.dispose()}if(this.debug&&this.buildVolume){const e=new YEe(Math.max(this.buildVolume.x/2,this.buildVolume.y/2)+20);this.scene.add(e)}if(this.buildVolume&&this.drawBuildVolume(),this.renderTubes){console.warn("Volumetric rendering is experimental. It may not work as expected or change in the future.");const e=new WEe(13421772,.3*Math.PI),a=new GEe(16777215,Math.PI,void 0,.001);a.position.set(0,500,500),this.scene.add(e),this.scene.add(a)}}createGroup(e){const a=new U0;return a.name=e,a.quaternion.setFromEuler(new HS(-Math.PI/2,0,0)),this.buildVolume?a.position.set(-this.buildVolume.x/2,0,this.buildVolume.y/2):a.position.set(-100,0,100),a}render(){const e=performance.now();this.group=this.createGroup("allLayers"),this.state=ow.initial,this.initScene();for(let a=0;a0?e:1)}))}renderFrameLoop(e){return new Promise((a=>{const r=()=>{this.renderLayerIndex>this.layers.length-1?a():(this.renderFrame(e),requestAnimationFrame(r))};r()}))}renderFrame(e){this.group=this.createGroup("layer"+this.renderLayerIndex);for(let a=0;athis.maxLayerIndex)return;const u=this.layers[e],m={extrusion:[],travel:[],z:this.state.z,height:u.height};for(const p of u.commands)if(p.gcode!="g20"){if(p.gcode.startsWith("t")){this.doRenderExtrusion(m,e),m.extrusion=[];const f=p;this.state.t=f.toolIndex}else if(["g0","g00","g1","g01","g2","g02","g3","g03"].indexOf(p.gcode)>-1){const f=p,g={x:(a=f.params.x)!==null&&a!==void 0?a:this.state.x,y:(r=f.params.y)!==null&&r!==void 0?r:this.state.y,z:(i=f.params.z)!==null&&i!==void 0?i:this.state.z,r:(s=f.params.r)!==null&&s!==void 0?s:this.state.r,e:(o=f.params.e)!==null&&o!==void 0?o:this.state.e,i:(l=f.params.i)!==null&&l!==void 0?l:this.state.i,j:(c=f.params.j)!==null&&c!==void 0?c:this.state.j,t:this.state.t};if(e>=this.minLayerIndex){const v=((d=f.params.e)!==null&&d!==void 0?d:0)>0||this.nonTravelmoves.indexOf(p.gcode)>-1;(g.x!=this.state.x||g.y!=this.state.y||g.z!=this.state.z)&&(v&&this.renderExtrusion||!v&&this.renderTravel)&&(p.gcode=="g2"||p.gcode=="g3"||p.gcode=="g02"||p.gcode=="g03"?this.addArcSegment(m,this.state,g,v,p.gcode=="g2"||p.gcode=="g02"):this.addLineSegment(m,this.state,g,v))}this.state.x=g.x,this.state.y=g.y,this.state.z=g.z,this.beyondFirstMove||(this.beyondFirstMove=!0)}}else this.setInches();this.doRenderExtrusion(m,e)}doRenderExtrusion(e,a){var r,i;if(this.renderExtrusion){let s=this.currentToolColor;if(!this.singleLayerMode&&!this.renderTubes&&!this.disableGradient){const o=.1+.7*a/this.layers.length;s.getHSL(R2),s=new La().setHSL(R2.h,R2.s,o)}if(a==this.layers.length-1){const o=(r=this._topLayerColor)!==null&&r!==void 0?r:s,l=(i=this._lastSegmentColor)!==null&&i!==void 0?i:o,c=e.extrusion.splice(-3),d=e.extrusion.splice(-3);this.renderTubes?(this.addTubeLine(e.extrusion,o.getHex(),e.height),this.addTubeLine([...d,...c],l.getHex(),e.height)):(this.addLine(e.extrusion,o.getHex()),this.addLine([...d,...c],l.getHex()))}else this.renderTubes?this.addTubeLine(e.extrusion,s.getHex(),e.height):this.addLine(e.extrusion,s.getHex())}this.renderTravel&&this.addLine(e.travel,this._travelColor.getHex())}setInches(){this.beyondFirstMove?console.warn("Switching units after movement is already made is discouraged and is not supported."):this.inches=!0}drawBuildVolume(){if(!this.buildVolume)return;this.scene.add(new m2e(this.buildVolume.x,10,this.buildVolume.y,10));const e=p2e(this.buildVolume.x,this.buildVolume.z,this.buildVolume.y,8947848);e.position.setY(this.buildVolume.z/2),this.scene.add(e)}clear(){this.resetState(),this.parser=new CC(this.minLayerThreshold)}resetState(){var e;this.startLayer=1,this.endLayer=1/0,this.singleLayerMode=!1,this.beyondFirstMove=!1,this.state=ow.initial,(e=this.devGui)===null||e===void 0||e.reset(),this._geometries={}}resize(){const[e,a]=[this.canvas.offsetWidth,this.canvas.offsetHeight];this.camera.aspect=e/a,this.camera.updateProjectionMatrix(),this.renderer.setPixelRatio(window.devicePixelRatio),this.renderer.setSize(e,a,!1)}addLineSegment(e,a,r,i){(i?e.extrusion:e.travel).push(a.x,a.y,a.z,r.x,r.y,r.z)}addArcSegment(e,a,r,i,s){const o=i?e.extrusion:e.travel,l=a.x,c=a.y,d=a.z,u=r.x,m=r.y,p=r.z;let f=r.r,g=r.i,v=r.j;if(f){const Z=u-l,ae=m-c,Q=Math.sqrt(Math.pow(Z/2,2)+Math.pow(ae/2,2));f=Math.max(f,Q);const oe=Math.pow(Z,2)+Math.pow(ae,2),ue=Math.pow(f,2)-oe/4;let pe=Math.sqrt(ue/oe);(s&&f<0||!s&&f>0)&&(pe=-pe),g=Z/2+ae*pe,v=ae/2-Z*pe}const b=l==u&&c==m,x=l+g,S=c+v,_=Math.sqrt(g*g+v*v),C=Math.atan2(-v,-g),P=Math.atan2(m-S,u-x);let T;b?T=2*Math.PI:(T=s?C-P:P-C,T<0&&(T+=2*Math.PI));let A=_*T/1.8;this.inches&&(A*=25),A<1&&(A=1);let F=T/A;F*=s?-1:1;const k=[];k.push({x:l,y:c,z:d});const E=(d-p)/A;let U=l,I=c,z=d,L=C;for(let Z=0;Z0)return void this.addThickLine(e,a);const i=new co;i.setAttribute("position",new bi(e,3)),this.disposables.push(i);const s=new $S({color:a});this.disposables.push(s);const o=new $T(i,s);(r=this.group)===null||r===void 0||r.add(o)}addTubeLine(e,a,r=.2){let i=[];const s=[];for(let o=0;o{var l;const c=new f2e(o,this.extrusionWidth,this.lineHeight||r,4);(l=this._geometries)[a]||(l[a]=[]),this._geometries[a].push(c)}))}addThickLine(e,a){var r;if(!e.length||!this.lineWidth)return;const i=new Uee;this.disposables.push(i);const s=new zee({color:a,linewidth:this.lineWidth/(1e3*window.devicePixelRatio)});this.disposables.push(s),i.setPositions(e);const o=new u2e(i,s);(r=this.group)===null||r===void 0||r.add(o)}dispose(){this.disposables.forEach((e=>e.dispose())),this.disposables=[],this.controls.dispose(),this.renderer.dispose(),this.cancelAnimation()}cancelAnimation(){this.animationFrameId!==void 0&&cancelAnimationFrame(this.animationFrameId),this.animationFrameId=void 0}_enableDropHandler(){console.warn("Drag and drop is deprecated as a library feature. See the demo how to implement your own."),this.canvas.addEventListener("dragover",(e=>{e.stopPropagation(),e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy"),this.canvas.classList.add("dragging")})),this.canvas.addEventListener("dragleave",(e=>{e.stopPropagation(),e.preventDefault(),this.canvas.classList.remove("dragging")})),this.canvas.addEventListener("drop",(e=>E2(this,void 0,void 0,(function*(){var a,r;e.stopPropagation(),e.preventDefault(),this.canvas.classList.remove("dragging");const i=((r=(a=e.dataTransfer)===null||a===void 0?void 0:a.files)!==null&&r!==void 0?r:[])[0];this.clear(),yield this._readFromStream(i.stream()),this.render()}))))}batchGeometries(){if(this._geometries)for(const e in this._geometries){const a=this.createBatchMesh(parseInt(e));for(;this._geometries[e].length>0;){const r=this._geometries[e].pop();a.addGeometry(r)}}}createBatchMesh(e){var a;const r=this._geometries[e],i=new HEe({color:e,wireframe:this._wireframe});this.disposables.push(i);const s=r.reduce(((l,c)=>3*c.attributes.position.count+l),0),o=new zEe(r.length,s,void 0,i);return this.disposables.push(o),(a=this.group)===null||a===void 0||a.add(o),o}_readFromStream(e){var a,r;return E2(this,void 0,void 0,(function*(){const i=e.getReader();let s,o="",l=0;do{console.debug("reading from stream"),s=yield i.read(),l+=(r=(a=s.value)===null||a===void 0?void 0:a.length)!==null&&r!==void 0?r:0;const d=(c=s.value,new TextDecoder("utf-8").decode(c)),u=d.lastIndexOf(` `),m=d.slice(0,u);this.parser.parseGCode(o+m),o=d.slice(u)}while(!s.done);var c;console.debug("read from stream",l)}))}initGui(){typeof this.devMode=="boolean"&&this.devMode===!0?this.devGui=new LV(this):typeof this.devMode=="object"&&(this.devGui=new LV(this,this.devMode))}initStats(){var e;this.stats&&(typeof this.devMode=="object"&&(this.statsContainer=this.devMode.statsContainer),((e=this.statsContainer)!==null&&e!==void 0?e:document.body).appendChild(this.stats.dom),this.stats.dom.classList.add("stats"),this.initGui())}}cy.defaultExtrusionColor=new La("hotpink");function Bee({gcodeUrl:t,buildVolume:e={x:256,y:256,z:256},filamentColors:a,className:r=""}){const i=y.useRef(null),s=y.useRef(null),o=y.useRef(null),l=y.useRef(!1),[c,d]=y.useState(!0),[u,m]=y.useState(null),[p,f]=y.useState(!1),[g,v]=y.useState(0),[b,x]=y.useState(0),S=y.useMemo(()=>JSON.stringify(a),[a]);y.useEffect(()=>{if(!i.current||l.current)return;l.current=!0;const P=i.current,T=P.parentElement?.getBoundingClientRect();T&&(P.width=T.width,P.height=T.height);const A=a&&a.length>1,F=a?.[0]||"#00ae42",k=new cy({canvas:P,buildVolume:e,backgroundColor:1710618,extrusionColor:A?a:F,disableGradient:!0,lineHeight:.2,lineWidth:2,renderTravel:!1,renderExtrusion:!0});s.current=k;const E={},U=Zm();U&&(E.Authorization=`Bearer ${U}`),fetch(t,{headers:E}).then(async z=>{if(!z.ok)throw z.status===404&&(await z.json().catch(()=>({}))).detail?.includes("sliced")?(f(!0),new Error("not_sliced")):new Error("Failed to load G-code");return z.text()}).then(z=>{const L=new Set,Z=/^(\s*)T(\d+)(\s*;.*)?$/gim;let ae;for(;(ae=Z.exec(z))!==null;){const le=parseInt(ae[2],10);le<=15&&L.add(le)}const Q=new Map,oe=Array.from(L).sort((le,me)=>le-me);oe.forEach((le,me)=>{Q.set(le,me%8)});const ue=[];oe.forEach((le,me)=>{const de=a?.[le]||"#00ae42";ue[me%8]=de});const pe=z.split(` `).map(le=>{const me=le.match(/^(\s*)T(\d+)(\s*;.*)?$/i);if(me){const de=parseInt(me[2],10);if(de>15)return`; FILTERED: ${le.trim()}`;const we=Q.get(de)??0;return`${me[1]}T${we}${me[3]||""}`}return le}).join(` -`);ue.length>0&&(k.extrusionColor=ue),k.processGCode(pe);const X=k.layers?.length||0;x(X),v(X),k.render(),d(!1)}).catch(z=>{z.message!=="not_sliced"&&m(z.message),d(!1)});const I=()=>{if(P.parentElement&&s.current){const z=P.parentElement.getBoundingClientRect();P.width=z.width,P.height=z.height,s.current.resize()}};return window.addEventListener("resize",I),()=>{window.removeEventListener("resize",I),o.current&&cancelAnimationFrame(o.current),s.current&&(s.current.dispose(),s.current=null),l.current=!1}},[t,S]);const _=y.useCallback(P=>{if(!s.current)return;const T=Math.max(1,Math.min(P,b));v(T),o.current&&cancelAnimationFrame(o.current),o.current=requestAnimationFrame(()=>{s.current&&(s.current.endLayer=T,s.current.render())})},[b]),C=P=>{_(parseInt(P.target.value,10))};return n.jsxs("div",{className:`relative flex flex-col h-full ${r}`,children:[n.jsxs("div",{className:"flex-1 relative bg-bambu-dark rounded-lg overflow-hidden",children:[n.jsx("canvas",{ref:i,className:"w-full h-full"}),c&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsxs("div",{className:"text-center",children:[n.jsx(pt,{className:"w-8 h-8 animate-spin text-bambu-green mx-auto mb-2"}),n.jsx("p",{className:"text-bambu-gray text-sm",children:"Loading G-code..."})]})}),p&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsxs("div",{className:"text-center max-w-sm px-4",children:[n.jsx(Nbe,{className:"w-12 h-12 text-bambu-gray mx-auto mb-3"}),n.jsx("p",{className:"text-white font-medium mb-2",children:"G-code not available"}),n.jsx("p",{className:"text-bambu-gray text-sm",children:"This file hasn't been sliced yet. G-code preview is only available after slicing in Bambu Studio or Orca Slicer."})]})}),u&&!p&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsx("div",{className:"text-center text-red-400",children:n.jsx("p",{className:"text-sm",children:u})})})]}),!c&&!u&&!p&&b>0&&n.jsx("div",{className:"mt-4 px-2",children:n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(cr,{className:"w-4 h-4 text-bambu-gray flex-shrink-0"}),n.jsx("button",{onClick:()=>_(g-1),disabled:g<=1,className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-30 disabled:cursor-not-allowed",children:n.jsx(tl,{className:"w-4 h-4"})}),n.jsx("input",{type:"range",min:1,max:b,value:g,onChange:C,className:"flex-1 h-2 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer accent-bambu-green"}),n.jsx("button",{onClick:()=>_(g+1),disabled:g>=b,className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-30 disabled:cursor-not-allowed",children:n.jsx(qr,{className:"w-4 h-4"})}),n.jsxs("span",{className:"text-sm text-bambu-gray min-w-[80px] text-right",children:[g," / ",b]})]})})]})}function g2e({printerId:t,filePath:e,filename:a,onClose:r}){const[i,s]=y.useState(null),[o,l]=y.useState([]),[c,d]=y.useState(!1),[u,m]=y.useState(null),p=a.toLowerCase().split(".").pop()||"",f=p==="3mf"||p==="stl",g=p==="gcode"||p==="3mf";y.useEffect(()=>{s(f?"3d":g?"gcode":null)},[f,g]),y.useEffect(()=>{l([]),m(null),f&&(d(!0),re.getPrinterFilePlates(t,e).then(x=>l(x.plates||[])).catch(()=>l([])).finally(()=>d(!1)))},[e,f,t]);const v=o.length>1,b=u==null?null:o.find(x=>x.index===u)??null;return n.jsx("div",{className:"fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-6",onClick:r,children:n.jsxs("div",{className:"bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-4xl h-[80vh] flex flex-col",onClick:x=>x.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-bambu-dark-tertiary",children:[n.jsx("h2",{className:"text-lg font-semibold text-white truncate flex-1 mr-4",children:a}),n.jsx(je,{variant:"ghost",size:"sm",onClick:r,children:n.jsx($t,{className:"w-5 h-5"})})]}),n.jsxs("div",{className:"flex border-b border-bambu-dark-tertiary",children:[n.jsxs("button",{onClick:()=>f&&s("3d"),disabled:!f,className:`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${i==="3d"?"text-bambu-green border-b-2 border-bambu-green":f?"text-bambu-gray hover:text-white":"text-bambu-gray/30 cursor-not-allowed"}`,children:[n.jsx(ji,{className:"w-4 h-4"}),"3D Model",!f&&n.jsx("span",{className:"text-xs",children:"(not available)"})]}),n.jsxs("button",{onClick:()=>g&&s("gcode"),disabled:!g,className:`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${i==="gcode"?"text-bambu-green border-b-2 border-bambu-green":g?"text-bambu-gray hover:text-white":"text-bambu-gray/30 cursor-not-allowed"}`,children:[n.jsx(Bs,{className:"w-4 h-4"}),"G-code Preview",!g&&n.jsx("span",{className:"text-xs",children:"(not sliced)"})]})]}),n.jsx("div",{className:"flex-1 overflow-hidden p-4",children:i==="3d"&&f?n.jsxs("div",{className:"w-full h-full flex flex-col gap-3",children:[v&&n.jsxs("div",{className:"rounded-lg border border-bambu-dark-tertiary bg-bambu-dark p-3",children:[n.jsxs("div",{className:"flex items-center gap-2 text-sm text-bambu-gray mb-2",children:[n.jsx(ji,{className:"w-4 h-4"}),"Plates",c&&n.jsx(pt,{className:"w-3 h-3 animate-spin"})]}),n.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-3 gap-2",children:[n.jsxs("button",{type:"button",onClick:()=>m(null),className:`flex items-center gap-2 rounded-lg border p-2 text-left transition-colors ${u==null?"border-bambu-green bg-bambu-green/10":"border-bambu-dark-tertiary bg-bambu-dark-secondary hover:border-bambu-gray"}`,children:[n.jsx("div",{className:"w-10 h-10 rounded bg-bambu-dark-tertiary flex items-center justify-center",children:n.jsx(ji,{className:"w-5 h-5 text-bambu-gray"})}),n.jsxs("div",{className:"min-w-0 flex-1",children:[n.jsx("p",{className:"text-sm text-white font-medium truncate",children:"All Plates"}),n.jsxs("p",{className:"text-xs text-bambu-gray truncate",children:[o.length," plate",o.length!==1?"s":""]})]}),u==null&&n.jsx(ws,{className:"w-4 h-4 text-bambu-green flex-shrink-0"})]}),o.map(x=>n.jsxs("button",{type:"button",onClick:()=>m(x.index),className:`flex items-center gap-2 rounded-lg border p-2 text-left transition-colors ${u===x.index?"border-bambu-green bg-bambu-green/10":"border-bambu-dark-tertiary bg-bambu-dark-secondary hover:border-bambu-gray"}`,children:[x.has_thumbnail?n.jsx("img",{src:re.getPrinterFilePlateThumbnail(t,x.index,e),alt:`Plate ${x.index}`,className:"w-10 h-10 rounded object-cover bg-bambu-dark-tertiary"}):n.jsx("div",{className:"w-10 h-10 rounded bg-bambu-dark-tertiary flex items-center justify-center",children:n.jsx(ji,{className:"w-5 h-5 text-bambu-gray"})}),n.jsxs("div",{className:"min-w-0 flex-1",children:[n.jsx("p",{className:"text-sm text-white font-medium truncate",children:x.name||`Plate ${x.index}`}),n.jsx("p",{className:"text-xs text-bambu-gray truncate",children:x.objects.length>0?x.objects.slice(0,2).join(", ")+(x.objects.length>2?"…":""):`${x.filaments.length} filament${x.filaments.length!==1?"s":""}`})]}),u===x.index&&n.jsx(ws,{className:"w-4 h-4 text-bambu-green flex-shrink-0"})]},x.index))]}),b&&n.jsxs("div",{className:"mt-3 text-xs text-bambu-gray flex flex-wrap gap-x-4 gap-y-1",children:[n.jsxs("span",{children:["Plate ",b.index]}),b.print_time_seconds!=null&&n.jsxs("span",{children:["ETA ",Math.round(b.print_time_seconds/60)," min"]}),b.filament_used_grams!=null&&n.jsxs("span",{children:[b.filament_used_grams.toFixed(1)," g"]}),b.filaments.length>0&&n.jsxs("span",{children:[b.filaments.length," filament",b.filaments.length!==1?"s":""]})]})]}),n.jsx("div",{className:"flex-1",children:n.jsx(iee,{url:re.getPrinterFileDownloadUrl(t,e),fileType:p,selectedPlateId:u,className:"w-full h-full"})})]}):i==="gcode"&&g?n.jsx(Bee,{gcodeUrl:re.getPrinterFileGcodeUrl(t,e),className:"w-full h-full"}):n.jsx("div",{className:"w-full h-full flex items-center justify-center text-bambu-gray",children:"No preview available for this file"})})]})})}function RV(t){if(t===0)return"0 GB";const e=t/(1024*1024*1024);return e>=1?`${e.toFixed(1)} GB`:`${(t/(1024*1024)).toFixed(0)} MB`}function b2e(t,e){if(e)return VZ;switch(t.toLowerCase().split(".").pop()||""){case"3mf":return Th;case"gcode":return Bs;case"mp4":case"avi":return Ah;case"png":case"jpg":case"jpeg":return cu;default:return jT}}const x2e=[{value:"name-asc",label:"Name (A-Z)"},{value:"name-desc",label:"Name (Z-A)"},{value:"size-asc",label:"Size (smallest)"},{value:"size-desc",label:"Size (largest)"},{value:"date-asc",label:"Date (oldest)"},{value:"date-desc",label:"Date (newest)"}];function y2e({printerId:t,printerName:e,onClose:a}){const{t:r}=St(),{showToast:i}=ra(),s=ta(),[o,l]=y.useState("/"),[c,d]=y.useState(new Set),[u,m]=y.useState(""),[p,f]=y.useState([]),[g,v]=y.useState("name-asc"),[b,x]=y.useState(null),[S,_]=y.useState(null);y.useEffect(()=>{const Q=oe=>{oe.key==="Escape"&&a()};return window.addEventListener("keydown",Q),()=>window.removeEventListener("keydown",Q)},[a]);const{data:C,isLoading:P,refetch:T}=Ye({queryKey:["printerFiles",t,o],queryFn:()=>re.getPrinterFiles(t,o),refetchInterval:3e4}),{data:A}=Ye({queryKey:["printerStorage",t],queryFn:()=>re.getPrinterStorage(t),staleTime:3e4}),F=lt({mutationFn:async Q=>{for(const oe of Q)await re.deletePrinterFile(t,oe)},onSuccess:()=>{i(r("printerFiles.toast.filesDeleted",{count:p.length})),s.invalidateQueries({queryKey:["printerFiles",t]}),d(new Set),f([])},onError:Q=>{i(r("printerFiles.toast.deleteFailed",{error:Q.message}),"error")}}),k=Q=>{l(Q),d(new Set)},E=()=>{if(o==="/")return;const Q=o.split("/").filter(Boolean);Q.pop(),l(Q.length?"/"+Q.join("/"):"/"),d(new Set)},U=(Q,oe)=>{oe.stopPropagation(),d(ue=>{const pe=new Set(ue);return pe.has(Q)?pe.delete(Q):pe.add(Q),pe})},I=()=>{if(!C?.files)return;const Q=C.files.filter(oe=>!oe.is_directory&&(!u||oe.name.toLowerCase().includes(u.toLowerCase()))).map(oe=>oe.path);d(new Set(Q))},z=()=>{d(new Set)},L=async()=>{if(c.size===0)return;const Q=Array.from(c);if(Q.length===1){re.downloadPrinterFile(t,Q[0]).catch(oe=>{console.error("Printer file download failed:",oe)}),d(new Set);return}x({current:0,total:Q.length});try{const oe=await re.downloadPrinterFilesAsZip(t,Q),ue=URL.createObjectURL(oe),pe=document.createElement("a");pe.href=ue,pe.download=`${e.replace(/[^a-zA-Z0-9]/g,"_")}-files.zip`,document.body.appendChild(pe),pe.click(),document.body.removeChild(pe),URL.revokeObjectURL(ue),i(`Downloaded ${Q.length} files as ZIP`),d(new Set)}catch(oe){i(`Download failed: ${oe instanceof Error?oe.message:"Unknown error"}`,"error")}finally{x(null)}},Z=()=>{c.size!==0&&f(Array.from(c))},ae=[{path:"/",label:"Root"},{path:"/cache",label:"Cache"},{path:"/model",label:"Models"},{path:"/timelapse",label:"Timelapse"}];return n.jsxs("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4",onClick:a,children:[n.jsxs("div",{className:"w-full max-w-3xl max-h-[85vh] flex flex-col bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary overflow-hidden",onClick:Q=>Q.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(cb,{className:"w-5 h-5 text-bambu-green"}),n.jsxs("div",{children:[n.jsx("h2",{className:"text-lg font-semibold text-white",children:r("printerFiles.title")}),n.jsx("p",{className:"text-sm text-bambu-gray",children:e})]})]}),n.jsxs("div",{className:"flex items-center gap-4",children:[A&&(A.used_bytes!=null||A.free_bytes!=null)&&n.jsxs("div",{className:"text-sm text-bambu-gray flex items-center gap-2",children:[A.used_bytes!=null&&n.jsxs("span",{children:[r("printerFiles.storageUsed")," ",RV(A.used_bytes)]}),A.used_bytes!=null&&A.free_bytes!=null&&n.jsx("span",{className:"text-bambu-dark-tertiary",children:"|"}),A.free_bytes!=null&&n.jsxs("span",{children:[r("printerFiles.storageFree")," ",RV(A.free_bytes)]})]}),n.jsx("button",{onClick:a,className:"text-bambu-gray hover:text-white transition-colors",title:"Close file manager","aria-label":"Close file manager",children:n.jsx($t,{className:"w-5 h-5"})})]})]}),n.jsxs("div",{className:"flex items-center gap-2 p-3 border-b border-bambu-dark-tertiary bg-bambu-dark/50 flex-shrink-0",children:[ae.map(Q=>n.jsx("button",{onClick:()=>{k(Q.path),m("")},className:`px-3 py-1 text-sm rounded-full transition-colors ${o===Q.path?"bg-bambu-green text-white":"bg-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,children:Q.label},Q.path)),n.jsx("div",{className:"flex-1"}),n.jsxs("div",{className:"relative",children:[n.jsx(gn,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),n.jsx("input",{type:"text",placeholder:r("printerFiles.filterPlaceholder"),value:u,onChange:Q=>m(Q.target.value),className:"w-40 pl-8 pr-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none"})]}),n.jsxs("div",{className:"relative flex items-center gap-1",children:[n.jsx(TT,{className:"w-4 h-4 text-bambu-gray"}),n.jsx("select",{value:g,onChange:Q=>v(Q.target.value),className:"appearance-none bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm py-1.5 pl-2 pr-6 focus:border-bambu-green focus:outline-none cursor-pointer",title:"Sort files","aria-label":"Sort files",children:x2e.map(Q=>n.jsx("option",{value:Q.value,children:Q.label},Q.value))})]}),n.jsx(je,{variant:"secondary",size:"sm",onClick:()=>T(),disabled:P,children:n.jsx(en,{className:`w-4 h-4 ${P?"animate-spin":""}`})})]}),n.jsxs("div",{className:"flex items-center gap-2 px-4 py-2 bg-bambu-dark text-sm flex-shrink-0",children:[n.jsx("button",{onClick:E,disabled:o==="/",className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed",title:"Go to parent folder","aria-label":"Go to parent folder",children:n.jsx(tl,{className:"w-4 h-4"})}),n.jsx("span",{className:"text-bambu-gray font-mono",children:o})]}),n.jsx("div",{className:"flex-1 overflow-y-auto p-2 min-h-0",children:P?n.jsx("div",{className:"flex items-center justify-center py-12",children:n.jsx(pt,{className:"w-8 h-8 text-bambu-green animate-spin"})}):C?.files?.length?n.jsx("div",{className:"space-y-1",children:[...C.files].filter(Q=>!u||Q.name.toLowerCase().includes(u.toLowerCase())).sort((Q,oe)=>{if(Q.is_directory&&!oe.is_directory)return-1;if(!Q.is_directory&&oe.is_directory)return 1;switch(g){case"name-asc":return Q.name.localeCompare(oe.name);case"name-desc":return oe.name.localeCompare(Q.name);case"size-asc":return Q.size-oe.size;case"size-desc":return oe.size-Q.size;case"date-asc":{const ue=Q.mtime?Qa(Q.mtime)?.getTime()??0:0,pe=oe.mtime?Qa(oe.mtime)?.getTime()??0:0;return ue-pe}case"date-desc":{const ue=Q.mtime?Qa(Q.mtime)?.getTime()??0:0;return(oe.mtime?Qa(oe.mtime)?.getTime()??0:0)-ue}default:return Q.name.localeCompare(oe.name)}}).map(Q=>{const oe=b2e(Q.name,Q.is_directory),ue=c.has(Q.path);return n.jsxs("div",{className:`flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors ${ue?"bg-bambu-green/20 border border-bambu-green/50":"hover:bg-bambu-dark-tertiary"}`,onClick:()=>{Q.is_directory&&k(Q.path)},children:[Q.is_directory?null:n.jsx("button",{onClick:pe=>U(Q.path,pe),className:"flex-shrink-0 text-bambu-gray hover:text-white",children:ue?n.jsx(ws,{className:"w-5 h-5 text-bambu-green"}):n.jsx(oo,{className:"w-5 h-5"})}),n.jsx(oe,{className:`w-5 h-5 flex-shrink-0 ${Q.is_directory?"text-bambu-green":"text-bambu-gray"}`}),n.jsx("span",{className:"flex-1 text-white truncate",children:Q.name}),!Q.is_directory&&n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("span",{className:"text-sm text-bambu-gray",children:ps(Q.size)}),(Q.name.toLowerCase().endsWith(".3mf")||Q.name.toLowerCase().endsWith(".gcode")||Q.name.toLowerCase().endsWith(".stl"))&&n.jsx("button",{onClick:pe=>{pe.stopPropagation(),_({path:Q.path,name:Q.name})},className:"p-1 rounded hover:bg-bambu-dark text-bambu-gray hover:text-bambu-green",title:"3D View",children:n.jsx(ji,{className:"w-4 h-4"})})]}),Q.is_directory&&n.jsx(tl,{className:"w-4 h-4 text-bambu-gray rotate-180"})]},Q.path)})}):n.jsx("div",{className:"text-center py-12 text-bambu-gray",children:"No files in this directory"})}),n.jsxs("div",{className:"flex items-center justify-between p-4 border-t border-bambu-dark-tertiary bg-bambu-dark/50 flex-shrink-0",children:[n.jsxs("div",{className:"flex items-center gap-4",children:[n.jsx("div",{className:"text-sm text-bambu-gray",children:c.size>0?`${c.size} selected`:u?`${C?.files?.filter(Q=>Q.name.toLowerCase().includes(u.toLowerCase())).length||0} of ${C?.files?.length||0} items`:`${C?.files?.length||0} items`}),C?.files?.some(Q=>!Q.is_directory)&&n.jsx("div",{className:"flex items-center gap-2",children:c.size>0?n.jsxs("button",{onClick:z,className:"flex items-center gap-1 text-xs text-bambu-gray hover:text-white transition-colors",children:[n.jsx(eve,{className:"w-4 h-4"}),"Deselect All"]}):n.jsxs("button",{onClick:I,className:"flex items-center gap-1 text-xs text-bambu-gray hover:text-white transition-colors",children:[n.jsx(ws,{className:"w-4 h-4"}),"Select All"]})})]}),n.jsxs("div",{className:"flex gap-2",children:[n.jsx(je,{variant:"secondary",disabled:c.size===0||b!==null,onClick:L,children:b?n.jsxs(n.Fragment,{children:[n.jsx(pt,{className:"w-4 h-4 animate-spin"}),b.current,"/",b.total]}):n.jsxs(n.Fragment,{children:[n.jsx($n,{className:"w-4 h-4"}),"Download",c.size>1?` (${c.size})`:""]})}),n.jsxs(je,{variant:"secondary",disabled:c.size===0||F.isPending,onClick:Z,className:"text-red-400 hover:text-red-300",children:[F.isPending?n.jsx(pt,{className:"w-4 h-4 animate-spin"}):n.jsx(ea,{className:"w-4 h-4"}),r("printerFiles.deleteButton"),c.size>1?` (${c.size})`:""]})]})]})]}),p.length>0&&n.jsx(va,{title:p.length>1?r("printerFiles.deleteFiles",{count:p.length}):r("fileManager.deleteFile"),message:p.length>1?r("printerFiles.deleteFilesConfirm",{count:p.length}):r("printerFiles.deleteFileConfirm",{name:p[0].split("/").pop()}),confirmText:r("common.delete"),variant:"danger",onConfirm:()=>{F.mutate(p)},onCancel:()=>f([])}),S&&n.jsx(g2e,{printerId:t,filePath:S.path,filename:S.name,onClose:()=>_(null)})]})}function m4({on:t,className:e="w-5 h-5"}){const a=t?"#facc15":"none",r=t?"#78350f":"currentColor",i=t?1:0;return n.jsxs("svg",{viewBox:"0 0 32 32",fill:"none",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:e,children:[n.jsxs("g",{stroke:r,opacity:i,children:[n.jsx("line",{x1:"16",y1:"2",x2:"16",y2:"6"}),n.jsx("line",{x1:"6.1",y1:"6.1",x2:"8.9",y2:"8.9"}),n.jsx("line",{x1:"25.9",y1:"6.1",x2:"23.1",y2:"8.9"}),n.jsx("line",{x1:"2",y1:"16",x2:"6",y2:"16"}),n.jsx("line",{x1:"30",y1:"16",x2:"26",y2:"16"})]}),n.jsx("path",{d:"M12 24v-2.3c0-.9-.4-1.7-1-2.3C9.2 17.6 8 15.4 8 13c0-4.4 3.6-8 8-8s8 3.6 8 8c0 2.4-1.2 4.6-3 6.4-.6.6-1 1.4-1 2.3V24",fill:a,stroke:r}),n.jsx("path",{d:"M12 24h8",stroke:r}),n.jsx("path",{d:"M12 27h8",stroke:r}),n.jsx("path",{d:"M13 30h6",stroke:r})]})}const VT=({className:t})=>n.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:t,children:[n.jsx("rect",{x:"2",y:"15",width:"5",height:"5",rx:"0.5"}),n.jsx("rect",{x:"9.5",y:"15",width:"5",height:"5",rx:"0.5",fill:"currentColor",opacity:"0.3"}),n.jsx("rect",{x:"17",y:"15",width:"5",height:"5",rx:"0.5"}),n.jsx("path",{d:"M4 12 C4 6, 14 6, 14 12"}),n.jsx("polyline",{points:"12,10 14,12 12,14"})]});function p4({printerId:t,isOpen:e,onClose:a}){const{t:r}=St(),{showToast:i}=ra(),{hasPermission:s}=un(),[o,l]=y.useState(null),[c,d]=y.useState(!1),{data:u}=Ye({queryKey:["printerStatus",t],queryFn:()=>re.getPrinterStatus(t),refetchInterval:3e4,enabled:e}),{data:m,refetch:p}=Ye({queryKey:["printableObjects",t],queryFn:()=>re.getPrintableObjects(t),enabled:e,refetchInterval:e?5e3:!1}),f=lt({mutationFn:g=>re.skipObjects(t,g),onSuccess:g=>{i(g.message||r("printers.skipObjects.objectsSkipped")),l(null),p()},onError:g=>i(g.message||r("printers.toast.failedToSkipObjects"),"error")});return e?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"fixed inset-0 z-50 flex items-center justify-center",onClick:a,onKeyDown:g=>{g.key==="Escape"&&(c?d(!1):a())},tabIndex:-1,ref:g=>g?.focus(),children:[n.jsx("div",{className:"absolute inset-0 bg-black/50 z-0"}),n.jsxs("div",{className:"relative z-10 bg-white dark:bg-bambu-dark border border-gray-200 dark:border-bambu-dark-tertiary rounded-xl shadow-2xl w-[560px] max-h-[85vh] flex flex-col overflow-hidden",onClick:g=>g.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-bambu-dark-tertiary bg-gray-50 dark:bg-bambu-dark",children:[n.jsxs("div",{className:"flex items-center gap-2",children:[n.jsx(VT,{className:"w-4 h-4 text-bambu-green"}),n.jsx("span",{className:"text-sm font-medium text-gray-900 dark:text-white",children:r("printers.skipObjects.title")})]}),n.jsx("button",{onClick:a,className:"p-1 text-gray-500 dark:text-bambu-gray hover:text-gray-900 dark:hover:text-white rounded transition-colors",children:n.jsx($t,{className:"w-4 h-4"})})]}),m?m.objects.length===0?n.jsxs("div",{className:"text-center py-8 px-4 text-bambu-gray",children:[n.jsx("p",{className:"text-sm",children:r("printers.noObjectsFound")}),n.jsx("p",{className:"text-xs mt-1 opacity-70",children:r("printers.objectsLoadedOnPrintStart")})]}):n.jsxs("div",{className:"flex flex-col overflow-hidden",children:[n.jsxs("div",{className:"flex items-center gap-3 px-4 py-2.5 bg-blue-50 dark:bg-blue-500/10 border-b border-gray-200 dark:border-bambu-dark-tertiary",children:[n.jsx("div",{className:"flex-shrink-0 w-8 h-8 rounded-lg bg-blue-100 dark:bg-blue-500/20 flex items-center justify-center",children:n.jsx(AO,{className:"w-4 h-4 text-blue-500 dark:text-blue-400"})}),n.jsxs("div",{className:"flex-1 min-w-0",children:[n.jsx("p",{className:"text-xs text-blue-600 dark:text-blue-300",children:r("printers.skipObjects.matchIdsInfo")}),n.jsx("p",{className:"text-[10px] text-blue-500/70 dark:text-blue-300/60",children:r("printers.skipObjects.printerShowsIds")})]}),n.jsxs("div",{className:"flex-shrink-0 text-xs text-gray-500 dark:text-bambu-gray",children:[m.skipped_count,"/",m.total," ",r("printers.skipObjects.skipped")]})]}),(u?.layer_num??0)<=1&&n.jsxs("div",{className:"flex items-center gap-2 px-4 py-2 bg-amber-50 dark:bg-amber-500/10 border-b border-gray-200 dark:border-bambu-dark-tertiary",children:[n.jsx(Hr,{className:"w-4 h-4 text-amber-500 dark:text-amber-400 flex-shrink-0"}),n.jsx("p",{className:"text-xs text-amber-600 dark:text-amber-400",children:r("printers.skipObjects.waitForLayer",{layer:u?.layer_num??0})})]}),n.jsxs("div",{className:"flex flex-1 overflow-hidden",children:[n.jsx("div",{className:"w-52 flex-shrink-0 p-4 border-r border-gray-200 dark:border-bambu-dark-tertiary bg-gray-50 dark:bg-bambu-dark-secondary overflow-y-auto",children:n.jsxs("div",{className:"relative cursor-pointer group",onClick:()=>d(!0),children:[u?.cover_url?n.jsx("img",{src:Fr(`${u.cover_url}?view=top`),alt:r("printers.printPreview"),className:"w-full aspect-square object-contain rounded-lg bg-gray-900 dark:bg-gray-900 border border-gray-300 dark:border-gray-600"}):n.jsx("div",{className:"w-full aspect-square rounded-lg bg-gray-100 dark:bg-bambu-dark flex items-center justify-center",children:n.jsx(ji,{className:"w-8 h-8 text-gray-300 dark:text-bambu-gray/30"})}),n.jsx("div",{className:"absolute top-2 right-2 p-1 bg-black/60 rounded opacity-0 group-hover:opacity-100 transition-opacity",children:n.jsx(MT,{className:"w-3.5 h-3.5 text-white"})}),m.objects.length>0&&n.jsx("div",{className:"absolute inset-0 pointer-events-none",children:m.objects.map((g,v)=>{let b,x;if(g.x!=null&&g.y!=null&&m.bbox_all){const[S,_,C,P]=m.bbox_all,T=C-S,A=P-_,F=8,k=100-F*2;b=F+(g.x-S)/T*k,x=F+(P-g.y)/A*k,b=Math.max(5,Math.min(95,b)),x=Math.max(5,Math.min(95,x))}else if(g.x!=null&&g.y!=null)b=g.x/256*100,x=100-g.y/256*100,b=Math.max(5,Math.min(95,b)),x=Math.max(5,Math.min(95,x));else{const S=Math.ceil(Math.sqrt(m.objects.length)),_=Math.floor(v/S),C=v%S,P=Math.ceil(m.objects.length/S);b=15+C*(70/S)+35/S,x=15+_*(70/P)+35/P}return n.jsx("div",{className:`absolute flex items-center justify-center w-6 h-6 rounded-full text-[10px] font-bold shadow-lg ${g.skipped?"bg-red-500 text-white line-through":"bg-bambu-green text-black"}`,style:{left:`${b}%`,top:`${x}%`,transform:"translate(-50%, -50%)"},title:g.name,children:g.id},g.id)})}),n.jsx("div",{className:"absolute bottom-2 right-2 px-2 py-1 bg-white/90 dark:bg-black/80 rounded text-[10px] text-gray-700 dark:text-white shadow-sm",children:r("printers.skipObjects.activeCount",{count:m.objects.filter(g=>!g.skipped).length})})]})}),n.jsx("div",{className:"flex-1 min-w-0 overflow-y-auto",children:m.objects.map(g=>n.jsxs("div",{className:` +`);ue.length>0&&(k.extrusionColor=ue),k.processGCode(pe);const X=k.layers?.length||0;x(X),v(X),k.render(),d(!1)}).catch(z=>{z.message!=="not_sliced"&&m(z.message),d(!1)});const I=()=>{if(P.parentElement&&s.current){const z=P.parentElement.getBoundingClientRect();P.width=z.width,P.height=z.height,s.current.resize()}};return window.addEventListener("resize",I),()=>{window.removeEventListener("resize",I),o.current&&cancelAnimationFrame(o.current),s.current&&(s.current.dispose(),s.current=null),l.current=!1}},[t,S]);const _=y.useCallback(P=>{if(!s.current)return;const T=Math.max(1,Math.min(P,b));v(T),o.current&&cancelAnimationFrame(o.current),o.current=requestAnimationFrame(()=>{s.current&&(s.current.endLayer=T,s.current.render())})},[b]),C=P=>{_(parseInt(P.target.value,10))};return n.jsxs("div",{className:`relative flex flex-col h-full ${r}`,children:[n.jsxs("div",{className:"flex-1 relative bg-bambu-dark rounded-lg overflow-hidden",children:[n.jsx("canvas",{ref:i,className:"w-full h-full"}),c&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsxs("div",{className:"text-center",children:[n.jsx(pt,{className:"w-8 h-8 animate-spin text-bambu-green mx-auto mb-2"}),n.jsx("p",{className:"text-bambu-gray text-sm",children:"Loading G-code..."})]})}),p&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsxs("div",{className:"text-center max-w-sm px-4",children:[n.jsx(Nbe,{className:"w-12 h-12 text-bambu-gray mx-auto mb-3"}),n.jsx("p",{className:"text-white font-medium mb-2",children:"G-code not available"}),n.jsx("p",{className:"text-bambu-gray text-sm",children:"This file hasn't been sliced yet. G-code preview is only available after slicing in Bambu Studio or Orca Slicer."})]})}),u&&!p&&n.jsx("div",{className:"absolute inset-0 flex items-center justify-center bg-bambu-dark/80",children:n.jsx("div",{className:"text-center text-red-400",children:n.jsx("p",{className:"text-sm",children:u})})})]}),!c&&!u&&!p&&b>0&&n.jsx("div",{className:"mt-4 px-2",children:n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(cr,{className:"w-4 h-4 text-bambu-gray flex-shrink-0"}),n.jsx("button",{onClick:()=>_(g-1),disabled:g<=1,className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-30 disabled:cursor-not-allowed",children:n.jsx(tl,{className:"w-4 h-4"})}),n.jsx("input",{type:"range",min:1,max:b,value:g,onChange:C,className:"flex-1 h-2 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer accent-bambu-green"}),n.jsx("button",{onClick:()=>_(g+1),disabled:g>=b,className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-30 disabled:cursor-not-allowed",children:n.jsx(qr,{className:"w-4 h-4"})}),n.jsxs("span",{className:"text-sm text-bambu-gray min-w-[80px] text-right",children:[g," / ",b]})]})})]})}function g2e({printerId:t,filePath:e,filename:a,onClose:r}){const[i,s]=y.useState(null),[o,l]=y.useState([]),[c,d]=y.useState(!1),[u,m]=y.useState(null),p=a.toLowerCase().split(".").pop()||"",f=p==="3mf"||p==="stl",g=p==="gcode"||p==="3mf";y.useEffect(()=>{s(f?"3d":g?"gcode":null)},[f,g]),y.useEffect(()=>{l([]),m(null),f&&(d(!0),re.getPrinterFilePlates(t,e).then(x=>l(x.plates||[])).catch(()=>l([])).finally(()=>d(!1)))},[e,f,t]);const v=o.length>1,b=u==null?null:o.find(x=>x.index===u)??null;return n.jsx("div",{className:"fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-6",onClick:r,children:n.jsxs("div",{className:"bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-4xl h-[80vh] flex flex-col",onClick:x=>x.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-bambu-dark-tertiary",children:[n.jsx("h2",{className:"text-lg font-semibold text-white truncate flex-1 mr-4",children:a}),n.jsx(je,{variant:"ghost",size:"sm",onClick:r,children:n.jsx($t,{className:"w-5 h-5"})})]}),n.jsxs("div",{className:"flex border-b border-bambu-dark-tertiary",children:[n.jsxs("button",{onClick:()=>f&&s("3d"),disabled:!f,className:`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${i==="3d"?"text-bambu-green border-b-2 border-bambu-green":f?"text-bambu-gray hover:text-white":"text-bambu-gray/30 cursor-not-allowed"}`,children:[n.jsx(ji,{className:"w-4 h-4"}),"3D Model",!f&&n.jsx("span",{className:"text-xs",children:"(not available)"})]}),n.jsxs("button",{onClick:()=>g&&s("gcode"),disabled:!g,className:`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${i==="gcode"?"text-bambu-green border-b-2 border-bambu-green":g?"text-bambu-gray hover:text-white":"text-bambu-gray/30 cursor-not-allowed"}`,children:[n.jsx(Bs,{className:"w-4 h-4"}),"G-code Preview",!g&&n.jsx("span",{className:"text-xs",children:"(not sliced)"})]})]}),n.jsx("div",{className:"flex-1 overflow-hidden p-4",children:i==="3d"&&f?n.jsxs("div",{className:"w-full h-full flex flex-col gap-3",children:[v&&n.jsxs("div",{className:"rounded-lg border border-bambu-dark-tertiary bg-bambu-dark p-3",children:[n.jsxs("div",{className:"flex items-center gap-2 text-sm text-bambu-gray mb-2",children:[n.jsx(ji,{className:"w-4 h-4"}),"Plates",c&&n.jsx(pt,{className:"w-3 h-3 animate-spin"})]}),n.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-3 gap-2",children:[n.jsxs("button",{type:"button",onClick:()=>m(null),className:`flex items-center gap-2 rounded-lg border p-2 text-left transition-colors ${u==null?"border-bambu-green bg-bambu-green/10":"border-bambu-dark-tertiary bg-bambu-dark-secondary hover:border-bambu-gray"}`,children:[n.jsx("div",{className:"w-10 h-10 rounded bg-bambu-dark-tertiary flex items-center justify-center",children:n.jsx(ji,{className:"w-5 h-5 text-bambu-gray"})}),n.jsxs("div",{className:"min-w-0 flex-1",children:[n.jsx("p",{className:"text-sm text-white font-medium truncate",children:"All Plates"}),n.jsxs("p",{className:"text-xs text-bambu-gray truncate",children:[o.length," plate",o.length!==1?"s":""]})]}),u==null&&n.jsx(ws,{className:"w-4 h-4 text-bambu-green flex-shrink-0"})]}),o.map(x=>n.jsxs("button",{type:"button",onClick:()=>m(x.index),className:`flex items-center gap-2 rounded-lg border p-2 text-left transition-colors ${u===x.index?"border-bambu-green bg-bambu-green/10":"border-bambu-dark-tertiary bg-bambu-dark-secondary hover:border-bambu-gray"}`,children:[x.has_thumbnail?n.jsx("img",{src:re.getPrinterFilePlateThumbnail(t,x.index,e),alt:`Plate ${x.index}`,className:"w-10 h-10 rounded object-cover bg-bambu-dark-tertiary"}):n.jsx("div",{className:"w-10 h-10 rounded bg-bambu-dark-tertiary flex items-center justify-center",children:n.jsx(ji,{className:"w-5 h-5 text-bambu-gray"})}),n.jsxs("div",{className:"min-w-0 flex-1",children:[n.jsx("p",{className:"text-sm text-white font-medium truncate",children:x.name||`Plate ${x.index}`}),n.jsx("p",{className:"text-xs text-bambu-gray truncate",children:x.objects.length>0?x.objects.slice(0,2).join(", ")+(x.objects.length>2?"…":""):`${x.filaments.length} filament${x.filaments.length!==1?"s":""}`})]}),u===x.index&&n.jsx(ws,{className:"w-4 h-4 text-bambu-green flex-shrink-0"})]},x.index))]}),b&&n.jsxs("div",{className:"mt-3 text-xs text-bambu-gray flex flex-wrap gap-x-4 gap-y-1",children:[n.jsxs("span",{children:["Plate ",b.index]}),b.print_time_seconds!=null&&n.jsxs("span",{children:["ETA ",Math.round(b.print_time_seconds/60)," min"]}),b.filament_used_grams!=null&&n.jsxs("span",{children:[b.filament_used_grams.toFixed(1)," g"]}),b.filaments.length>0&&n.jsxs("span",{children:[b.filaments.length," filament",b.filaments.length!==1?"s":""]})]})]}),n.jsx("div",{className:"flex-1",children:n.jsx(iee,{url:re.getPrinterFileDownloadUrl(t,e),fileType:p,selectedPlateId:u,className:"w-full h-full"})})]}):i==="gcode"&&g?n.jsx(Bee,{gcodeUrl:re.getPrinterFileGcodeUrl(t,e),className:"w-full h-full"}):n.jsx("div",{className:"w-full h-full flex items-center justify-center text-bambu-gray",children:"No preview available for this file"})})]})})}function RV(t){if(t===0)return"0 GB";const e=t/(1024*1024*1024);return e>=1?`${e.toFixed(1)} GB`:`${(t/(1024*1024)).toFixed(0)} MB`}function b2e(t,e){if(e)return VZ;switch(t.toLowerCase().split(".").pop()||""){case"3mf":return Th;case"gcode":return Bs;case"mp4":case"avi":return Ah;case"png":case"jpg":case"jpeg":return cu;default:return jT}}const x2e=[{value:"name-asc",label:"Name (A-Z)"},{value:"name-desc",label:"Name (Z-A)"},{value:"size-asc",label:"Size (smallest)"},{value:"size-desc",label:"Size (largest)"},{value:"date-asc",label:"Date (oldest)"},{value:"date-desc",label:"Date (newest)"}];function y2e({printerId:t,printerName:e,onClose:a}){const{t:r}=St(),{showToast:i}=ra(),s=ta(),[o,l]=y.useState("/"),[c,d]=y.useState(new Set),[u,m]=y.useState(""),[p,f]=y.useState([]),[g,v]=y.useState("name-asc"),[b,x]=y.useState(null),[S,_]=y.useState(null);y.useEffect(()=>{const Q=oe=>{oe.key==="Escape"&&a()};return window.addEventListener("keydown",Q),()=>window.removeEventListener("keydown",Q)},[a]);const{data:C,isLoading:P,refetch:T}=Ye({queryKey:["printerFiles",t,o],queryFn:()=>re.getPrinterFiles(t,o)}),{data:A}=Ye({queryKey:["printerStorage",t],queryFn:()=>re.getPrinterStorage(t),staleTime:3e4}),F=lt({mutationFn:async Q=>{for(const oe of Q)await re.deletePrinterFile(t,oe)},onSuccess:()=>{i(r("printerFiles.toast.filesDeleted",{count:p.length})),s.invalidateQueries({queryKey:["printerFiles",t]}),d(new Set),f([])},onError:Q=>{i(r("printerFiles.toast.deleteFailed",{error:Q.message}),"error")}}),k=Q=>{l(Q),d(new Set)},E=()=>{if(o==="/")return;const Q=o.split("/").filter(Boolean);Q.pop(),l(Q.length?"/"+Q.join("/"):"/"),d(new Set)},U=(Q,oe)=>{oe.stopPropagation(),d(ue=>{const pe=new Set(ue);return pe.has(Q)?pe.delete(Q):pe.add(Q),pe})},I=()=>{if(!C?.files)return;const Q=C.files.filter(oe=>!oe.is_directory&&(!u||oe.name.toLowerCase().includes(u.toLowerCase()))).map(oe=>oe.path);d(new Set(Q))},z=()=>{d(new Set)},L=async()=>{if(c.size===0)return;const Q=Array.from(c);if(Q.length===1){re.downloadPrinterFile(t,Q[0]).catch(oe=>{console.error("Printer file download failed:",oe)}),d(new Set);return}x({current:0,total:Q.length});try{const oe=await re.downloadPrinterFilesAsZip(t,Q),ue=URL.createObjectURL(oe),pe=document.createElement("a");pe.href=ue,pe.download=`${e.replace(/[^a-zA-Z0-9]/g,"_")}-files.zip`,document.body.appendChild(pe),pe.click(),document.body.removeChild(pe),URL.revokeObjectURL(ue),i(`Downloaded ${Q.length} files as ZIP`),d(new Set)}catch(oe){i(`Download failed: ${oe instanceof Error?oe.message:"Unknown error"}`,"error")}finally{x(null)}},Z=()=>{c.size!==0&&f(Array.from(c))},ae=[{path:"/",label:"Root"},{path:"/cache",label:"Cache"},{path:"/model",label:"Models"},{path:"/timelapse",label:"Timelapse"}];return n.jsxs("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4",onClick:a,children:[n.jsxs("div",{className:"w-full max-w-3xl max-h-[85vh] flex flex-col bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary overflow-hidden",onClick:Q=>Q.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary flex-shrink-0",children:[n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx(cb,{className:"w-5 h-5 text-bambu-green"}),n.jsxs("div",{children:[n.jsx("h2",{className:"text-lg font-semibold text-white",children:r("printerFiles.title")}),n.jsx("p",{className:"text-sm text-bambu-gray",children:e})]})]}),n.jsxs("div",{className:"flex items-center gap-4",children:[A&&(A.used_bytes!=null||A.free_bytes!=null)&&n.jsxs("div",{className:"text-sm text-bambu-gray flex items-center gap-2",children:[A.used_bytes!=null&&n.jsxs("span",{children:[r("printerFiles.storageUsed")," ",RV(A.used_bytes)]}),A.used_bytes!=null&&A.free_bytes!=null&&n.jsx("span",{className:"text-bambu-dark-tertiary",children:"|"}),A.free_bytes!=null&&n.jsxs("span",{children:[r("printerFiles.storageFree")," ",RV(A.free_bytes)]})]}),n.jsx("button",{onClick:a,className:"text-bambu-gray hover:text-white transition-colors",title:"Close file manager","aria-label":"Close file manager",children:n.jsx($t,{className:"w-5 h-5"})})]})]}),n.jsxs("div",{className:"flex items-center gap-2 p-3 border-b border-bambu-dark-tertiary bg-bambu-dark/50 flex-shrink-0",children:[ae.map(Q=>n.jsx("button",{onClick:()=>{k(Q.path),m("")},className:`px-3 py-1 text-sm rounded-full transition-colors ${o===Q.path?"bg-bambu-green text-white":"bg-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,children:Q.label},Q.path)),n.jsx("div",{className:"flex-1"}),n.jsxs("div",{className:"relative",children:[n.jsx(gn,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-bambu-gray"}),n.jsx("input",{type:"text",placeholder:r("printerFiles.filterPlaceholder"),value:u,onChange:Q=>m(Q.target.value),className:"w-40 pl-8 pr-3 py-1.5 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm focus:border-bambu-green focus:outline-none"})]}),n.jsxs("div",{className:"relative flex items-center gap-1",children:[n.jsx(TT,{className:"w-4 h-4 text-bambu-gray"}),n.jsx("select",{value:g,onChange:Q=>v(Q.target.value),className:"appearance-none bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white text-sm py-1.5 pl-2 pr-6 focus:border-bambu-green focus:outline-none cursor-pointer",title:"Sort files","aria-label":"Sort files",children:x2e.map(Q=>n.jsx("option",{value:Q.value,children:Q.label},Q.value))})]}),n.jsx(je,{variant:"secondary",size:"sm",onClick:()=>T(),disabled:P,children:n.jsx(en,{className:`w-4 h-4 ${P?"animate-spin":""}`})})]}),n.jsxs("div",{className:"flex items-center gap-2 px-4 py-2 bg-bambu-dark text-sm flex-shrink-0",children:[n.jsx("button",{onClick:E,disabled:o==="/",className:"p-1 rounded hover:bg-bambu-dark-tertiary disabled:opacity-50 disabled:cursor-not-allowed",title:"Go to parent folder","aria-label":"Go to parent folder",children:n.jsx(tl,{className:"w-4 h-4"})}),n.jsx("span",{className:"text-bambu-gray font-mono",children:o})]}),n.jsx("div",{className:"flex-1 overflow-y-auto p-2 min-h-0",children:P?n.jsx("div",{className:"flex items-center justify-center py-12",children:n.jsx(pt,{className:"w-8 h-8 text-bambu-green animate-spin"})}):C?.files?.length?n.jsx("div",{className:"space-y-1",children:[...C.files].filter(Q=>!u||Q.name.toLowerCase().includes(u.toLowerCase())).sort((Q,oe)=>{if(Q.is_directory&&!oe.is_directory)return-1;if(!Q.is_directory&&oe.is_directory)return 1;switch(g){case"name-asc":return Q.name.localeCompare(oe.name);case"name-desc":return oe.name.localeCompare(Q.name);case"size-asc":return Q.size-oe.size;case"size-desc":return oe.size-Q.size;case"date-asc":{const ue=Q.mtime?Qa(Q.mtime)?.getTime()??0:0,pe=oe.mtime?Qa(oe.mtime)?.getTime()??0:0;return ue-pe}case"date-desc":{const ue=Q.mtime?Qa(Q.mtime)?.getTime()??0:0;return(oe.mtime?Qa(oe.mtime)?.getTime()??0:0)-ue}default:return Q.name.localeCompare(oe.name)}}).map(Q=>{const oe=b2e(Q.name,Q.is_directory),ue=c.has(Q.path);return n.jsxs("div",{className:`flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors ${ue?"bg-bambu-green/20 border border-bambu-green/50":"hover:bg-bambu-dark-tertiary"}`,onClick:()=>{Q.is_directory&&k(Q.path)},children:[Q.is_directory?null:n.jsx("button",{onClick:pe=>U(Q.path,pe),className:"flex-shrink-0 text-bambu-gray hover:text-white",children:ue?n.jsx(ws,{className:"w-5 h-5 text-bambu-green"}):n.jsx(oo,{className:"w-5 h-5"})}),n.jsx(oe,{className:`w-5 h-5 flex-shrink-0 ${Q.is_directory?"text-bambu-green":"text-bambu-gray"}`}),n.jsx("span",{className:"flex-1 text-white truncate",children:Q.name}),!Q.is_directory&&n.jsxs("div",{className:"flex items-center gap-3",children:[n.jsx("span",{className:"text-sm text-bambu-gray",children:ps(Q.size)}),(Q.name.toLowerCase().endsWith(".3mf")||Q.name.toLowerCase().endsWith(".gcode")||Q.name.toLowerCase().endsWith(".stl"))&&n.jsx("button",{onClick:pe=>{pe.stopPropagation(),_({path:Q.path,name:Q.name})},className:"p-1 rounded hover:bg-bambu-dark text-bambu-gray hover:text-bambu-green",title:"3D View",children:n.jsx(ji,{className:"w-4 h-4"})})]}),Q.is_directory&&n.jsx(tl,{className:"w-4 h-4 text-bambu-gray rotate-180"})]},Q.path)})}):n.jsx("div",{className:"text-center py-12 text-bambu-gray",children:"No files in this directory"})}),n.jsxs("div",{className:"flex items-center justify-between p-4 border-t border-bambu-dark-tertiary bg-bambu-dark/50 flex-shrink-0",children:[n.jsxs("div",{className:"flex items-center gap-4",children:[n.jsx("div",{className:"text-sm text-bambu-gray",children:c.size>0?`${c.size} selected`:u?`${C?.files?.filter(Q=>Q.name.toLowerCase().includes(u.toLowerCase())).length||0} of ${C?.files?.length||0} items`:`${C?.files?.length||0} items`}),C?.files?.some(Q=>!Q.is_directory)&&n.jsx("div",{className:"flex items-center gap-2",children:c.size>0?n.jsxs("button",{onClick:z,className:"flex items-center gap-1 text-xs text-bambu-gray hover:text-white transition-colors",children:[n.jsx(eve,{className:"w-4 h-4"}),"Deselect All"]}):n.jsxs("button",{onClick:I,className:"flex items-center gap-1 text-xs text-bambu-gray hover:text-white transition-colors",children:[n.jsx(ws,{className:"w-4 h-4"}),"Select All"]})})]}),n.jsxs("div",{className:"flex gap-2",children:[n.jsx(je,{variant:"secondary",disabled:c.size===0||b!==null,onClick:L,children:b?n.jsxs(n.Fragment,{children:[n.jsx(pt,{className:"w-4 h-4 animate-spin"}),b.current,"/",b.total]}):n.jsxs(n.Fragment,{children:[n.jsx($n,{className:"w-4 h-4"}),"Download",c.size>1?` (${c.size})`:""]})}),n.jsxs(je,{variant:"secondary",disabled:c.size===0||F.isPending,onClick:Z,className:"text-red-400 hover:text-red-300",children:[F.isPending?n.jsx(pt,{className:"w-4 h-4 animate-spin"}):n.jsx(ea,{className:"w-4 h-4"}),r("printerFiles.deleteButton"),c.size>1?` (${c.size})`:""]})]})]})]}),p.length>0&&n.jsx(va,{title:p.length>1?r("printerFiles.deleteFiles",{count:p.length}):r("fileManager.deleteFile"),message:p.length>1?r("printerFiles.deleteFilesConfirm",{count:p.length}):r("printerFiles.deleteFileConfirm",{name:p[0].split("/").pop()}),confirmText:r("common.delete"),variant:"danger",onConfirm:()=>{F.mutate(p)},onCancel:()=>f([])}),S&&n.jsx(g2e,{printerId:t,filePath:S.path,filename:S.name,onClose:()=>_(null)})]})}function m4({on:t,className:e="w-5 h-5"}){const a=t?"#facc15":"none",r=t?"#78350f":"currentColor",i=t?1:0;return n.jsxs("svg",{viewBox:"0 0 32 32",fill:"none",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:e,children:[n.jsxs("g",{stroke:r,opacity:i,children:[n.jsx("line",{x1:"16",y1:"2",x2:"16",y2:"6"}),n.jsx("line",{x1:"6.1",y1:"6.1",x2:"8.9",y2:"8.9"}),n.jsx("line",{x1:"25.9",y1:"6.1",x2:"23.1",y2:"8.9"}),n.jsx("line",{x1:"2",y1:"16",x2:"6",y2:"16"}),n.jsx("line",{x1:"30",y1:"16",x2:"26",y2:"16"})]}),n.jsx("path",{d:"M12 24v-2.3c0-.9-.4-1.7-1-2.3C9.2 17.6 8 15.4 8 13c0-4.4 3.6-8 8-8s8 3.6 8 8c0 2.4-1.2 4.6-3 6.4-.6.6-1 1.4-1 2.3V24",fill:a,stroke:r}),n.jsx("path",{d:"M12 24h8",stroke:r}),n.jsx("path",{d:"M12 27h8",stroke:r}),n.jsx("path",{d:"M13 30h6",stroke:r})]})}const VT=({className:t})=>n.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",className:t,children:[n.jsx("rect",{x:"2",y:"15",width:"5",height:"5",rx:"0.5"}),n.jsx("rect",{x:"9.5",y:"15",width:"5",height:"5",rx:"0.5",fill:"currentColor",opacity:"0.3"}),n.jsx("rect",{x:"17",y:"15",width:"5",height:"5",rx:"0.5"}),n.jsx("path",{d:"M4 12 C4 6, 14 6, 14 12"}),n.jsx("polyline",{points:"12,10 14,12 12,14"})]});function p4({printerId:t,isOpen:e,onClose:a}){const{t:r}=St(),{showToast:i}=ra(),{hasPermission:s}=un(),[o,l]=y.useState(null),[c,d]=y.useState(!1),{data:u}=Ye({queryKey:["printerStatus",t],queryFn:()=>re.getPrinterStatus(t),refetchInterval:3e4,enabled:e}),{data:m,refetch:p}=Ye({queryKey:["printableObjects",t],queryFn:()=>re.getPrintableObjects(t),enabled:e,refetchInterval:e?5e3:!1}),f=lt({mutationFn:g=>re.skipObjects(t,g),onSuccess:g=>{i(g.message||r("printers.skipObjects.objectsSkipped")),l(null),p()},onError:g=>i(g.message||r("printers.toast.failedToSkipObjects"),"error")});return e?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"fixed inset-0 z-50 flex items-center justify-center",onClick:a,onKeyDown:g=>{g.key==="Escape"&&(c?d(!1):a())},tabIndex:-1,ref:g=>g?.focus(),children:[n.jsx("div",{className:"absolute inset-0 bg-black/50 z-0"}),n.jsxs("div",{className:"relative z-10 bg-white dark:bg-bambu-dark border border-gray-200 dark:border-bambu-dark-tertiary rounded-xl shadow-2xl w-[560px] max-h-[85vh] flex flex-col overflow-hidden",onClick:g=>g.stopPropagation(),children:[n.jsxs("div",{className:"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-bambu-dark-tertiary bg-gray-50 dark:bg-bambu-dark",children:[n.jsxs("div",{className:"flex items-center gap-2",children:[n.jsx(VT,{className:"w-4 h-4 text-bambu-green"}),n.jsx("span",{className:"text-sm font-medium text-gray-900 dark:text-white",children:r("printers.skipObjects.title")})]}),n.jsx("button",{onClick:a,className:"p-1 text-gray-500 dark:text-bambu-gray hover:text-gray-900 dark:hover:text-white rounded transition-colors",children:n.jsx($t,{className:"w-4 h-4"})})]}),m?m.objects.length===0?n.jsxs("div",{className:"text-center py-8 px-4 text-bambu-gray",children:[n.jsx("p",{className:"text-sm",children:r("printers.noObjectsFound")}),n.jsx("p",{className:"text-xs mt-1 opacity-70",children:r("printers.objectsLoadedOnPrintStart")})]}):n.jsxs("div",{className:"flex flex-col overflow-hidden",children:[n.jsxs("div",{className:"flex items-center gap-3 px-4 py-2.5 bg-blue-50 dark:bg-blue-500/10 border-b border-gray-200 dark:border-bambu-dark-tertiary",children:[n.jsx("div",{className:"flex-shrink-0 w-8 h-8 rounded-lg bg-blue-100 dark:bg-blue-500/20 flex items-center justify-center",children:n.jsx(AO,{className:"w-4 h-4 text-blue-500 dark:text-blue-400"})}),n.jsxs("div",{className:"flex-1 min-w-0",children:[n.jsx("p",{className:"text-xs text-blue-600 dark:text-blue-300",children:r("printers.skipObjects.matchIdsInfo")}),n.jsx("p",{className:"text-[10px] text-blue-500/70 dark:text-blue-300/60",children:r("printers.skipObjects.printerShowsIds")})]}),n.jsxs("div",{className:"flex-shrink-0 text-xs text-gray-500 dark:text-bambu-gray",children:[m.skipped_count,"/",m.total," ",r("printers.skipObjects.skipped")]})]}),(u?.layer_num??0)<=1&&n.jsxs("div",{className:"flex items-center gap-2 px-4 py-2 bg-amber-50 dark:bg-amber-500/10 border-b border-gray-200 dark:border-bambu-dark-tertiary",children:[n.jsx(Hr,{className:"w-4 h-4 text-amber-500 dark:text-amber-400 flex-shrink-0"}),n.jsx("p",{className:"text-xs text-amber-600 dark:text-amber-400",children:r("printers.skipObjects.waitForLayer",{layer:u?.layer_num??0})})]}),n.jsxs("div",{className:"flex flex-1 overflow-hidden",children:[n.jsx("div",{className:"w-52 flex-shrink-0 p-4 border-r border-gray-200 dark:border-bambu-dark-tertiary bg-gray-50 dark:bg-bambu-dark-secondary overflow-y-auto",children:n.jsxs("div",{className:"relative cursor-pointer group",onClick:()=>d(!0),children:[u?.cover_url?n.jsx("img",{src:Fr(`${u.cover_url}?view=top`),alt:r("printers.printPreview"),className:"w-full aspect-square object-contain rounded-lg bg-gray-900 dark:bg-gray-900 border border-gray-300 dark:border-gray-600"}):n.jsx("div",{className:"w-full aspect-square rounded-lg bg-gray-100 dark:bg-bambu-dark flex items-center justify-center",children:n.jsx(ji,{className:"w-8 h-8 text-gray-300 dark:text-bambu-gray/30"})}),n.jsx("div",{className:"absolute top-2 right-2 p-1 bg-black/60 rounded opacity-0 group-hover:opacity-100 transition-opacity",children:n.jsx(MT,{className:"w-3.5 h-3.5 text-white"})}),m.objects.length>0&&n.jsx("div",{className:"absolute inset-0 pointer-events-none",children:m.objects.map((g,v)=>{let b,x;if(g.x!=null&&g.y!=null&&m.bbox_all){const[S,_,C,P]=m.bbox_all,T=C-S,A=P-_,F=8,k=100-F*2;b=F+(g.x-S)/T*k,x=F+(P-g.y)/A*k,b=Math.max(5,Math.min(95,b)),x=Math.max(5,Math.min(95,x))}else if(g.x!=null&&g.y!=null)b=g.x/256*100,x=100-g.y/256*100,b=Math.max(5,Math.min(95,b)),x=Math.max(5,Math.min(95,x));else{const S=Math.ceil(Math.sqrt(m.objects.length)),_=Math.floor(v/S),C=v%S,P=Math.ceil(m.objects.length/S);b=15+C*(70/S)+35/S,x=15+_*(70/P)+35/P}return n.jsx("div",{className:`absolute flex items-center justify-center w-6 h-6 rounded-full text-[10px] font-bold shadow-lg ${g.skipped?"bg-red-500 text-white line-through":"bg-bambu-green text-black"}`,style:{left:`${b}%`,top:`${x}%`,transform:"translate(-50%, -50%)"},title:g.name,children:g.id},g.id)})}),n.jsx("div",{className:"absolute bottom-2 right-2 px-2 py-1 bg-white/90 dark:bg-black/80 rounded text-[10px] text-gray-700 dark:text-white shadow-sm",children:r("printers.skipObjects.activeCount",{count:m.objects.filter(g=>!g.skipped).length})})]})}),n.jsx("div",{className:"flex-1 min-w-0 overflow-y-auto",children:m.objects.map(g=>n.jsxs("div",{className:` flex items-center gap-3 px-4 py-3 border-b border-gray-200 dark:border-bambu-dark-tertiary/50 last:border-0 ${g.skipped?"bg-red-50 dark:bg-red-500/10":"hover:bg-gray-50 dark:hover:bg-bambu-dark/50"} `,children:[n.jsxs("div",{className:` diff --git a/static/index.html b/static/index.html index 8e8f5c372..2fbf11687 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@ - +