diff --git a/CHANGELOG.md b/CHANGELOG.md
index b36f01955..d21a2601f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@ All notable changes to Bambuddy will be documented in this file.
- **Filament Track Switch (FTS) support — print modal filament dropdown is no longer empty when an X2D / H2D has the FTS accessory installed** ([#1162](https://github.com/maziggy/bambuddy/issues/1162), reported by @mkavalecz) — When the FTS accessory is installed the printer's MQTT changes one nibble of the per-AMS `info` bitmask: bits 8-11 flip from a fixed extruder ID (0x0 / 0x1) to `0xE` ("uninitialized"), because the AMS is no longer wired to a single nozzle — the FTS dynamically routes any slot to either extruder. Bambuddy's MQTT parser already skipped 0xE entries when building `ams_extruder_map` (matching BambuStudio's reading for boot-time transient state), so with the FTS installed the map ended up empty and the print modal's filament dropdown — which filters by `extruderId === nozzle_id` to prevent cross-nozzle assignment ("position of left hotend is abnormal" failures) — filtered out *every* loaded slot. Net effect: empty Filament Mapping dropdown on every dual-nozzle print with the FTS, even when the AMS was fully loaded with the right material. Detection comes from a new MQTT field — `print.device.fila_switch` — which is non-null only when the accessory is installed; it carries the routing topology as two arrays: `in[track] = currently fed slot (-1 = empty)` and `out[track] = extruder this track terminates at`. The fix surfaces this through a new `FilaSwitchState` dataclass on `PrinterState` (`installed`, `in_slots`, `out_extruders`, `stat`, `info`) and the equivalent `FilaSwitchResponse` Pydantic schema on the `GET /printers/{id}/status` route. Frontend (`useFilamentMapping.ts` + `FilamentMapping.tsx`) skips the per-extruder filter when `printerStatus.fila_switch?.installed === true` so any compatible AMS slot can satisfy any nozzle's filament requirement, since the FTS handles the routing. Slots currently fed into a track also get a routing badge in the dropdown — `[L]` or `[R]` — so the user can tell at a glance which slot the FTS is currently routing where (idle slots get no badge: they can be routed to either extruder on demand). The hard "no cross-nozzle assignment" filter on real dual-nozzle printers without the FTS stays untouched (still trips the same way it always has — `fila_switch == null` keeps the existing behaviour). 4 backend tests in `test_bambu_mqtt.py::TestFilamentTrackSwitchDetection` (default-not-installed, detect-from-MQTT-using-the-reporter's-bundle, no-fila_switch-field-stays-not-installed, missing-in-out-arrays-don't-crash) and 2 frontend tests in `useFilamentMapping.test.ts` (FTS-active drops the nozzle filter; explicit `fila_switch: null` keeps the filter applied). Upstream fila_switch payloads with anything other than the documented shape are tolerated — `installed` flips on the *presence* of the field, the routing arrays default to empty lists if missing, and the dropdown skips the badge for slots not currently in `in_slots`.
### Fixed
+- **MakerWorld sidebar entry visible to every user regardless of group permissions** ([#1175](https://github.com/maziggy/bambuddy/issues/1175)) — Backend already enforced `makerworld:view` on every `/makerworld/*` route (`backend/app/api/routes/makerworld.py:145, 157, 242, 406`), the permission was correctly granted to the admin and standard-user role defaults (`permissions.py:298, 364, 454`), and the frontend `Permission` type union already included `'makerworld:view' | 'makerworld:import'` (`client.ts:2498`) — but the sidebar's hand-maintained `navPermissions` map in `Layout.tsx:278` had no entry for `makerworld`, so `isHidden('makerworld')` always returned false and the entry rendered for every authenticated user. Users without the permission saw the entry, clicked, and the page rendered while every API call inside it 403'd. Two-line fix: (1) `Layout.tsx:278` — add `makerworld: 'makerworld:view'` to the map, matching every other sidebar entry's gating shape; (2) `App.tsx:200` — wrap the route in `` for defence in depth, so a user who knows the URL can no longer reach the page directly (matches the existing pattern on `settings`, `groups/new`, `groups/:id/edit` two lines below). 2 new Layout tests pin the contract: with auth enabled and a user lacking `makerworld:view`, the sidebar `` link is absent (other links like `/files` still render); with the permission granted, the link renders.
+
- **Printer Info modal: serial-number and IP-address copy buttons silently did nothing on plain-HTTP LAN deployments** ([#1174](https://github.com/maziggy/bambuddy/issues/1174), reported by @BurntOutHylian) — `PrinterInfoModal`'s `CopyButton` only tried `navigator.clipboard.writeText()`, which is gated by the secure-context requirement (HTTPS or localhost). On the typical Bambuddy deployment shape — bare-IP HTTP on the LAN — `navigator.clipboard` is undefined; the existing `try/catch` swallowed the resulting `TypeError`, the icon never flipped to the tick, and nothing landed on the user's clipboard. Fixed by adding the same off-screen-textarea + `document.execCommand('copy')` fallback that `CameraTokensPage`'s plaintext-token modal already uses for plain-HTTP LAN deployments: gate on `navigator.clipboard && window.isSecureContext`, fall back to the legacy path otherwise, and surface the success-tick only when the copy actually landed (return early without flipping `copied` if `execCommand('copy')` returns false). The `try/finally` around the textarea guarantees DOM cleanup even when the browser throws on a restricted context. 3 new component tests in `PrinterInfoModal.test.tsx` cover (a) secure-context happy path uses `navigator.clipboard.writeText`, (b) plain-HTTP fallback path actually invokes `execCommand('copy')` and leaves no leaked textarea in the DOM, (c) `finally` cleanup removes the textarea even when `execCommand` throws synthetically. Thanks to @BurntOutHylian for the precise file/line pointer in the report.
- **Queue auto-dispatched the next print onto a fouled bed after an aborted or cancelled print** ([#1171](https://github.com/maziggy/bambuddy/issues/1171), reported by @tom5677) — When a print ended with status `aborted` (printer self-abort, or a user stopping the print on the printer's own touchscreen) or `cancelled` (user stopping the print via the Bambuddy queue UI), the plate-clear gate added in [#961](https://github.com/maziggy/bambuddy/issues/961) was *not* raised — only `completed` and `failed` triggered it (`backend/app/main.py:2660`). Result: the queue scheduler dispatched the next pending item ~2 seconds after the abort, with the previous print's material still on the bed. The reporter saw two prints (P1P + P1S) auto-start onto fouled beds within seconds of each other after touchscreen-aborts, and explicitly flagged the risk of damage to the printer; a third printer (his second P1S) behaved correctly because its previous print had ended `completed`. The original code's comment ("user-cancelled prints don't require a plate-clear ack — nothing printed on the bed") only holds if you cancel right at layer 1; cancelling a 12-hour print at hour 11 leaves a fouled bed too. Fix: the gate is now raised for every terminal status — `completed`, `failed`, `aborted`, `cancelled` — matching the safety contract that the user must acknowledge the bed is clear before any next queued print starts. The gate is user-clearable on the Printers page, so worst case for a layer-1 cancel the user clicks "Clear Plate" once. Touchscreen-aborts are particularly important to gate because Bambuddy's "user stopped via UI" override (`_user_stopped_printers` → `aborted` mapped to `cancelled`) only fires when the user stops via the Bambuddy queue; a touchscreen-stop reports `aborted` straight through. Regression coverage in `test_print_lifecycle.py::TestPlateClearGate`: parametrised across all four terminal statuses (asserts `set_awaiting_plate_clear(printer_id, True)` is called for each), plus a defence-in-depth test that an unrecognised future status string never silently raises the gate.
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 853c21d46..a8d2baa13 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -197,7 +197,7 @@ function App() {
} />
} />
} />
- } />
+ } />
} />
} />
} />
diff --git a/frontend/src/__tests__/components/Layout.test.tsx b/frontend/src/__tests__/components/Layout.test.tsx
index 0d8a1c6ff..611e55a86 100644
--- a/frontend/src/__tests__/components/Layout.test.tsx
+++ b/frontend/src/__tests__/components/Layout.test.tsx
@@ -305,4 +305,78 @@ describe('Layout', () => {
});
});
});
+
+ describe('MakerWorld sidebar permission gate (#1175)', () => {
+ // The MakerWorld sidebar entry was visible to every authenticated user
+ // regardless of group permissions because Layout's `navPermissions` map
+ // had no entry for `makerworld`. Backend routes already gated on
+ // `makerworld:view`, so users without the permission saw the entry,
+ // clicked, and got 403'd by every API call inside the page. The fix
+ // adds `makerworld: 'makerworld:view'` to the map so the entry is
+ // hidden when the permission is absent — same shape as every other
+ // sidebar entry.
+ const enableAuthWithUser = (permissions: string[]) => {
+ server.use(
+ http.get('/api/v1/auth/status', () =>
+ HttpResponse.json({ auth_enabled: true, requires_setup: false }),
+ ),
+ http.get('/api/v1/auth/me', () =>
+ HttpResponse.json({
+ id: 1,
+ username: 'tester',
+ role: 'user',
+ is_active: true,
+ is_admin: false,
+ groups: [{ id: 2, name: 'Standard Users' }],
+ permissions,
+ created_at: '2026-01-01T00:00:00Z',
+ }),
+ ),
+ );
+ // AuthProvider needs a token in localStorage to fetch /auth/me; the
+ // value isn't validated by the mocked server.
+ window.localStorage.setItem('auth_token', 'test-token');
+ };
+
+ const findMakerWorldNavLink = () => {
+ // Sidebar nav links use react-router's `to` prop, which renders as a
+ // plain ``. Match on the href so the test isn't
+ // coupled to whatever locale string is rendered.
+ return document.querySelector('aside a[href="/makerworld"]');
+ };
+
+ it('hides the MakerWorld nav entry when the user lacks makerworld:view', async () => {
+ // Standard user without the MakerWorld permission. Every other
+ // permission they hold (library:read, etc.) is irrelevant here — the
+ // gate is per-entry and the MakerWorld entry must not render.
+ enableAuthWithUser(['library:read', 'archives:read', 'queue:read']);
+
+ render();
+
+ await waitFor(() => {
+ // Wait for the auth resolution + sidebar render. Some other nav
+ // entry (Files / Archives) confirms the sidebar finished mounting.
+ const sidebar = document.querySelector('aside');
+ expect(sidebar).toBeInTheDocument();
+ expect(sidebar?.querySelector('a[href="/files"]')).toBeInTheDocument();
+ });
+
+ expect(findMakerWorldNavLink()).toBeNull();
+ });
+
+ it('shows the MakerWorld nav entry when the user has makerworld:view', async () => {
+ enableAuthWithUser([
+ 'library:read',
+ 'archives:read',
+ 'queue:read',
+ 'makerworld:view',
+ ]);
+
+ render();
+
+ await waitFor(() => {
+ expect(findMakerWorldNavLink()).toBeInTheDocument();
+ });
+ });
+ });
});
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index 0086c26c7..8db7d7585 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -284,6 +284,7 @@ export function Layout() {
projects: 'projects:read',
inventory: 'inventory:read',
files: 'library:read',
+ makerworld: 'makerworld:view',
settings: 'settings:read',
notifications: 'notifications:user_email',
};
diff --git a/static/assets/index-BM5VeuBp.js b/static/assets/index-BeGsSdpN.js
similarity index 94%
rename from static/assets/index-BM5VeuBp.js
rename to static/assets/index-BeGsSdpN.js
index c120f5be5..d359f8ed2 100644
--- a/static/assets/index-BM5VeuBp.js
+++ b/static/assets/index-BeGsSdpN.js
@@ -1,12 +1,12 @@
-function cue(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var Z_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function gc(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var kM={exports:{}},Ev={};var X8;function due(){if(X8)return Ev;X8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return Ev.Fragment=e,Ev.jsx=n,Ev.jsxs=n,Ev}var Y8;function uue(){return Y8||(Y8=1,kM.exports=due()),kM.exports}var a=uue(),NM={exports:{}},Wn={};var Q8;function mue(){if(Q8)return Wn;Q8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),u=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),p=Symbol.iterator;function f(K){return K===null||typeof K!="object"?null:(K=p&&K[p]||K["@@iterator"],typeof K=="function"?K:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function g(K,ae,pe){this.props=K,this.context=ae,this.refs=b,this.updater=pe||x}g.prototype.isReactComponent={},g.prototype.setState=function(K,ae){if(typeof K!="object"&&typeof K!="function"&&K!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,K,ae,"setState")},g.prototype.forceUpdate=function(K){this.updater.enqueueForceUpdate(this,K,"forceUpdate")};function S(){}S.prototype=g.prototype;function N(K,ae,pe){this.props=K,this.context=ae,this.refs=b,this.updater=pe||x}var P=N.prototype=new S;P.constructor=N,v(P,g.prototype),P.isPureReactComponent=!0;var C=Array.isArray;function A(){}var T={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function k(K,ae,pe){var be=pe.ref;return{$$typeof:t,type:K,key:ae,ref:be!==void 0?be:null,props:pe}}function E(K,ae){return k(K.type,ae,K.props)}function $(K){return typeof K=="object"&&K!==null&&K.$$typeof===t}function z(K){var ae={"=":"=0",":":"=2"};return"$"+K.replace(/[=:]/g,function(pe){return ae[pe]})}var X=/\/+/g;function R(K,ae){return typeof K=="object"&&K!==null&&K.key!=null?z(""+K.key):ae.toString(36)}function ne(K){switch(K.status){case"fulfilled":return K.value;case"rejected":throw K.reason;default:switch(typeof K.status=="string"?K.then(A,A):(K.status="pending",K.then(function(ae){K.status==="pending"&&(K.status="fulfilled",K.value=ae)},function(ae){K.status==="pending"&&(K.status="rejected",K.reason=ae)})),K.status){case"fulfilled":return K.value;case"rejected":throw K.reason}}throw K}function re(K,ae,pe,be,fe){var B=typeof K;(B==="undefined"||B==="boolean")&&(K=null);var Y=!1;if(K===null)Y=!0;else switch(B){case"bigint":case"string":case"number":Y=!0;break;case"object":switch(K.$$typeof){case t:case e:Y=!0;break;case u:return Y=K._init,re(Y(K._payload),ae,pe,be,fe)}}if(Y)return fe=fe(K),Y=be===""?"."+R(K,0):be,C(fe)?(pe="",Y!=null&&(pe=Y.replace(X,"$&/")+"/"),re(fe,ae,pe,"",function(U){return U})):fe!=null&&($(fe)&&(fe=E(fe,pe+(fe.key==null||K&&K.key===fe.key?"":(""+fe.key).replace(X,"$&/")+"/")+Y)),ae.push(fe)),1;Y=0;var D=be===""?".":be+":";if(C(K))for(var M=0;M>>1,se=re[ie];if(0>>1;iei(pe,ue))bei(fe,pe)?(re[ie]=fe,re[be]=ue,ie=be):(re[ie]=pe,re[ae]=ue,ie=ae);else if(bei(fe,ue))re[ie]=fe,re[be]=ue,ie=be;else break e}}return J}function i(re,J){var ue=re.sortIndex-J.sortIndex;return ue!==0?ue:re.id-J.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],d=[],u=1,m=null,p=3,f=!1,x=!1,v=!1,b=!1,g=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function P(re){for(var J=n(d);J!==null;){if(J.callback===null)r(d);else if(J.startTime<=re)r(d),J.sortIndex=J.expirationTime,e(c,J);else break;J=n(d)}}function C(re){if(v=!1,P(re),!x)if(n(c)!==null)x=!0,A||(A=!0,z());else{var J=n(d);J!==null&&ne(C,J.startTime-re)}}var A=!1,T=-1,F=5,k=-1;function E(){return b?!0:!(t.unstable_now()-kre&&E());){var ie=m.callback;if(typeof ie=="function"){m.callback=null,p=m.priorityLevel;var se=ie(m.expirationTime<=re);if(re=t.unstable_now(),typeof se=="function"){m.callback=se,P(re),J=!0;break t}m===n(c)&&r(c),P(re)}else r(c);m=n(c)}if(m!==null)J=!0;else{var K=n(d);K!==null&&ne(C,K.startTime-re),J=!1}}break e}finally{m=null,p=ue,f=!1}J=void 0}}finally{J?z():A=!1}}}var z;if(typeof N=="function")z=function(){N($)};else if(typeof MessageChannel<"u"){var X=new MessageChannel,R=X.port2;X.port1.onmessage=$,z=function(){R.postMessage(null)}}else z=function(){g($,0)};function ne(re,J){T=g(function(){re(t.unstable_now())},J)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(re){re.callback=null},t.unstable_forceFrameRate=function(re){0>re||125ie?(re.sortIndex=ue,e(d,re),n(c)===null&&re===n(d)&&(v?(S(T),T=-1):v=!0,ne(C,ue-ie))):(re.sortIndex=se,e(c,re),x||f||(x=!0,A||(A=!0,z()))),re},t.unstable_shouldYield=E,t.unstable_wrapCallback=function(re){var J=p;return function(){var ue=p;p=J;try{return re.apply(this,arguments)}finally{p=ue}}}})(TM)),TM}var eH;function fue(){return eH||(eH=1,PM.exports=pue()),PM.exports}var AM={exports:{}},Ds={};var tH;function gue(){if(tH)return Ds;tH=1;var t=Bg();function e(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),AM.exports=gue(),AM.exports}var rH;function bue(){if(rH)return Dv;rH=1;var t=fue(),e=Bg(),n=dQ();function r(h){var y="https://react.dev/errors/"+h;if(1se||(h.current=ie[se],ie[se]=null,se--)}function pe(h,y){se++,ie[se]=h.current,h.current=y}var be=K(null),fe=K(null),B=K(null),Y=K(null);function D(h,y){switch(pe(B,y),pe(fe,h),pe(be,null),y.nodeType){case 9:case 11:h=(h=y.documentElement)&&(h=h.namespaceURI)?x8(h):0;break;default:if(h=y.tagName,y=y.namespaceURI)y=x8(y),h=y8(y,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}ae(be),pe(be,h)}function M(){ae(be),ae(fe),ae(B)}function U(h){h.memoizedState!==null&&pe(Y,h);var y=be.current,_=y8(y,h.type);y!==_&&(pe(fe,h),pe(be,_))}function Q(h){fe.current===h&&(ae(be),ae(fe)),Y.current===h&&(ae(Y),Tv._currentValue=ue)}var O,ce;function I(h){if(O===void 0)try{throw Error()}catch(_){var y=_.stack.trim().match(/\n( *(at )?)/);O=y&&y[1]||"",ce=-1<_.stack.indexOf(`
+function cue(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var Z_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function gc(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var NM={exports:{}},Ev={};var X8;function due(){if(X8)return Ev;X8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return Ev.Fragment=e,Ev.jsx=n,Ev.jsxs=n,Ev}var Y8;function uue(){return Y8||(Y8=1,NM.exports=due()),NM.exports}var a=uue(),CM={exports:{}},Wn={};var Q8;function mue(){if(Q8)return Wn;Q8=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),u=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),p=Symbol.iterator;function f(K){return K===null||typeof K!="object"?null:(K=p&&K[p]||K["@@iterator"],typeof K=="function"?K:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function g(K,ae,pe){this.props=K,this.context=ae,this.refs=b,this.updater=pe||x}g.prototype.isReactComponent={},g.prototype.setState=function(K,ae){if(typeof K!="object"&&typeof K!="function"&&K!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,K,ae,"setState")},g.prototype.forceUpdate=function(K){this.updater.enqueueForceUpdate(this,K,"forceUpdate")};function S(){}S.prototype=g.prototype;function N(K,ae,pe){this.props=K,this.context=ae,this.refs=b,this.updater=pe||x}var P=N.prototype=new S;P.constructor=N,v(P,g.prototype),P.isPureReactComponent=!0;var C=Array.isArray;function A(){}var T={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function k(K,ae,pe){var be=pe.ref;return{$$typeof:t,type:K,key:ae,ref:be!==void 0?be:null,props:pe}}function E(K,ae){return k(K.type,ae,K.props)}function $(K){return typeof K=="object"&&K!==null&&K.$$typeof===t}function z(K){var ae={"=":"=0",":":"=2"};return"$"+K.replace(/[=:]/g,function(pe){return ae[pe]})}var X=/\/+/g;function R(K,ae){return typeof K=="object"&&K!==null&&K.key!=null?z(""+K.key):ae.toString(36)}function ne(K){switch(K.status){case"fulfilled":return K.value;case"rejected":throw K.reason;default:switch(typeof K.status=="string"?K.then(A,A):(K.status="pending",K.then(function(ae){K.status==="pending"&&(K.status="fulfilled",K.value=ae)},function(ae){K.status==="pending"&&(K.status="rejected",K.reason=ae)})),K.status){case"fulfilled":return K.value;case"rejected":throw K.reason}}throw K}function re(K,ae,pe,be,fe){var B=typeof K;(B==="undefined"||B==="boolean")&&(K=null);var Y=!1;if(K===null)Y=!0;else switch(B){case"bigint":case"string":case"number":Y=!0;break;case"object":switch(K.$$typeof){case t:case e:Y=!0;break;case u:return Y=K._init,re(Y(K._payload),ae,pe,be,fe)}}if(Y)return fe=fe(K),Y=be===""?"."+R(K,0):be,C(fe)?(pe="",Y!=null&&(pe=Y.replace(X,"$&/")+"/"),re(fe,ae,pe,"",function(U){return U})):fe!=null&&($(fe)&&(fe=E(fe,pe+(fe.key==null||K&&K.key===fe.key?"":(""+fe.key).replace(X,"$&/")+"/")+Y)),ae.push(fe)),1;Y=0;var D=be===""?".":be+":";if(C(K))for(var M=0;M>>1,se=re[ie];if(0>>1;iei(pe,ue))bei(fe,pe)?(re[ie]=fe,re[be]=ue,ie=be):(re[ie]=pe,re[ae]=ue,ie=ae);else if(bei(fe,ue))re[ie]=fe,re[be]=ue,ie=be;else break e}}return J}function i(re,J){var ue=re.sortIndex-J.sortIndex;return ue!==0?ue:re.id-J.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],d=[],u=1,m=null,p=3,f=!1,x=!1,v=!1,b=!1,g=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function P(re){for(var J=n(d);J!==null;){if(J.callback===null)r(d);else if(J.startTime<=re)r(d),J.sortIndex=J.expirationTime,e(c,J);else break;J=n(d)}}function C(re){if(v=!1,P(re),!x)if(n(c)!==null)x=!0,A||(A=!0,z());else{var J=n(d);J!==null&&ne(C,J.startTime-re)}}var A=!1,T=-1,F=5,k=-1;function E(){return b?!0:!(t.unstable_now()-kre&&E());){var ie=m.callback;if(typeof ie=="function"){m.callback=null,p=m.priorityLevel;var se=ie(m.expirationTime<=re);if(re=t.unstable_now(),typeof se=="function"){m.callback=se,P(re),J=!0;break t}m===n(c)&&r(c),P(re)}else r(c);m=n(c)}if(m!==null)J=!0;else{var K=n(d);K!==null&&ne(C,K.startTime-re),J=!1}}break e}finally{m=null,p=ue,f=!1}J=void 0}}finally{J?z():A=!1}}}var z;if(typeof N=="function")z=function(){N($)};else if(typeof MessageChannel<"u"){var X=new MessageChannel,R=X.port2;X.port1.onmessage=$,z=function(){R.postMessage(null)}}else z=function(){g($,0)};function ne(re,J){T=g(function(){re(t.unstable_now())},J)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(re){re.callback=null},t.unstable_forceFrameRate=function(re){0>re||125ie?(re.sortIndex=ue,e(d,re),n(c)===null&&re===n(d)&&(v?(S(T),T=-1):v=!0,ne(C,ue-ie))):(re.sortIndex=se,e(c,re),x||f||(x=!0,A||(A=!0,z()))),re},t.unstable_shouldYield=E,t.unstable_wrapCallback=function(re){var J=p;return function(){var ue=p;p=J;try{return re.apply(this,arguments)}finally{p=ue}}}})(AM)),AM}var eH;function fue(){return eH||(eH=1,TM.exports=pue()),TM.exports}var jM={exports:{}},Ds={};var tH;function gue(){if(tH)return Ds;tH=1;var t=Bg();function e(c){var d="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),jM.exports=gue(),jM.exports}var rH;function bue(){if(rH)return Dv;rH=1;var t=fue(),e=Bg(),n=dQ();function r(h){var y="https://react.dev/errors/"+h;if(1se||(h.current=ie[se],ie[se]=null,se--)}function pe(h,y){se++,ie[se]=h.current,h.current=y}var be=K(null),fe=K(null),B=K(null),Y=K(null);function D(h,y){switch(pe(B,y),pe(fe,h),pe(be,null),y.nodeType){case 9:case 11:h=(h=y.documentElement)&&(h=h.namespaceURI)?x8(h):0;break;default:if(h=y.tagName,y=y.namespaceURI)y=x8(y),h=y8(y,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}ae(be),pe(be,h)}function M(){ae(be),ae(fe),ae(B)}function U(h){h.memoizedState!==null&&pe(Y,h);var y=be.current,_=y8(y,h.type);y!==_&&(pe(fe,h),pe(be,_))}function Q(h){fe.current===h&&(ae(be),ae(fe)),Y.current===h&&(ae(Y),Tv._currentValue=ue)}var O,ce;function I(h){if(O===void 0)try{throw Error()}catch(_){var y=_.stack.trim().match(/\n( *(at )?)/);O=y&&y[1]||"",ce=-1<_.stack.indexOf(`
at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return`
`+O+h+ce}var G=!1;function q(h,y){if(!h||G)return"";G=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var j={DetermineComponentFrameRoot:function(){try{if(y){var Ut=function(){throw Error()};if(Object.defineProperty(Ut.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Ut,[])}catch(At){var kt=At}Reflect.construct(h,[],Ut)}else{try{Ut.call()}catch(At){kt=At}h.call(Ut.prototype)}}else{try{throw Error()}catch(At){kt=At}(Ut=h())&&typeof Ut.catch=="function"&&Ut.catch(function(){})}}catch(At){if(At&&kt&&typeof At.stack=="string")return[At.stack,kt.stack]}return[null,null]}};j.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var V=Object.getOwnPropertyDescriptor(j.DetermineComponentFrameRoot,"name");V&&V.configurable&&Object.defineProperty(j.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var Z=j.DetermineComponentFrameRoot(),Ne=Z[0],qe=Z[1];if(Ne&&qe){var lt=Ne.split(`
`),vt=qe.split(`
`);for(V=j=0;jV||lt[j]!==vt[V]){var Rt=`
`+lt[j].replace(" at new "," at ");return h.displayName&&Rt.includes("")&&(Rt=Rt.replace("",h.displayName)),Rt}while(1<=j&&0<=V);break}}}finally{G=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?I(_):""}function W(h,y){switch(h.tag){case 26:case 27:case 5:return I(h.type);case 16:return I("Lazy");case 13:return h.child!==y&&y!==null?I("Suspense Fallback"):I("Suspense");case 19:return I("SuspenseList");case 0:case 15:return q(h.type,!1);case 11:return q(h.type.render,!1);case 1:return q(h.type,!0);case 31:return I("Activity");default:return""}}function te(h){try{var y="",_=null;do y+=W(h,_),_=h,h=h.return;while(h);return y}catch(j){return`
Error generating stack: `+j.message+`
-`+j.stack}}var le=Object.prototype.hasOwnProperty,ye=t.unstable_scheduleCallback,he=t.unstable_cancelCallback,me=t.unstable_shouldYield,H=t.unstable_requestPaint,L=t.unstable_now,ee=t.unstable_getCurrentPriorityLevel,ge=t.unstable_ImmediatePriority,ke=t.unstable_UserBlockingPriority,je=t.unstable_NormalPriority,Te=t.unstable_LowPriority,xe=t.unstable_IdlePriority,Fe=t.log,Be=t.unstable_setDisableYieldValue,Ce=null,ze=null;function Me(h){if(typeof Fe=="function"&&Be(h),ze&&typeof ze.setStrictMode=="function")try{ze.setStrictMode(Ce,h)}catch{}}var Oe=Math.clz32?Math.clz32:ve,Ge=Math.log,et=Math.LN2;function ve(h){return h>>>=0,h===0?32:31-(Ge(h)/et|0)|0}var Xe=256,Pe=262144,Ve=4194304;function we(h){var y=h&42;if(y!==0)return y;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function de(h,y,_){var j=h.pendingLanes;if(j===0)return 0;var V=0,Z=h.suspendedLanes,Ne=h.pingedLanes;h=h.warmLanes;var qe=j&134217727;return qe!==0?(j=qe&~Z,j!==0?V=we(j):(Ne&=qe,Ne!==0?V=we(Ne):_||(_=qe&~h,_!==0&&(V=we(_))))):(qe=j&~Z,qe!==0?V=we(qe):Ne!==0?V=we(Ne):_||(_=j&~h,_!==0&&(V=we(_)))),V===0?0:y!==0&&y!==V&&(y&Z)===0&&(Z=V&-V,_=y&-y,Z>=_||Z===32&&(_&4194048)!==0)?y:V}function Se(h,y){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&y)===0}function $e(h,y){switch(h){case 1:case 2:case 4:case 8:case 64:return y+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return y+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function st(){var h=Ve;return Ve<<=1,(Ve&62914560)===0&&(Ve=4194304),h}function He(h){for(var y=[],_=0;31>_;_++)y.push(h);return y}function at(h,y){h.pendingLanes|=y,y!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function ht(h,y,_,j,V,Z){var Ne=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var qe=h.entanglements,lt=h.expirationTimes,vt=h.hiddenUpdates;for(_=Ne&~_;0<_;){var Rt=31-Oe(_),Ut=1<