From 71d506be0c6ec6d7a33c571d54c811ad717c20ff Mon Sep 17 00:00:00 2001 From: maziggy Date: Fri, 14 Aug 2026 14:04:10 +0200 Subject: [PATCH] Show the printer card thumbnail again after navigating back to the page (#2826) The cover URL is cache-busted on the print name, which does not change while a print runs. Leaving the printers page and returning therefore re-mounts with a byte-identical src, which the browser serves from its in-memory cache with no network request -- which is why the reporter's network panel was empty while the placeholder sat there. `loaded` could only ever be set by onLoad, and a mount effect reset it to false unconditionally. For a cache hit those are two racing tasks with no ordering between them: when the load event won, the effect undid it, and nothing put it right afterwards because the URL does not change again for the rest of the print. Read the element's own complete/naturalWidth in that effect instead of assuming nothing has loaded. That settles it whichever task wins, and also covers the variant the reporter proposed, where the handler is not live in time. Being a race explains why it reproduced 100% for the reporter and not at all here. Only the printer card was affected; archive thumbnails build a fresh URL every mount, so they always hit the network. CoverImage is exported so the regression tests can mount it directly. --- CHANGELOG.md | 1 + .../components/CoverImageCachedMount.test.tsx | 193 ++++++++++++++++++ frontend/src/pages/PrintersPage.tsx | 32 ++- .../{index-CwdtRDU5.js => index-Cin_PtM9.js} | 2 +- static/index.html | 2 +- 5 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 frontend/src/__tests__/components/CoverImageCachedMount.test.tsx rename static/assets/{index-CwdtRDU5.js => index-Cin_PtM9.js} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d8f7182..d7af95b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ All notable changes to Bambuddy will be documented in this file. - **Error and warning toasts now stay up twice as long** — Every pop-up notification disappeared after three seconds regardless of what it said. That is about right for "Settings saved", which confirms something you just did and is skimmed rather than read, but errors and warnings are a different kind of message: they carry a reason, often one relayed from the printer or the backend, and they run to a couple of lines. Three seconds was not long enough to finish reading one, and a missed error message is gone for good — there is no notification history to go back to. Errors and warnings now hold for six seconds. Success and informational toasts keep the three-second default, so the common case of clicking something and seeing it confirmed is unchanged, and the close button and the manual dismiss work exactly as before on all of them. The background print-dispatch toast is unaffected: it stays up while it has work in progress and clears itself shortly after the last job settles. Covered by frontend tests. ### Fixed +- **The printer card's thumbnail dropped to the placeholder after leaving the page and coming back, until the browser was reloaded (#2826, reported by @chitrangdesign)** — The thumbnail's address carries the print's name so that starting a different print fetches a different picture. That name does not change while a print runs, so returning to the printers page asked for the very same address again and the browser answered from memory without going to the network at all. The card only ever learned that its picture had arrived from the browser telling it so, and on a picture that was already in hand that announcement raced against the card's own setup, which assumed nothing had arrived yet. When the announcement came first the setup undid it, and nothing put it right afterwards, because the address stays the same for the rest of the print. The card now asks the picture whether it is showing something rather than assuming it is not, which settles the race whichever way round it happens. Being a race is also why it happened every single time for the reporter and not once on the machine it was tested on, and why nothing showed up in the browser's network panel: there was no request to show. Only the printer card was affected -- archive thumbnails use a different address every time and never had it. Covered by frontend tests. - **A print the printer kept on its own internal storage archived as a bare name, with no thumbnail and no reason given (#2780, reported by @Utility9298 and @AntonPalmqvist)** — Bambuddy reads a print's 3MF, cover and timelapse off the printer over FTPS on port 990, and on every Bambu model that port serves external storage only — the card or the stick. It is not a view of the printer's filesystem. Under some configurations H2-series and P2S firmware keeps the sliced file on internal storage instead, and Bambu Studio uploads it there over a separate service on port 6000, so there is nothing on FTPS to fetch at any path. **This is not every H2C or P2S** — plenty archive perfectly, and a print launched from Bambuddy rather than the slicer always does, because Bambuddy uploads over FTPS itself. What decides it is where the file ended up, and the print command says which of the two it used and always has: its `url` field reads `ftp://` for the card and `brtc://emmc/` for internal storage. Bambuddy ignored that field and swept anyway — six filename variants across five directories with four retries for the 3MF, sixteen more paths for the cover, then the timelapse scan, roughly 110 connections per print that could not succeed. In the reporter's support bundle every one of the 35 dispatches to their H2C and P2S said internal storage and every one of the 25 to their X1C said external, and all 44 of the empty archive cards belonged to those two printers and no others. The field is now read, the sweep is skipped when it cannot succeed, and the card says which of the two reasons applies. Nothing changes for a printer that puts its prints on the card: the sweep runs exactly as before, and it also runs whenever the answer is not known — a printer whose broker refuses the request topic never sends us a `url`, and reading that silence as bad news would break archives that work today. The answer is deliberately forgotten when the print it described ends, rather than kept as a standing fact about the printer, because plenty of prints never announce themselves at all: 14 of the 79 print starts in the reporter's bundle arrived with no command on that topic, being started from the printer's own screen or picked up after a restart. Left standing, one slicer print to internal storage would have suppressed the lookup for every screen-started print after it. **This does not make the affected prints archive in full** — reading internal storage needs the port-6000 protocol, which is tracked separately as #2762. Name, timing, status and the finish photo were never affected. Two things that were actively misleading are gone with it. The archives banner told everyone the same thing — that "Store sent files on external storage" was off in the slicer and to go and turn it on — which is wrong advice for this cause and was followed twice: the reporter's three affected printers had that setting on for the entire three weeks of the log, and turning it on again changes nothing. And the connection diagnostic reported a clean pass for a printer whose slot was empty, because it read only the toggle; an empty slot is now reported as a failure with the empty slot named, and a printer that has storage and still used internal storage is reported as a warning rather than a pass. The timelapse scan is gated more narrowly than the rest on purpose: the printer writes its video to the card itself, so where the sliced file went says nothing about whether a video exists, and only a printer that reports an empty slot skips it. Covered by backend and frontend tests. - **A failed FTP connection was dropped without closing its socket** — Every failure path left the connected socket to the garbage collector. That is survivable once and not at the volume this runs at: a single print used to walk about 110 candidate paths, so a printer refusing FTPS got that many sockets opened and abandoned in a couple of minutes, and one support bundle recorded 1813 of them in a day. It may also be part of what sustains the refusal. The message accompanying it has been corrected as well. It stated that the printer's file service was wedged and told the operator to restart the printer; #2780's reporter did that twice with no effect, and a single manual connection to the same printer completes a clean handshake and returns a valid certificate while Bambuddy is failing, so that reading was wrong. The likelier explanation is that the printer is refusing on a connection count — the refusal arrives in cleartext, which is exactly the TLS error we see, and the same limit reached globally produces the handshake timeouts that appear alongside it — but that is not yet proven, so the message now states what was observed and stops there rather than sending people to do the one thing already known not to work. Two other places gave the same advice and no longer do. Covered by backend tests. - **A print stage Bambuddy has no name for now says so in the log** — The printer reports its current activity as a number, and Bambuddy keeps a table of what those numbers mean. The table is hand-maintained and every new model adds to it, so a printer occasionally reports one that is not in it and the card reads "Unknown stage (72)" — which happened on an H2C, where the table runs to 66 and then jumps to 74. Stage changes were logged, but at debug level, which is off in normal running: the only record that it had happened at all was the card, and by the time anyone looked the printer had moved on. An unnamed stage is now recorded at the normal log level, once per stage number, along with the model, the stage it came from and what the printer was doing at the time — which is what identifying it afterwards actually needs. Stages that do have names stay at debug as before, so a normal print logs nothing new. Fixing this turned up a latent crash beside it: the stage-change log line builds its text before the log level is consulted, so a printer reporting a stage that was not a number at all — malformed telemetry rather than an unknown stage — would abort processing of that update entirely. Labelling a value can no longer do that. Covered by backend tests. diff --git a/frontend/src/__tests__/components/CoverImageCachedMount.test.tsx b/frontend/src/__tests__/components/CoverImageCachedMount.test.tsx new file mode 100644 index 000000000..fee319be9 --- /dev/null +++ b/frontend/src/__tests__/components/CoverImageCachedMount.test.tsx @@ -0,0 +1,193 @@ +/** + * The printer-card thumbnail has to survive an image the browser already has (#2826). + * + * The URL is cache-busted on the print *name*, which does not change while a + * print runs. So navigating away from the printers page and back re-mounts + * with a byte-identical `src`, which the browser serves from its in-memory + * cache -- no network request at all, which is why the reporter's Network + * panel was empty while the thumbnail sat on the placeholder. + * + * `loaded` used to be settable only by `onLoad`, while a mount effect reset it + * to false unconditionally. For a cache hit those two are racing tasks with no + * ordering between them, and when `load` won, the effect undid it -- and never + * ran again, because the URL does not change again during the print. That is + * why it reproduced 100% for the reporter and not at all on the maintainer's + * machine. + * + * jsdom never loads images or fires `load`, so the race itself cannot be + * staged here. What these tests pin is the invariant that makes the race + * unwinnable either way: the component must read the element's own state + * instead of assuming nothing has loaded yet. + */ + +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { render, waitFor } from '@testing-library/react'; +import { CoverImage } from '../../pages/PrintersPage'; + +vi.mock('../../hooks/useCameraStreamToken', async () => { + const actual = await vi.importActual>('../../hooks/useCameraStreamToken'); + return { ...actual, withStreamToken: (u: string) => u }; +}); + +/** Present an the way a memory-cache hit does: already complete. */ +function stubAlreadyComplete(naturalWidth = 640) { + Object.defineProperty(HTMLImageElement.prototype, 'complete', { + get: () => true, + configurable: true, + }); + Object.defineProperty(HTMLImageElement.prototype, 'naturalWidth', { + get: () => naturalWidth, + configurable: true, + }); +} + +function restoreImg() { + // @ts-expect-error removing the test-only prototype overrides + delete HTMLImageElement.prototype.complete; + // @ts-expect-error removing the test-only prototype overrides + delete HTMLImageElement.prototype.naturalWidth; +} + +const URL_ = '/api/v1/printers/1/cover'; + +describe('CoverImage with an image the browser already has', () => { + afterEach(restoreImg); + + it('shows it instead of the placeholder', async () => { + stubAlreadyComplete(); + + const { container } = render(); + + await waitFor(() => { + expect(container.querySelector('img')!.className).toContain('block'); + }); + expect(container.querySelector('img')!.className).not.toContain('hidden'); + }); + + it('treats it as loaded across a remount with the same print', async () => { + stubAlreadyComplete(); + + // First visit: warms the cache in a real browser. + const first = render(); + await waitFor(() => expect(first.container.querySelector('img')!.className).toContain('block')); + first.unmount(); + + // Navigating back. Same print name means a byte-identical URL, so nothing + // is fetched -- this is the mount that used to come back blank. + const second = render(); + + await waitFor(() => { + expect(second.container.querySelector('img')!.className).toContain('block'); + }); + }); + + it('makes it clickable, not just visible', async () => { + // `loaded` also gates the click-to-enlarge overlay, so a stuck `false` + // left the thumbnail inert as well as invisible. + stubAlreadyComplete(); + + const { container } = render(); + + await waitFor(() => { + expect(container.querySelector('div')!.className).toContain('cursor-pointer'); + }); + }); +}); + +describe('CoverImage when nothing is cached', () => { + beforeEach(restoreImg); + + it('waits behind the placeholder until the image arrives', () => { + // jsdom leaves `complete` false and never fires `load`, which is exactly + // the cold-cache state: the placeholder is correct until it resolves. + const { container } = render(); + + expect(container.querySelector('img')!.className).toContain('hidden'); + }); + + it('still reveals the image when onLoad fires', async () => { + const { container } = render(); + const img = container.querySelector('img')!; + + img.dispatchEvent(new Event('load')); + + await waitFor(() => expect(img.className).toContain('block')); + }); + + it('falls back to the placeholder when the image fails', async () => { + const { container } = render(); + + container.querySelector('img')!.dispatchEvent(new Event('error')); + + await waitFor(() => expect(container.querySelector('img')).toBeNull()); + }); + + it('shows the placeholder when there is no cover at all', () => { + const { container } = render(); + + expect(container.querySelector('img')).toBeNull(); + }); +}); + +describe('CoverImage when the print changes', () => { + afterEach(restoreImg); + + it('re-evaluates rather than carrying the previous print forward', async () => { + // The reset-on-change behaviour the effect was added for in the first + // place still has to hold: a new print name is a new URL, and a fresh + // element that is not yet complete must go back behind the placeholder. + stubAlreadyComplete(); + const { container, rerender } = render(); + await waitFor(() => expect(container.querySelector('img')!.className).toContain('block')); + + restoreImg(); + rerender(); + + await waitFor(() => { + expect(container.querySelector('img')!.className).toContain('hidden'); + }); + }); + + it('keeps the cache-buster tied to the print name', () => { + stubAlreadyComplete(); + const { container, rerender } = render(); + const first = container.querySelector('img')!.getAttribute('src'); + + rerender(); + const second = container.querySelector('img')!.getAttribute('src'); + + expect(first).toContain('v=Benchy'); + expect(second).toContain('v=Other'); + expect(first).not.toEqual(second); + }); + + it('reuses the URL for the same print, which is what makes the cache hit', () => { + // Pinning the precondition, not an incidental detail: if this ever became + // unique per mount the bug would vanish and so would the caching, and the + // tests above would silently stop covering anything. + stubAlreadyComplete(); + const a = render(); + const first = a.container.querySelector('img')!.getAttribute('src'); + a.unmount(); + + const b = render(); + const second = b.container.querySelector('img')!.getAttribute('src'); + + expect(second).toEqual(first); + }); +}); + +describe('CoverImage with a broken cached image', () => { + afterEach(restoreImg); + + it('does not treat a zero-width complete image as loaded', async () => { + // `complete` is also true for an image that failed. Width is what + // separates "decoded and ready" from "finished, with nothing to show". + stubAlreadyComplete(0); + + const { container } = render(); + + await waitFor(() => expect(container.querySelector('img')).not.toBeNull()); + expect(container.querySelector('img')!.className).toContain('hidden'); + }); +}); diff --git a/frontend/src/pages/PrintersPage.tsx b/frontend/src/pages/PrintersPage.tsx index f8fd932dd..17935d520 100644 --- a/frontend/src/pages/PrintersPage.tsx +++ b/frontend/src/pages/PrintersPage.tsx @@ -943,7 +943,7 @@ function getEmptySlotKind(tray: { tray_type?: string | null; state?: number | nu const DRY_START_CONFIRM_MS = 30_000; -function CoverImage({ +export function CoverImage({ url, printName, className = 'w-20 h-20', @@ -958,6 +958,7 @@ function CoverImage({ const [loaded, setLoaded] = useState(false); const [error, setError] = useState(false); const [showOverlay, setShowOverlay] = useState(false); + const imgRef = useRef(null); // Cache-bust the image URL when the print name changes so the browser // fetches the new cover instead of serving the stale cached image. @@ -967,10 +968,34 @@ function CoverImage({ return withStreamToken(`${url}${sep}v=${encodeURIComponent(printName || Date.now().toString())}`); }, [url, printName]); - // Reset loaded/error state when the image URL changes + // Re-evaluate load state when the image URL changes, and ask the element + // whether it is already showing something rather than assuming it is not. + // + // `onLoad` used to be the only thing that could set `loaded`, and this + // effect reset it to false unconditionally. That is right when the URL + // really changes, but it also runs on mount — and the two are not the same + // situation (#2826). The URL is cache-busted on the print *name*, which is + // constant for the duration of a print, so navigating away from the + // printers page and back re-mounts with a byte-identical src that the + // browser serves from its in-memory cache. The `load` event for a cache hit + // and React's passive-effect flush are both plain tasks with no ordering + // between them, so when `load` won, this effect ran afterwards and undid + // it: `loaded` stayed false, the img stayed `hidden`, and the placeholder + // sat there until a full reload. Nothing recovered it, because the URL + // never changes again during the print and so this effect never re-runs. + // + // Being a race, it reproduced every time for #2826's reporter and not at + // all here, which is also why the network panel showed no request: there + // was no request to show. + // + // Asking `complete && naturalWidth > 0` settles it without needing to know + // which of the two ran first. It is also correct for the case the reporter + // proposed (a `load` that fires before React's handler is live), so the + // fix stands whichever mechanism is really at work. useEffect(() => { - setLoaded(false); setError(false); + const el = imgRef.current; + setLoaded(Boolean(el?.complete && el.naturalWidth > 0)); }, [cacheBustedUrl]); return ( @@ -982,6 +1007,7 @@ function CoverImage({ {cacheBustedUrl && !error ? ( <> {t('printers.printPreview')}e.id>=2),i=Array.from({length:6},(e,t)=>r.find(e=>e.id===16+t)??{id:-(t+1),nozzle_type:``,nozzle_diameter:``,wear:null,stat:null,max_temp:0,serial_number:``,filament_color:``,filament_id:``,filament_type:``});return(0,G.jsxs)(`div`,{className:`text-center px-2.5 py-1.5 bg-bambu-dark rounded-lg flex-[0_1_auto] flex flex-col justify-center`,children:[(0,G.jsx)(`p`,{className:`text-[length:var(--pc-t9,9px)] text-bambu-gray mb-1`,children:n(`printers.nozzleRack`)}),(0,G.jsx)(`div`,{className:`flex gap-[3px] justify-center`,children:i.map((e,n)=>{let r=!e.nozzle_diameter&&!e.nozzle_type,i=r?null:yf(e.filament_color),a=i?bf(e.filament_color):!1;return(0,G.jsx)(lW,{slot:e,index:n,filamentName:e.filament_id?t?.[e.filament_id]?.name:void 0,children:(0,G.jsxs)(`div`,{className:`flex flex-col items-center gap-0.5`,children:[(0,G.jsx)(`div`,{className:`w-[var(--pc-i7,28px)] h-[var(--pc-i7,28px)] rounded flex items-center justify-center cursor-default transition-colors border-b-2 ${r?`bg-bambu-dark-tertiary/20 border-bambu-dark-tertiary/20`:`bg-bambu-dark-tertiary/40 border-bambu-dark-tertiary/40`}`,style:i?{backgroundColor:i}:void 0,children:(0,G.jsx)(`span`,{"data-rack-diameter":!0,className:`text-[length:var(--pc-t10,10px)] font-semibold ${r?`text-bambu-gray/30`:a?`text-black/80`:`text-white`}`,style:i&&!a?{textShadow:`0 1px 3px rgba(0,0,0,0.9)`}:void 0,children:r?`—`:e.nozzle_diameter||`?`})}),(0,G.jsx)(`span`,{className:`text-[length:var(--pc-t8,8px)] leading-none tabular-nums text-bambu-gray/70`,children:n+1})]})},e.id>=0?e.id:`empty-${n}`)})})]})}function MOe({className:e}){return(0,G.jsx)(`svg`,{className:e,viewBox:`0 0 36 54`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,G.jsx)(`path`,{d:`M17.8131 0.00538C18.4463 -0.15091 20.3648 3.14642 20.8264 3.84781C25.4187 10.816 35.3089 26.9368 35.9383 34.8694C37.4182 53.5822 11.882 61.3357 2.53721 45.3789C-1.73471 38.0791 0.016 32.2049 3.178 25.0232C6.99221 16.3662 12.6411 7.90372 17.8131 0.00538ZM18.3738 7.24807L17.5881 7.48441C14.4452 12.9431 10.917 18.2341 8.19369 23.9368C4.6808 31.29 1.18317 38.5479 7.69403 45.5657C17.3058 55.9228 34.9847 46.8808 31.4604 32.8681C29.2558 24.0969 22.4207 15.2913 18.3776 7.24807H18.3738Z`,fill:`#C3C2C1`})})}function NOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 35 53`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M17.3165 0.0038C17.932 -0.14959 19.7971 3.08645 20.2458 3.77481C24.7103 10.6135 34.3251 26.4346 34.937 34.2198C36.3757 52.5848 11.5505 60.1942 2.46584 44.534C-1.68714 37.3735 0.0148 31.6085 3.08879 24.5603C6.79681 16.0605 12.2884 7.75907 17.3165 0.0038ZM17.8615 7.11561L17.0977 7.34755C14.0423 12.7048 10.6124 17.8974 7.96483 23.4941C4.54975 30.7107 1.14949 37.8337 7.47908 44.721C16.8233 54.8856 34.01 46.0117 30.5838 32.2595C28.4405 23.6512 21.7957 15.0093 17.8652 7.11561H17.8615Z`,fill:`#C3C2C1`}),(0,G.jsx)(`path`,{d:`M5.03547 30.112C9.64453 30.4936 11.632 35.7985 16.4154 35.791C19.6339 35.7873 20.2161 33.2283 22.3853 31.6197C31.6776 24.7286 33.5835 37.4894 27.9881 44.4254C18.1878 56.5653 -1.16063 44.6013 5.03917 30.1158L5.03547 30.112Z`,fill:`#1F8FEB`})]})}function POe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 36 54`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M17.9625 4.48059L4.77216 26.3154L2.08228 40.2175L10.0224 50.8414H23.1594L33.3246 42.1693V30.2455L17.9625 4.48059Z`,fill:`#1F8FEB`}),(0,G.jsx)(`path`,{d:`M17.7948 0.00538C18.4273 -0.15091 20.3438 3.14642 20.8048 3.84781C25.3921 10.816 35.2715 26.9368 35.9001 34.8694C37.3784 53.5822 11.8702 61.3357 2.53562 45.3789C-1.73163 38.0829 0.0134 32.2087 3.1757 25.027C6.98574 16.3662 12.6284 7.90372 17.7948 0.00538ZM18.3549 7.24807L17.57 7.48441C14.4306 12.9431 10.9063 18.2341 8.1859 23.9368C4.67686 31.29 1.18305 38.5479 7.68679 45.5657C17.2881 55.9228 34.9476 46.8808 31.4271 32.8681C29.2249 24.0969 22.3974 15.2913 18.3587 7.24807H18.3549Z`,fill:`#C3C2C1`})]})}function FOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2.5`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function IOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`8`,width:`3`,height:`4.5`,fill:`#d4a017`,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:`#d4a017`}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function LOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`3`,width:`3`,height:`9.5`,fill:`#c62828`,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:`#c62828`}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function ROe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 24 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,G.jsx)(`rect`,{x:`9.2`,y:`3.4`,width:`5.6`,height:`8.1`}),(0,G.jsx)(`rect`,{x:`6`,y:`11.5`,width:`12.1`,height:`3.7`}),(0,G.jsx)(`path`,{d:`M 7.3 15.2 L 12.1 19.6 L 16.7 15.2`})]})}function uW({className:e,color:t,isHeating:n}){let r={"text-orange-400":`#fb923c`,"text-blue-400":`#60a5fa`,"text-green-400":`#4ade80`}[t]||`#888`,i=n?{filter:`drop-shadow(0 0 4px ${r}) drop-shadow(0 0 8px ${r})`}:{};return n?(0,G.jsxs)(`svg`,{className:e,style:i,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`3`,width:`3`,height:`9.5`,fill:r,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:r}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:r,strokeWidth:`1`,fill:`none`})]}):(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:r,strokeWidth:`1`,fill:`none`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2.5`,stroke:r,strokeWidth:`1`,fill:`none`})]})}function zOe({state:e,onClick:t}){let{t:n}=U(),r=e!==null,i=`flex items-center justify-center w-[18px] h-[18px] rounded text-[length:var(--pc-t10,10px)] transition-colors `,a;return e===!0?(i+=r?`bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-400 hover:bg-blue-500/30 cursor-pointer`:`bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-400 cursor-default`,a=n(`printers.amsBackup.titleOn`)):e===!1?(i+=r?`bg-bambu-dark text-bambu-gray hover:text-white hover:bg-bambu-dark/80 cursor-pointer`:`bg-bambu-dark text-bambu-gray cursor-default`,a=n(`printers.amsBackup.titleOff`)):(i+=`bg-bambu-dark text-bambu-gray/50 cursor-default`,a=n(`printers.amsBackup.titleUnknown`)),(0,G.jsx)(`button`,{type:`button`,disabled:!r,onClick:()=>r&&t(),className:i,title:a,"aria-label":a,children:r?(0,G.jsx)(Rl,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`}):(0,G.jsx)(`span`,{children:`?`})})}function BOe({hidden:e,onClick:t}){let{t:n}=U(),r=n(e?`printers.externalSpool.show`:`printers.externalSpool.hide`);return(0,G.jsx)(`button`,{type:`button`,onClick:t,"aria-pressed":e,className:`flex items-center justify-center w-[18px] h-[18px] rounded transition-colors cursor-pointer ${e?`bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30`:`bg-bambu-dark text-bambu-gray hover:text-white hover:bg-bambu-dark/80`}`,title:r,"aria-label":r,children:e?(0,G.jsx)(ec,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`}):(0,G.jsx)(tc,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`})})}function dW({humidity:e,goodThreshold:t=40,fairThreshold:n=60,onClick:r,compact:i}){let a=typeof e==`string`?parseInt(e,10):e,o=typeof t==`number`?t:40,s=typeof n==`number`?n:60,c,l;isNaN(a)?(c=`#C3C2C1`,l=`Unknown`):a<=o?(c=`#22a352`,l=`Good`):a<=s?(c=`#d4a017`,l=`Fair`):(c=`#c62828`,l=`Bad`);let u;return u=isNaN(a)||a<=o?MOe:a<=s?NOe:POe,(0,G.jsxs)(`button`,{type:`button`,onClick:r,className:`flex items-center gap-1 ${r?`cursor-pointer hover:opacity-80 transition-opacity`:``}`,title:`Humidity: ${a}% - ${l}${r?` (click for history)`:``}`,children:[(0,G.jsx)(u,{className:i?`w-2.5 h-3`:`w-3 h-4`}),(0,G.jsxs)(`span`,{className:`font-medium tabular-nums ${i?`text-[length:var(--pc-t10,10px)]`:`text-xs`}`,style:{color:c},children:[a,`%`]})]})}function fW({temp:e,goodThreshold:t=28,fairThreshold:n=35,onClick:r,compact:i}){let a=typeof t==`number`?t:28,o=typeof n==`number`?n:35,s,c,l;return e<=a?(s=`#22a352`,c=`Good`,l=FOe):e<=o?(s=`#d4a017`,c=`Fair`,l=IOe):(s=`#c62828`,c=`Bad`,l=LOe),(0,G.jsxs)(`button`,{type:`button`,onClick:r,className:`flex items-center gap-1 ${r?`cursor-pointer hover:opacity-80 transition-opacity`:``}`,title:`Temperature: ${e}°C - ${c}${r?` (click for history)`:``}`,children:[(0,G.jsx)(l,{className:i?`w-2.5 h-3`:`w-3 h-4`}),(0,G.jsxs)(`span`,{className:`tabular-nums text-right ${i?`text-[length:var(--pc-t10,10px)] w-8`:`w-12`}`,style:{color:s},children:[e,`°C`]})]})}function pW(e){return e?.tray_type?null:e?.exists===!0?`reset`:e?.exists===!1||e?.state===9||e?.state===10?`physical`:`reset`}var VOe=3e4;function HOe({url:e,printName:t,className:n=`w-20 h-20`,radiusClass:r=`rounded-lg`}){let{t:i}=U(),[a,o]=(0,y.useState)(!1),[s,c]=(0,y.useState)(!1),[l,u]=(0,y.useState)(!1),d=(0,y.useMemo)(()=>e?ld(`${e}${e.includes(`?`)?`&`:`?`}v=${encodeURIComponent(t||Date.now().toString())}`):null,[e,t]);return(0,y.useEffect)(()=>{o(!1),c(!1)},[d]),(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`${n} flex-shrink-0 ${r} overflow-hidden bg-bambu-dark-tertiary flex items-center justify-center ${d&&a?`cursor-pointer`:``}`,onClick:()=>d&&a&&u(!0),children:d&&!s?(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`img`,{src:d,alt:i(`printers.printPreview`),className:`w-full h-full object-cover ${a?`block`:`hidden`}`,onLoad:()=>o(!0),onError:()=>c(!0)}),!a&&(0,G.jsx)(Yo,{className:`w-8 h-8 text-bambu-gray`})]}):(0,G.jsx)(Yo,{className:`w-8 h-8 text-bambu-gray`})}),l&&d&&(0,G.jsx)(`div`,{className:`fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-8`,onClick:()=>u(!1),children:(0,G.jsxs)(`div`,{className:`relative max-w-2xl max-h-full`,children:[(0,G.jsx)(`img`,{src:d,alt:i(`printers.printPreview`),className:`max-w-full max-h-[80vh] rounded-lg shadow-2xl`}),t&&(0,G.jsx)(`p`,{className:`text-white text-center mt-4 text-lg`,children:t})]})})]})}function UOe({printers:e}){let{t}=U(),n=so(),[r,i]=(0,y.useState)(0);(0,y.useEffect)(()=>{let e=!1,t=n.getQueryCache().subscribe(()=>{e||(e=!0,requestAnimationFrame(()=>{i(e=>e+1),e=!1}))});return()=>t()},[n]);let{counts:a,nextFinish:o}=(0,y.useMemo)(()=>{let t=0,r=0,i=0,a=0,o=0,s=0,c=0,l=null,u=null,d=0;return e?.forEach(e=>{let f=n.getQueryData([`printerStatus`,e.id]);if(f===void 0)s++;else if(!f.connected)o++;else{let n=f.hms_errors?hm(f.hms_errors).length:0;switch(n>0&&c++,f.state){case`RUNNING`:t++,f.remaining_time!=null&&f.remaining_time>0&&(u===null||f.remaining_time0||i++;break;default:a++;break}}}),{counts:{printing:t,paused:r,finished:i,idle:a,offline:o,loading:s,error:c,total:e?.length||0},nextFinish:l&&u?{name:l,remainingMin:u,progress:d}:null}},[e,n,r]);return e?.length?(0,G.jsxs)(`div`,{className:`mt-1 flex flex-wrap items-center gap-4 gap-y-2 text-bambu-gray`,children:[[{count:a.printing,dot:`bg-bambu-green animate-pulse`,label:t(`printers.status.printing`).toLowerCase()},{count:a.paused,dot:`bg-status-warning`,label:t(`printers.status.paused`,`paused`).toLowerCase()},{count:a.finished,dot:`bg-blue-400`,label:t(`printers.status.finished`,`finished`).toLowerCase()},{count:a.idle,dot:a.idle>0?`bg-bambu-green`:`bg-gray-500`,label:t(`printers.status.available`).toLowerCase()},{count:a.error,dot:`bg-status-error`,label:t(`printers.status.problem`).toLowerCase()},{count:a.offline,dot:`bg-gray-400`,label:t(`printers.status.offline`).toLowerCase()}].map(({count:e,dot:t,label:n})=>e>0&&(0,G.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,G.jsx)(`div`,{className:`w-2 h-2 rounded-full ${t}`}),(0,G.jsxs)(`span`,{className:`text-bambu-gray`,children:[(0,G.jsx)(`span`,{className:`text-white font-medium`,children:e}),` `,n]})]},n)),o&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`w-px h-4 bg-bambu-dark-tertiary`}),(0,G.jsxs)(`div`,{className:`flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-2`,children:[(0,G.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,G.jsxs)(`span`,{className:`text-bambu-green font-medium`,children:[t(`printers.nextAvailable`),`:`]}),(0,G.jsx)(`span`,{className:`text-white font-medium`,children:o.name})]}),(0,G.jsxs)(`div`,{className:`flex items-center gap-2 w-full sm:w-auto`,children:[(0,G.jsx)(`div`,{className:`w-full sm:w-16 bg-bambu-dark-tertiary rounded-full h-1.5`,children:(0,G.jsx)(`div`,{className:`bg-bambu-green h-1.5 rounded-full transition-all`,style:{width:`${o.progress}%`}})}),(0,G.jsxs)(`span`,{className:`text-white font-medium`,children:[Math.round(o.progress),`%`]}),(0,G.jsxs)(`span`,{className:`text-bambu-gray`,children:[`(`,Yf(o.remainingMin*60),`)`]})]})]})]})]}):null}function mW({value:e,options:t,onChange:n,fullWidth:r=!1}){let[i,a]=(0,y.useState)(!1),o=t.find(t=>t.value===e)??t[0];return(0,G.jsxs)(`div`,{className:`relative ${r?`w-full min-w-0`:``}`,children:[(0,G.jsxs)(`button`,{type:`button`,onClick:()=>a(e=>!e),className:`h-8 px-2 rounded-lg border bg-bambu-dark border-bambu-dark-tertiary text-white text-sm font-medium transition-colors hover:bg-bambu-dark-tertiary focus:outline-none focus:border-bambu-green flex items-center justify-between gap-2 ${r?`w-full`:`min-w-28`}`,children:[(0,G.jsx)(`span`,{className:`truncate`,children:o?.label}),(0,G.jsx)(ds,{className:`w-4 h-4 text-bambu-gray transition-transform ${i?`rotate-180`:``}`})]}),i&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-10`,onClick:()=>a(!1)}),(0,G.jsx)(`div`,{className:`absolute left-0 top-full z-20 mt-1 min-w-full rounded-lg border border-bambu-dark-tertiary bg-bambu-dark-secondary py-1 shadow-xl`,children:t.map(t=>(0,G.jsx)(`button`,{type:`button`,onClick:()=>{n(t.value),a(!1)},className:`w-full px-3 py-2 text-left text-sm transition-colors hover:bg-bambu-dark-tertiary ${t.value===e?`text-bambu-green`:`text-white`}`,children:t.label},t.value))})]})]})}function hW({label:e,icon:t,children:n}){let[r,i]=(0,y.useState)(!1);return(0,G.jsxs)(`div`,{className:`relative`,children:[(0,G.jsx)(`button`,{type:`button`,onClick:()=>i(e=>!e),className:`h-8 w-8 rounded-lg border bg-bambu-dark border-bambu-dark-tertiary text-white hover:bg-bambu-dark-tertiary transition-colors flex items-center justify-center`,"aria-label":e,title:e,children:t}),r&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-10`,onClick:()=>i(!1)}),(0,G.jsx)(`div`,{className:`absolute right-0 top-full z-20 mt-1 min-w-40 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark-secondary p-2 shadow-xl`,children:n})]})]})}function gW({title:e,options:t=[],unit:n,customMin:r,customMax:i,customStep:a=1,widthClass:o=`w-[240px]`,popoverWidth:s=240,popoverHeight:c=280,isPending:l,onClose:u,onSubmit:d,children:f}){let p=(0,y.useRef)(null),[m,h]=(0,y.useState)(null),[g,_]=(0,y.useState)(``);(0,y.useLayoutEffect)(()=>{let e=p.current?.parentElement;if(!e)return;let t=()=>{h(Qp({triggerRect:e.getBoundingClientRect(),popoverWidth:s,estimatedHeight:c,horizontalAlign:`center`}))};return t(),window.addEventListener(`resize`,t),window.addEventListener(`scroll`,t,!0),()=>{window.removeEventListener(`resize`,t),window.removeEventListener(`scroll`,t,!0)}},[s,c]);let v=n!==void 0,b=()=>{let e=Number(g);Number.isFinite(e)&&d?.(Math.round(Math.min(i??e,Math.max(r??e,e))))};return(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`span`,{ref:p,className:`hidden`,"aria-hidden":`true`}),(0,At.createPortal)((0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-[1000]`,onClick:u}),(0,G.jsxs)(`div`,{className:`fixed z-[1001] flex ${o} flex-col overflow-hidden rounded-xl border border-bambu-dark-tertiary bg-bambu-dark-secondary shadow-2xl`,style:{top:m?.top??-9999,left:m?.left??-9999,visibility:m?`visible`:`hidden`},onClick:e=>e.stopPropagation(),children:[(0,G.jsx)(`div`,{className:`shrink-0 px-3 py-2.5 text-center text-sm font-medium text-white`,children:e}),(0,G.jsx)(`div`,{className:`shrink-0 h-px bg-bambu-dark-tertiary`}),t.length>0&&(0,G.jsx)(`div`,{className:`px-3 py-2.5`,children:(0,G.jsx)(`div`,{className:`grid grid-cols-2 gap-1.5`,children:t.map(e=>(0,G.jsx)(`button`,{type:`button`,disabled:l,onClick:()=>d?.(e.value),className:`h-8 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:e.label},`${e.label}-${e.value}`))})}),f,v&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`shrink-0 h-px bg-bambu-dark-tertiary`}),(0,G.jsxs)(`form`,{className:`flex gap-1.5 px-3 pt-2.5 pb-3`,onSubmit:e=>{e.preventDefault(),b()},children:[(0,G.jsx)(`input`,{type:`number`,min:r,max:i,step:a,value:g,onChange:e=>_(e.target.value),placeholder:`Custom`,className:`h-8 min-w-0 flex-1 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs text-white placeholder:text-bambu-gray/60 focus:border-bambu-green focus:outline-none`}),(0,G.jsx)(`button`,{type:`submit`,disabled:l||g.trim()===``,className:`h-8 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:`Set`})]})]})]})]}),document.body)]})}var WOe=dm(om,`C`);function _W({label:e,current:t,target:n,isActive:r,isPending:i,onSubmit:a,options:o=WOe}){let[s,c]=(0,y.useState)(``),l=()=>{let e=Number(s);Number.isFinite(e)&&a(Math.min(320,Math.max(0,Math.round(e))))};return(0,G.jsxs)(`div`,{className:`rounded-lg border p-2 ${r?`border-amber-500/60 dark:border-amber-400/60 bg-amber-50 dark:bg-amber-400/10`:`border-bambu-dark-tertiary bg-bambu-dark`}`,children:[(0,G.jsxs)(`div`,{className:`mb-1.5 flex items-center justify-between gap-2`,children:[(0,G.jsx)(`span`,{className:`text-xs font-medium ${r?`text-amber-700 dark:text-amber-300`:`text-white`}`,children:e}),(0,G.jsxs)(`span`,{className:`text-[10px] text-bambu-gray`,children:[Math.round(t??0),`°C`,n===void 0?``:` / ${Math.round(n)}°`]})]}),(0,G.jsx)(`div`,{className:`grid grid-cols-2 gap-1`,children:o.map(t=>(0,G.jsx)(`button`,{type:`button`,disabled:i,onClick:()=>a(t.value),className:`h-7 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-1.5 text-[11px] font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:t.label},`${e}-${t.value}`))}),(0,G.jsxs)(`form`,{className:`mt-1.5 flex gap-1`,onSubmit:e=>{e.preventDefault(),l()},children:[(0,G.jsx)(`input`,{type:`number`,min:0,max:320,step:1,value:s,onChange:e=>c(e.target.value),placeholder:`Custom`,className:`h-7 min-w-0 flex-1 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-1.5 text-[11px] text-white placeholder:text-bambu-gray/60 focus:border-bambu-green focus:outline-none`}),(0,G.jsx)(`button`,{type:`submit`,disabled:i||s.trim()===``,className:`h-7 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-2 text-[11px] font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:`Set`})]})]})}var GOe=[`error`,`printing`,`paused`,`finished`,`idle`,`offline`],vW={error:{labelKey:`printers.status.problem`,dot:`bg-status-error`},printing:{labelKey:`printers.status.printing`,dot:`bg-bambu-green animate-pulse`},paused:{labelKey:`printers.status.paused`,dot:`bg-status-warning`},finished:{labelKey:`printers.status.finished`,dot:`bg-blue-400`},idle:{labelKey:`printers.status.idle`,dot:`bg-bambu-green`},offline:{labelKey:`printers.status.offline`,dot:`bg-gray-400`}};function yW(e){if(!e?.connected)return`offline`;if((e.hms_errors?hm(e.hms_errors):[]).length>0)return`error`;switch(e.state){case`RUNNING`:return`printing`;case`PAUSE`:return`paused`;case`FINISH`:return`finished`;case`FAILED`:return`finished`;default:return`idle`}}function KOe(e,t){if(t)return t;switch(e){case`RUNNING`:return`Printing`;case`PAUSE`:return`Paused`;case`FINISH`:return`Finished`;case`FAILED`:return`Failed`;case`IDLE`:return`Idle`;default:return e?e.charAt(0)+e.slice(1).toLowerCase():`Idle`}}var qOe=new Set([`X1C`,`X1`,`X1E`,`X2D`,`P1S`,`P2S`,`H2D`,`H2D Pro`,`H2C`,`H2S`]),JOe=new Set([`P2S`,`X2D`]);function bW(e){return e?{O1D:`H2D`,O1E:`H2D Pro`,O2D:`H2D Pro`,O1C:`H2C`,O1C2:`H2C`,O1S:`H2S`,"BL-P001":`X1C`,"BL-P002":`X1`,"BL-P003":`X1E`,N6:`X2D`,N9:`A2L`,C11:`P1S`,C12:`P1P`,C13:`P2S`,N2S:`A1`,N1:`A1 Mini`,X1C:`X1C`,X1:`X1`,X1E:`X1E`,X2D:`X2D`,P1S:`P1S`,P1P:`P1P`,P2S:`P2S`,A1:`A1`,"A1 Mini":`A1 Mini`,A2L:`A2L`,H2D:`H2D`,"H2D Pro":`H2D Pro`,H2C:`H2C`,H2S:`H2S`}[e]||e:``}function xW({ams:e,printerId:t,label:n,amsLabels:r,canEdit:i,onSaved:a,children:o}){let{t:s}=U(),[c,l]=(0,y.useState)(!1),[u,d]=(0,y.useState)(`top`),[f,p]=(0,y.useState)(``),[m,h]=(0,y.useState)(!1),[g,_]=(0,y.useState)(null),[v,b]=(0,y.useState)(!1),x=(0,y.useRef)(null),S=(0,y.useRef)(null),C=(0,y.useRef)(null);(0,y.useEffect)(()=>{c&&(p(r?.[e.id]??``),_(null),requestAnimationFrame(()=>{if(x.current&&S.current){let e=x.current.getBoundingClientRect(),t=e.top-56,n=window.innerHeight-e.bottom;d(tt?`bottom`:`top`)}}))},[c,r,e.id]);let w=()=>{C.current&&clearTimeout(C.current),C.current=setTimeout(()=>l(!0),80)},T=()=>{C.current&&clearTimeout(C.current),v||(C.current=setTimeout(()=>l(!1),200))};(0,y.useEffect)(()=>()=>{C.current&&clearTimeout(C.current)},[]);let E=async()=>{if(i){h(!0),_(null);try{let n=f.trim();n?await Z.saveAmsLabel(t,e.id,n,e.serial_number):await Z.deleteAmsLabel(t,e.id,e.serial_number),a(),l(!1)}catch(e){_(e instanceof Error?e.message:String(e))}finally{h(!1)}}};return(0,G.jsxs)(`div`,{ref:x,className:`relative inline-block`,onMouseEnter:w,onMouseLeave:T,children:[o,c&&(0,G.jsx)(`div`,{ref:S,className:` + `})]})]})}function jOe({slots:e,filamentInfo:t}){let{t:n}=U(),r=e.filter(e=>e.id>=2),i=Array.from({length:6},(e,t)=>r.find(e=>e.id===16+t)??{id:-(t+1),nozzle_type:``,nozzle_diameter:``,wear:null,stat:null,max_temp:0,serial_number:``,filament_color:``,filament_id:``,filament_type:``});return(0,G.jsxs)(`div`,{className:`text-center px-2.5 py-1.5 bg-bambu-dark rounded-lg flex-[0_1_auto] flex flex-col justify-center`,children:[(0,G.jsx)(`p`,{className:`text-[length:var(--pc-t9,9px)] text-bambu-gray mb-1`,children:n(`printers.nozzleRack`)}),(0,G.jsx)(`div`,{className:`flex gap-[3px] justify-center`,children:i.map((e,n)=>{let r=!e.nozzle_diameter&&!e.nozzle_type,i=r?null:yf(e.filament_color),a=i?bf(e.filament_color):!1;return(0,G.jsx)(lW,{slot:e,index:n,filamentName:e.filament_id?t?.[e.filament_id]?.name:void 0,children:(0,G.jsxs)(`div`,{className:`flex flex-col items-center gap-0.5`,children:[(0,G.jsx)(`div`,{className:`w-[var(--pc-i7,28px)] h-[var(--pc-i7,28px)] rounded flex items-center justify-center cursor-default transition-colors border-b-2 ${r?`bg-bambu-dark-tertiary/20 border-bambu-dark-tertiary/20`:`bg-bambu-dark-tertiary/40 border-bambu-dark-tertiary/40`}`,style:i?{backgroundColor:i}:void 0,children:(0,G.jsx)(`span`,{"data-rack-diameter":!0,className:`text-[length:var(--pc-t10,10px)] font-semibold ${r?`text-bambu-gray/30`:a?`text-black/80`:`text-white`}`,style:i&&!a?{textShadow:`0 1px 3px rgba(0,0,0,0.9)`}:void 0,children:r?`—`:e.nozzle_diameter||`?`})}),(0,G.jsx)(`span`,{className:`text-[length:var(--pc-t8,8px)] leading-none tabular-nums text-bambu-gray/70`,children:n+1})]})},e.id>=0?e.id:`empty-${n}`)})})]})}function MOe({className:e}){return(0,G.jsx)(`svg`,{className:e,viewBox:`0 0 36 54`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:(0,G.jsx)(`path`,{d:`M17.8131 0.00538C18.4463 -0.15091 20.3648 3.14642 20.8264 3.84781C25.4187 10.816 35.3089 26.9368 35.9383 34.8694C37.4182 53.5822 11.882 61.3357 2.53721 45.3789C-1.73471 38.0791 0.016 32.2049 3.178 25.0232C6.99221 16.3662 12.6411 7.90372 17.8131 0.00538ZM18.3738 7.24807L17.5881 7.48441C14.4452 12.9431 10.917 18.2341 8.19369 23.9368C4.6808 31.29 1.18317 38.5479 7.69403 45.5657C17.3058 55.9228 34.9847 46.8808 31.4604 32.8681C29.2558 24.0969 22.4207 15.2913 18.3776 7.24807H18.3738Z`,fill:`#C3C2C1`})})}function NOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 35 53`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M17.3165 0.0038C17.932 -0.14959 19.7971 3.08645 20.2458 3.77481C24.7103 10.6135 34.3251 26.4346 34.937 34.2198C36.3757 52.5848 11.5505 60.1942 2.46584 44.534C-1.68714 37.3735 0.0148 31.6085 3.08879 24.5603C6.79681 16.0605 12.2884 7.75907 17.3165 0.0038ZM17.8615 7.11561L17.0977 7.34755C14.0423 12.7048 10.6124 17.8974 7.96483 23.4941C4.54975 30.7107 1.14949 37.8337 7.47908 44.721C16.8233 54.8856 34.01 46.0117 30.5838 32.2595C28.4405 23.6512 21.7957 15.0093 17.8652 7.11561H17.8615Z`,fill:`#C3C2C1`}),(0,G.jsx)(`path`,{d:`M5.03547 30.112C9.64453 30.4936 11.632 35.7985 16.4154 35.791C19.6339 35.7873 20.2161 33.2283 22.3853 31.6197C31.6776 24.7286 33.5835 37.4894 27.9881 44.4254C18.1878 56.5653 -1.16063 44.6013 5.03917 30.1158L5.03547 30.112Z`,fill:`#1F8FEB`})]})}function POe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 36 54`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M17.9625 4.48059L4.77216 26.3154L2.08228 40.2175L10.0224 50.8414H23.1594L33.3246 42.1693V30.2455L17.9625 4.48059Z`,fill:`#1F8FEB`}),(0,G.jsx)(`path`,{d:`M17.7948 0.00538C18.4273 -0.15091 20.3438 3.14642 20.8048 3.84781C25.3921 10.816 35.2715 26.9368 35.9001 34.8694C37.3784 53.5822 11.8702 61.3357 2.53562 45.3789C-1.73163 38.0829 0.0134 32.2087 3.1757 25.027C6.98574 16.3662 12.6284 7.90372 17.7948 0.00538ZM18.3549 7.24807L17.57 7.48441C14.4306 12.9431 10.9063 18.2341 8.1859 23.9368C4.67686 31.29 1.18305 38.5479 7.68679 45.5657C17.2881 55.9228 34.9476 46.8808 31.4271 32.8681C29.2249 24.0969 22.3974 15.2913 18.3587 7.24807H18.3549Z`,fill:`#C3C2C1`})]})}function FOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2.5`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function IOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`8`,width:`3`,height:`4.5`,fill:`#d4a017`,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:`#d4a017`}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function LOe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`3`,width:`3`,height:`9.5`,fill:`#c62828`,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:`#c62828`}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:`#C3C2C1`,strokeWidth:`1`,fill:`none`})]})}function ROe({className:e}){return(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 24 24`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,stroke:`currentColor`,strokeWidth:`1.5`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,G.jsx)(`rect`,{x:`9.2`,y:`3.4`,width:`5.6`,height:`8.1`}),(0,G.jsx)(`rect`,{x:`6`,y:`11.5`,width:`12.1`,height:`3.7`}),(0,G.jsx)(`path`,{d:`M 7.3 15.2 L 12.1 19.6 L 16.7 15.2`})]})}function uW({className:e,color:t,isHeating:n}){let r={"text-orange-400":`#fb923c`,"text-blue-400":`#60a5fa`,"text-green-400":`#4ade80`}[t]||`#888`,i=n?{filter:`drop-shadow(0 0 4px ${r}) drop-shadow(0 0 8px ${r})`}:{};return n?(0,G.jsxs)(`svg`,{className:e,style:i,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`rect`,{x:`4.5`,y:`3`,width:`3`,height:`9.5`,fill:r,rx:`0.5`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2`,fill:r}),(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:r,strokeWidth:`1`,fill:`none`})]}):(0,G.jsxs)(`svg`,{className:e,viewBox:`0 0 12 20`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,children:[(0,G.jsx)(`path`,{d:`M6 0.5C4.6 0.5 3.5 1.6 3.5 3V12.1C2.6 12.8 2 13.9 2 15C2 17.2 3.8 19 6 19C8.2 19 10 17.2 10 15C10 13.9 9.4 12.8 8.5 12.1V3C8.5 1.6 7.4 0.5 6 0.5Z`,stroke:r,strokeWidth:`1`,fill:`none`}),(0,G.jsx)(`circle`,{cx:`6`,cy:`15`,r:`2.5`,stroke:r,strokeWidth:`1`,fill:`none`})]})}function zOe({state:e,onClick:t}){let{t:n}=U(),r=e!==null,i=`flex items-center justify-center w-[18px] h-[18px] rounded text-[length:var(--pc-t10,10px)] transition-colors `,a;return e===!0?(i+=r?`bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-400 hover:bg-blue-500/30 cursor-pointer`:`bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-400 cursor-default`,a=n(`printers.amsBackup.titleOn`)):e===!1?(i+=r?`bg-bambu-dark text-bambu-gray hover:text-white hover:bg-bambu-dark/80 cursor-pointer`:`bg-bambu-dark text-bambu-gray cursor-default`,a=n(`printers.amsBackup.titleOff`)):(i+=`bg-bambu-dark text-bambu-gray/50 cursor-default`,a=n(`printers.amsBackup.titleUnknown`)),(0,G.jsx)(`button`,{type:`button`,disabled:!r,onClick:()=>r&&t(),className:i,title:a,"aria-label":a,children:r?(0,G.jsx)(Rl,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`}):(0,G.jsx)(`span`,{children:`?`})})}function BOe({hidden:e,onClick:t}){let{t:n}=U(),r=n(e?`printers.externalSpool.show`:`printers.externalSpool.hide`);return(0,G.jsx)(`button`,{type:`button`,onClick:t,"aria-pressed":e,className:`flex items-center justify-center w-[18px] h-[18px] rounded transition-colors cursor-pointer ${e?`bg-bambu-green/20 text-bambu-green hover:bg-bambu-green/30`:`bg-bambu-dark text-bambu-gray hover:text-white hover:bg-bambu-dark/80`}`,title:r,"aria-label":r,children:e?(0,G.jsx)(ec,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`}):(0,G.jsx)(tc,{className:`w-[var(--pc-i3,0.75rem)] h-[var(--pc-i3,0.75rem)]`})})}function dW({humidity:e,goodThreshold:t=40,fairThreshold:n=60,onClick:r,compact:i}){let a=typeof e==`string`?parseInt(e,10):e,o=typeof t==`number`?t:40,s=typeof n==`number`?n:60,c,l;isNaN(a)?(c=`#C3C2C1`,l=`Unknown`):a<=o?(c=`#22a352`,l=`Good`):a<=s?(c=`#d4a017`,l=`Fair`):(c=`#c62828`,l=`Bad`);let u;return u=isNaN(a)||a<=o?MOe:a<=s?NOe:POe,(0,G.jsxs)(`button`,{type:`button`,onClick:r,className:`flex items-center gap-1 ${r?`cursor-pointer hover:opacity-80 transition-opacity`:``}`,title:`Humidity: ${a}% - ${l}${r?` (click for history)`:``}`,children:[(0,G.jsx)(u,{className:i?`w-2.5 h-3`:`w-3 h-4`}),(0,G.jsxs)(`span`,{className:`font-medium tabular-nums ${i?`text-[length:var(--pc-t10,10px)]`:`text-xs`}`,style:{color:c},children:[a,`%`]})]})}function fW({temp:e,goodThreshold:t=28,fairThreshold:n=35,onClick:r,compact:i}){let a=typeof t==`number`?t:28,o=typeof n==`number`?n:35,s,c,l;return e<=a?(s=`#22a352`,c=`Good`,l=FOe):e<=o?(s=`#d4a017`,c=`Fair`,l=IOe):(s=`#c62828`,c=`Bad`,l=LOe),(0,G.jsxs)(`button`,{type:`button`,onClick:r,className:`flex items-center gap-1 ${r?`cursor-pointer hover:opacity-80 transition-opacity`:``}`,title:`Temperature: ${e}°C - ${c}${r?` (click for history)`:``}`,children:[(0,G.jsx)(l,{className:i?`w-2.5 h-3`:`w-3 h-4`}),(0,G.jsxs)(`span`,{className:`tabular-nums text-right ${i?`text-[length:var(--pc-t10,10px)] w-8`:`w-12`}`,style:{color:s},children:[e,`°C`]})]})}function pW(e){return e?.tray_type?null:e?.exists===!0?`reset`:e?.exists===!1||e?.state===9||e?.state===10?`physical`:`reset`}var VOe=3e4;function HOe({url:e,printName:t,className:n=`w-20 h-20`,radiusClass:r=`rounded-lg`}){let{t:i}=U(),[a,o]=(0,y.useState)(!1),[s,c]=(0,y.useState)(!1),[l,u]=(0,y.useState)(!1),d=(0,y.useRef)(null),f=(0,y.useMemo)(()=>e?ld(`${e}${e.includes(`?`)?`&`:`?`}v=${encodeURIComponent(t||Date.now().toString())}`):null,[e,t]);return(0,y.useEffect)(()=>{c(!1);let e=d.current;o(!!(e?.complete&&e.naturalWidth>0))},[f]),(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`${n} flex-shrink-0 ${r} overflow-hidden bg-bambu-dark-tertiary flex items-center justify-center ${f&&a?`cursor-pointer`:``}`,onClick:()=>f&&a&&u(!0),children:f&&!s?(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`img`,{ref:d,src:f,alt:i(`printers.printPreview`),className:`w-full h-full object-cover ${a?`block`:`hidden`}`,onLoad:()=>o(!0),onError:()=>c(!0)}),!a&&(0,G.jsx)(Yo,{className:`w-8 h-8 text-bambu-gray`})]}):(0,G.jsx)(Yo,{className:`w-8 h-8 text-bambu-gray`})}),l&&f&&(0,G.jsx)(`div`,{className:`fixed inset-0 bg-black/80 flex items-center justify-center z-50 p-8`,onClick:()=>u(!1),children:(0,G.jsxs)(`div`,{className:`relative max-w-2xl max-h-full`,children:[(0,G.jsx)(`img`,{src:f,alt:i(`printers.printPreview`),className:`max-w-full max-h-[80vh] rounded-lg shadow-2xl`}),t&&(0,G.jsx)(`p`,{className:`text-white text-center mt-4 text-lg`,children:t})]})})]})}function UOe({printers:e}){let{t}=U(),n=so(),[r,i]=(0,y.useState)(0);(0,y.useEffect)(()=>{let e=!1,t=n.getQueryCache().subscribe(()=>{e||(e=!0,requestAnimationFrame(()=>{i(e=>e+1),e=!1}))});return()=>t()},[n]);let{counts:a,nextFinish:o}=(0,y.useMemo)(()=>{let t=0,r=0,i=0,a=0,o=0,s=0,c=0,l=null,u=null,d=0;return e?.forEach(e=>{let f=n.getQueryData([`printerStatus`,e.id]);if(f===void 0)s++;else if(!f.connected)o++;else{let n=f.hms_errors?hm(f.hms_errors).length:0;switch(n>0&&c++,f.state){case`RUNNING`:t++,f.remaining_time!=null&&f.remaining_time>0&&(u===null||f.remaining_time0||i++;break;default:a++;break}}}),{counts:{printing:t,paused:r,finished:i,idle:a,offline:o,loading:s,error:c,total:e?.length||0},nextFinish:l&&u?{name:l,remainingMin:u,progress:d}:null}},[e,n,r]);return e?.length?(0,G.jsxs)(`div`,{className:`mt-1 flex flex-wrap items-center gap-4 gap-y-2 text-bambu-gray`,children:[[{count:a.printing,dot:`bg-bambu-green animate-pulse`,label:t(`printers.status.printing`).toLowerCase()},{count:a.paused,dot:`bg-status-warning`,label:t(`printers.status.paused`,`paused`).toLowerCase()},{count:a.finished,dot:`bg-blue-400`,label:t(`printers.status.finished`,`finished`).toLowerCase()},{count:a.idle,dot:a.idle>0?`bg-bambu-green`:`bg-gray-500`,label:t(`printers.status.available`).toLowerCase()},{count:a.error,dot:`bg-status-error`,label:t(`printers.status.problem`).toLowerCase()},{count:a.offline,dot:`bg-gray-400`,label:t(`printers.status.offline`).toLowerCase()}].map(({count:e,dot:t,label:n})=>e>0&&(0,G.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,G.jsx)(`div`,{className:`w-2 h-2 rounded-full ${t}`}),(0,G.jsxs)(`span`,{className:`text-bambu-gray`,children:[(0,G.jsx)(`span`,{className:`text-white font-medium`,children:e}),` `,n]})]},n)),o&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`w-px h-4 bg-bambu-dark-tertiary`}),(0,G.jsxs)(`div`,{className:`flex flex-col gap-1 sm:flex-row sm:items-center sm:gap-2`,children:[(0,G.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,G.jsxs)(`span`,{className:`text-bambu-green font-medium`,children:[t(`printers.nextAvailable`),`:`]}),(0,G.jsx)(`span`,{className:`text-white font-medium`,children:o.name})]}),(0,G.jsxs)(`div`,{className:`flex items-center gap-2 w-full sm:w-auto`,children:[(0,G.jsx)(`div`,{className:`w-full sm:w-16 bg-bambu-dark-tertiary rounded-full h-1.5`,children:(0,G.jsx)(`div`,{className:`bg-bambu-green h-1.5 rounded-full transition-all`,style:{width:`${o.progress}%`}})}),(0,G.jsxs)(`span`,{className:`text-white font-medium`,children:[Math.round(o.progress),`%`]}),(0,G.jsxs)(`span`,{className:`text-bambu-gray`,children:[`(`,Yf(o.remainingMin*60),`)`]})]})]})]})]}):null}function mW({value:e,options:t,onChange:n,fullWidth:r=!1}){let[i,a]=(0,y.useState)(!1),o=t.find(t=>t.value===e)??t[0];return(0,G.jsxs)(`div`,{className:`relative ${r?`w-full min-w-0`:``}`,children:[(0,G.jsxs)(`button`,{type:`button`,onClick:()=>a(e=>!e),className:`h-8 px-2 rounded-lg border bg-bambu-dark border-bambu-dark-tertiary text-white text-sm font-medium transition-colors hover:bg-bambu-dark-tertiary focus:outline-none focus:border-bambu-green flex items-center justify-between gap-2 ${r?`w-full`:`min-w-28`}`,children:[(0,G.jsx)(`span`,{className:`truncate`,children:o?.label}),(0,G.jsx)(ds,{className:`w-4 h-4 text-bambu-gray transition-transform ${i?`rotate-180`:``}`})]}),i&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-10`,onClick:()=>a(!1)}),(0,G.jsx)(`div`,{className:`absolute left-0 top-full z-20 mt-1 min-w-full rounded-lg border border-bambu-dark-tertiary bg-bambu-dark-secondary py-1 shadow-xl`,children:t.map(t=>(0,G.jsx)(`button`,{type:`button`,onClick:()=>{n(t.value),a(!1)},className:`w-full px-3 py-2 text-left text-sm transition-colors hover:bg-bambu-dark-tertiary ${t.value===e?`text-bambu-green`:`text-white`}`,children:t.label},t.value))})]})]})}function hW({label:e,icon:t,children:n}){let[r,i]=(0,y.useState)(!1);return(0,G.jsxs)(`div`,{className:`relative`,children:[(0,G.jsx)(`button`,{type:`button`,onClick:()=>i(e=>!e),className:`h-8 w-8 rounded-lg border bg-bambu-dark border-bambu-dark-tertiary text-white hover:bg-bambu-dark-tertiary transition-colors flex items-center justify-center`,"aria-label":e,title:e,children:t}),r&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-10`,onClick:()=>i(!1)}),(0,G.jsx)(`div`,{className:`absolute right-0 top-full z-20 mt-1 min-w-40 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark-secondary p-2 shadow-xl`,children:n})]})]})}function gW({title:e,options:t=[],unit:n,customMin:r,customMax:i,customStep:a=1,widthClass:o=`w-[240px]`,popoverWidth:s=240,popoverHeight:c=280,isPending:l,onClose:u,onSubmit:d,children:f}){let p=(0,y.useRef)(null),[m,h]=(0,y.useState)(null),[g,_]=(0,y.useState)(``);(0,y.useLayoutEffect)(()=>{let e=p.current?.parentElement;if(!e)return;let t=()=>{h(Qp({triggerRect:e.getBoundingClientRect(),popoverWidth:s,estimatedHeight:c,horizontalAlign:`center`}))};return t(),window.addEventListener(`resize`,t),window.addEventListener(`scroll`,t,!0),()=>{window.removeEventListener(`resize`,t),window.removeEventListener(`scroll`,t,!0)}},[s,c]);let v=n!==void 0,b=()=>{let e=Number(g);Number.isFinite(e)&&d?.(Math.round(Math.min(i??e,Math.max(r??e,e))))};return(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`span`,{ref:p,className:`hidden`,"aria-hidden":`true`}),(0,At.createPortal)((0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`fixed inset-0 z-[1000]`,onClick:u}),(0,G.jsxs)(`div`,{className:`fixed z-[1001] flex ${o} flex-col overflow-hidden rounded-xl border border-bambu-dark-tertiary bg-bambu-dark-secondary shadow-2xl`,style:{top:m?.top??-9999,left:m?.left??-9999,visibility:m?`visible`:`hidden`},onClick:e=>e.stopPropagation(),children:[(0,G.jsx)(`div`,{className:`shrink-0 px-3 py-2.5 text-center text-sm font-medium text-white`,children:e}),(0,G.jsx)(`div`,{className:`shrink-0 h-px bg-bambu-dark-tertiary`}),t.length>0&&(0,G.jsx)(`div`,{className:`px-3 py-2.5`,children:(0,G.jsx)(`div`,{className:`grid grid-cols-2 gap-1.5`,children:t.map(e=>(0,G.jsx)(`button`,{type:`button`,disabled:l,onClick:()=>d?.(e.value),className:`h-8 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:e.label},`${e.label}-${e.value}`))})}),f,v&&(0,G.jsxs)(G.Fragment,{children:[(0,G.jsx)(`div`,{className:`shrink-0 h-px bg-bambu-dark-tertiary`}),(0,G.jsxs)(`form`,{className:`flex gap-1.5 px-3 pt-2.5 pb-3`,onSubmit:e=>{e.preventDefault(),b()},children:[(0,G.jsx)(`input`,{type:`number`,min:r,max:i,step:a,value:g,onChange:e=>_(e.target.value),placeholder:`Custom`,className:`h-8 min-w-0 flex-1 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs text-white placeholder:text-bambu-gray/60 focus:border-bambu-green focus:outline-none`}),(0,G.jsx)(`button`,{type:`submit`,disabled:l||g.trim()===``,className:`h-8 rounded-lg border border-bambu-dark-tertiary bg-bambu-dark px-2 text-xs font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:`Set`})]})]})]})]}),document.body)]})}var WOe=dm(om,`C`);function _W({label:e,current:t,target:n,isActive:r,isPending:i,onSubmit:a,options:o=WOe}){let[s,c]=(0,y.useState)(``),l=()=>{let e=Number(s);Number.isFinite(e)&&a(Math.min(320,Math.max(0,Math.round(e))))};return(0,G.jsxs)(`div`,{className:`rounded-lg border p-2 ${r?`border-amber-500/60 dark:border-amber-400/60 bg-amber-50 dark:bg-amber-400/10`:`border-bambu-dark-tertiary bg-bambu-dark`}`,children:[(0,G.jsxs)(`div`,{className:`mb-1.5 flex items-center justify-between gap-2`,children:[(0,G.jsx)(`span`,{className:`text-xs font-medium ${r?`text-amber-700 dark:text-amber-300`:`text-white`}`,children:e}),(0,G.jsxs)(`span`,{className:`text-[10px] text-bambu-gray`,children:[Math.round(t??0),`°C`,n===void 0?``:` / ${Math.round(n)}°`]})]}),(0,G.jsx)(`div`,{className:`grid grid-cols-2 gap-1`,children:o.map(t=>(0,G.jsx)(`button`,{type:`button`,disabled:i,onClick:()=>a(t.value),className:`h-7 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-1.5 text-[11px] font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:t.label},`${e}-${t.value}`))}),(0,G.jsxs)(`form`,{className:`mt-1.5 flex gap-1`,onSubmit:e=>{e.preventDefault(),l()},children:[(0,G.jsx)(`input`,{type:`number`,min:0,max:320,step:1,value:s,onChange:e=>c(e.target.value),placeholder:`Custom`,className:`h-7 min-w-0 flex-1 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-1.5 text-[11px] text-white placeholder:text-bambu-gray/60 focus:border-bambu-green focus:outline-none`}),(0,G.jsx)(`button`,{type:`submit`,disabled:i||s.trim()===``,className:`h-7 rounded-md border border-bambu-dark-tertiary bg-bambu-dark-secondary px-2 text-[11px] font-medium text-white transition-colors hover:bg-bambu-dark-tertiary disabled:cursor-not-allowed disabled:opacity-50`,children:`Set`})]})]})}var GOe=[`error`,`printing`,`paused`,`finished`,`idle`,`offline`],vW={error:{labelKey:`printers.status.problem`,dot:`bg-status-error`},printing:{labelKey:`printers.status.printing`,dot:`bg-bambu-green animate-pulse`},paused:{labelKey:`printers.status.paused`,dot:`bg-status-warning`},finished:{labelKey:`printers.status.finished`,dot:`bg-blue-400`},idle:{labelKey:`printers.status.idle`,dot:`bg-bambu-green`},offline:{labelKey:`printers.status.offline`,dot:`bg-gray-400`}};function yW(e){if(!e?.connected)return`offline`;if((e.hms_errors?hm(e.hms_errors):[]).length>0)return`error`;switch(e.state){case`RUNNING`:return`printing`;case`PAUSE`:return`paused`;case`FINISH`:return`finished`;case`FAILED`:return`finished`;default:return`idle`}}function KOe(e,t){if(t)return t;switch(e){case`RUNNING`:return`Printing`;case`PAUSE`:return`Paused`;case`FINISH`:return`Finished`;case`FAILED`:return`Failed`;case`IDLE`:return`Idle`;default:return e?e.charAt(0)+e.slice(1).toLowerCase():`Idle`}}var qOe=new Set([`X1C`,`X1`,`X1E`,`X2D`,`P1S`,`P2S`,`H2D`,`H2D Pro`,`H2C`,`H2S`]),JOe=new Set([`P2S`,`X2D`]);function bW(e){return e?{O1D:`H2D`,O1E:`H2D Pro`,O2D:`H2D Pro`,O1C:`H2C`,O1C2:`H2C`,O1S:`H2S`,"BL-P001":`X1C`,"BL-P002":`X1`,"BL-P003":`X1E`,N6:`X2D`,N9:`A2L`,C11:`P1S`,C12:`P1P`,C13:`P2S`,N2S:`A1`,N1:`A1 Mini`,X1C:`X1C`,X1:`X1`,X1E:`X1E`,X2D:`X2D`,P1S:`P1S`,P1P:`P1P`,P2S:`P2S`,A1:`A1`,"A1 Mini":`A1 Mini`,A2L:`A2L`,H2D:`H2D`,"H2D Pro":`H2D Pro`,H2C:`H2C`,H2S:`H2S`}[e]||e:``}function xW({ams:e,printerId:t,label:n,amsLabels:r,canEdit:i,onSaved:a,children:o}){let{t:s}=U(),[c,l]=(0,y.useState)(!1),[u,d]=(0,y.useState)(`top`),[f,p]=(0,y.useState)(``),[m,h]=(0,y.useState)(!1),[g,_]=(0,y.useState)(null),[v,b]=(0,y.useState)(!1),x=(0,y.useRef)(null),S=(0,y.useRef)(null),C=(0,y.useRef)(null);(0,y.useEffect)(()=>{c&&(p(r?.[e.id]??``),_(null),requestAnimationFrame(()=>{if(x.current&&S.current){let e=x.current.getBoundingClientRect(),t=e.top-56,n=window.innerHeight-e.bottom;d(tt?`bottom`:`top`)}}))},[c,r,e.id]);let w=()=>{C.current&&clearTimeout(C.current),C.current=setTimeout(()=>l(!0),80)},T=()=>{C.current&&clearTimeout(C.current),v||(C.current=setTimeout(()=>l(!1),200))};(0,y.useEffect)(()=>()=>{C.current&&clearTimeout(C.current)},[]);let E=async()=>{if(i){h(!0),_(null);try{let n=f.trim();n?await Z.saveAmsLabel(t,e.id,n,e.serial_number):await Z.deleteAmsLabel(t,e.id,e.serial_number),a(),l(!1)}catch(e){_(e instanceof Error?e.message:String(e))}finally{h(!1)}}};return(0,G.jsxs)(`div`,{ref:x,className:`relative inline-block`,onMouseEnter:w,onMouseLeave:T,children:[o,c&&(0,G.jsx)(`div`,{ref:S,className:` absolute left-0 z-50 ${u===`top`?`bottom-full mb-2`:`top-full mt-2`} animate-in fade-in-0 zoom-in-95 duration-150 diff --git a/static/index.html b/static/index.html index 4ce292fce..b7acd03ca 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@ - +