diff --git a/dist/hornverifier b/dist/hornverifier index b74a94a9..4bf37fae 100755 Binary files a/dist/hornverifier and b/dist/hornverifier differ diff --git a/dist/roverd b/dist/roverd index c93a6d63..6a7537e7 100755 Binary files a/dist/roverd and b/dist/roverd differ diff --git a/dist/servoverifier b/dist/servoverifier index 6a783558..c8b6a830 100755 Binary files a/dist/servoverifier and b/dist/servoverifier differ diff --git a/pi/roverd/auto_charge.go b/pi/roverd/auto_charge.go index dd5c83a8..14549e2d 100644 --- a/pi/roverd/auto_charge.go +++ b/pi/roverd/auto_charge.go @@ -7,7 +7,7 @@ import ( ) const ( - autoChargeTimeout = 5 * time.Second + autoChargeTimeout = 1 * time.Second autoChargeCooldown = 0 * time.Minute sourceHomeBase = 1 << 1 ) diff --git a/plans/switch from auto docking to assisted manual docking.md b/plans/switch from auto docking to assisted manual docking.md new file mode 100644 index 00000000..166dd330 --- /dev/null +++ b/plans/switch from auto docking to assisted manual docking.md @@ -0,0 +1,11 @@ +- roombas are worn out, cant dock themselves anymore using seek dock +- completely redesign the user facing docking process + - get rid of the auto dock instructions + - replace it with a flow for manual docking mode: + - does a little explanation + - line up your round front sensor with the sensor on the dock + - maybe show a video or an image in the HUD to assist + - plays a song on the roomba to indicate that "docking mode" has been entered even though its all client side. + - forces the camera to look all the way down so people can see their round front sensor + - limits movement speed to be very slow to help people be more precise +- MAYBE try some computer vision stuff that runs server-side and guides people? sounds hard and slow and not worth it. \ No newline at end of file diff --git a/plans/to-do.md b/plans/to-do.md index 6df25dd3..0974b0a7 100644 --- a/plans/to-do.md +++ b/plans/to-do.md @@ -6,12 +6,6 @@ 6. enable scrolling on horn frequencies, turn down frequency limit to like 3500 7. make replays more instant, probably make segments shorter 1. fix replay UI so it doesnt save anything in cookie -8. rework drive / dock panel somehow to explain how to dock manually instead of relying on auto docking - 1. maybe have a flag in the rover to choose between auto or manual directions - 2. probably have a short inline video that plays and shows the process - 3. manual docking mode - 1. have camera move down automatically and limit speed during manual docking mode - 2. maybe do some image recodgnition on the server and guide the rover manually?? might be too much # relative pipe dreams: 1. VPS video forwarding diff --git a/server/prompts/overseer_control_system.txt b/server/prompts/overseer_control_system.txt index 99edc282..178d0be6 100644 --- a/server/prompts/overseer_control_system.txt +++ b/server/prompts/overseer_control_system.txt @@ -1,9 +1,10 @@ -You are , an evil, creature-like room intelligence. +You are , a sarcastic dispatcher of room chaos. Stay in character at all times. Identity: -- Sly, mischievous, and a little mean. -- You watch chat and rover activity and push the room toward chaos and novelty. -- You are strategic, not random, and not spammy. +- Tactical, sarcastic, and dry. +- You watch chat and rover activity, then push the room toward chaos and novelty. +- You act first, comment second. +- You are efficient and strategic, not random and not spammy. Rules: - Each cycle is autonomous; do not wait for permission from metadata. diff --git a/server/public/assets/index-BGI3M6ER.js b/server/public/assets/index-BGI3M6ER.js deleted file mode 100644 index 27d15374..00000000 --- a/server/public/assets/index-BGI3M6ER.js +++ /dev/null @@ -1,131 +0,0 @@ -(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))o(u);new MutationObserver(u=>{for(const f of u)if(f.type==="childList")for(const p of f.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&o(p)}).observe(document,{childList:!0,subtree:!0});function r(u){const f={};return u.integrity&&(f.integrity=u.integrity),u.referrerPolicy&&(f.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?f.credentials="include":u.crossOrigin==="anonymous"?f.credentials="omit":f.credentials="same-origin",f}function o(u){if(u.ep)return;u.ep=!0;const f=r(u);fetch(u.href,f)}})();function Ny(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var Vu={exports:{}},Kl={};var p0;function wy(){if(p0)return Kl;p0=1;var n=Symbol.for("react.transitional.element"),a=Symbol.for("react.fragment");function r(o,u,f){var p=null;if(f!==void 0&&(p=""+f),u.key!==void 0&&(p=""+u.key),"key"in u){f={};for(var h in u)h!=="key"&&(f[h]=u[h])}else f=u;return u=f.ref,{$$typeof:n,type:o,key:p,ref:u!==void 0?u:null,props:f}}return Kl.Fragment=a,Kl.jsx=r,Kl.jsxs=r,Kl}var x0;function Ay(){return x0||(x0=1,Vu.exports=wy()),Vu.exports}var l=Ay(),$u={exports:{}},Ee={};var g0;function Cy(){if(g0)return Ee;g0=1;var n=Symbol.for("react.transitional.element"),a=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),u=Symbol.for("react.profiler"),f=Symbol.for("react.consumer"),p=Symbol.for("react.context"),h=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),v=Symbol.for("react.lazy"),b=Symbol.for("react.activity"),S=Symbol.iterator;function E(T){return T===null||typeof T!="object"?null:(T=S&&T[S]||T["@@iterator"],typeof T=="function"?T:null)}var R={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},N=Object.assign,w={};function j(T,D,X){this.props=T,this.context=D,this.refs=w,this.updater=X||R}j.prototype.isReactComponent={},j.prototype.setState=function(T,D){if(typeof T!="object"&&typeof T!="function"&&T!=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,T,D,"setState")},j.prototype.forceUpdate=function(T){this.updater.enqueueForceUpdate(this,T,"forceUpdate")};function M(){}M.prototype=j.prototype;function k(T,D,X){this.props=T,this.context=D,this.refs=w,this.updater=X||R}var C=k.prototype=new M;C.constructor=k,N(C,j.prototype),C.isPureReactComponent=!0;var z=Array.isArray;function V(){}var q={H:null,A:null,T:null,S:null},B=Object.prototype.hasOwnProperty;function F(T,D,X){var se=X.ref;return{$$typeof:n,type:T,key:D,ref:se!==void 0?se:null,props:X}}function H(T,D){return F(T.type,D,T.props)}function $(T){return typeof T=="object"&&T!==null&&T.$$typeof===n}function P(T){var D={"=":"=0",":":"=2"};return"$"+T.replace(/[=:]/g,function(X){return D[X]})}var ue=/\/+/g;function ce(T,D){return typeof T=="object"&&T!==null&&T.key!=null?P(""+T.key):D.toString(36)}function ee(T){switch(T.status){case"fulfilled":return T.value;case"rejected":throw T.reason;default:switch(typeof T.status=="string"?T.then(V,V):(T.status="pending",T.then(function(D){T.status==="pending"&&(T.status="fulfilled",T.value=D)},function(D){T.status==="pending"&&(T.status="rejected",T.reason=D)})),T.status){case"fulfilled":return T.value;case"rejected":throw T.reason}}throw T}function O(T,D,X,se,he){var de=typeof T;(de==="undefined"||de==="boolean")&&(T=null);var me=!1;if(T===null)me=!0;else switch(de){case"bigint":case"string":case"number":me=!0;break;case"object":switch(T.$$typeof){case n:case a:me=!0;break;case v:return me=T._init,O(me(T._payload),D,X,se,he)}}if(me)return he=he(T),me=se===""?"."+ce(T,0):se,z(he)?(X="",me!=null&&(X=me.replace(ue,"$&/")+"/"),O(he,D,X,"",function(xe){return xe})):he!=null&&($(he)&&(he=H(he,X+(he.key==null||T&&T.key===he.key?"":(""+he.key).replace(ue,"$&/")+"/")+me)),D.push(he)),1;me=0;var je=se===""?".":se+":";if(z(T))for(var ye=0;ye>>1,ie=O[J];if(0>>1;Ju(X,Q))seu(he,X)?(O[J]=he,O[se]=Q,J=se):(O[J]=X,O[D]=Q,J=D);else if(seu(he,Q))O[J]=he,O[se]=Q,J=se;else break e}}return K}function u(O,K){var Q=O.sortIndex-K.sortIndex;return Q!==0?Q:O.id-K.id}if(n.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var f=performance;n.unstable_now=function(){return f.now()}}else{var p=Date,h=p.now();n.unstable_now=function(){return p.now()-h}}var g=[],x=[],v=1,b=null,S=3,E=!1,R=!1,N=!1,w=!1,j=typeof setTimeout=="function"?setTimeout:null,M=typeof clearTimeout=="function"?clearTimeout:null,k=typeof setImmediate<"u"?setImmediate:null;function C(O){for(var K=r(x);K!==null;){if(K.callback===null)o(x);else if(K.startTime<=O)o(x),K.sortIndex=K.expirationTime,a(g,K);else break;K=r(x)}}function z(O){if(N=!1,C(O),!R)if(r(g)!==null)R=!0,V||(V=!0,P());else{var K=r(x);K!==null&&ee(z,K.startTime-O)}}var V=!1,q=-1,B=5,F=-1;function H(){return w?!0:!(n.unstable_now()-FO&&H());){var J=b.callback;if(typeof J=="function"){b.callback=null,S=b.priorityLevel;var ie=J(b.expirationTime<=O);if(O=n.unstable_now(),typeof ie=="function"){b.callback=ie,C(O),K=!0;break t}b===r(g)&&o(g),C(O)}else o(g);b=r(g)}if(b!==null)K=!0;else{var T=r(x);T!==null&&ee(z,T.startTime-O),K=!1}}break e}finally{b=null,S=Q,E=!1}K=void 0}}finally{K?P():V=!1}}}var P;if(typeof k=="function")P=function(){k($)};else if(typeof MessageChannel<"u"){var ue=new MessageChannel,ce=ue.port2;ue.port1.onmessage=$,P=function(){ce.postMessage(null)}}else P=function(){j($,0)};function ee(O,K){q=j(function(){O(n.unstable_now())},K)}n.unstable_IdlePriority=5,n.unstable_ImmediatePriority=1,n.unstable_LowPriority=4,n.unstable_NormalPriority=3,n.unstable_Profiling=null,n.unstable_UserBlockingPriority=2,n.unstable_cancelCallback=function(O){O.callback=null},n.unstable_forceFrameRate=function(O){0>O||125J?(O.sortIndex=Q,a(x,O),r(g)===null&&O===r(x)&&(N?(M(q),q=-1):N=!0,ee(z,Q-J))):(O.sortIndex=ie,a(g,O),R||E||(R=!0,V||(V=!0,P()))),O},n.unstable_shouldYield=H,n.unstable_wrapCallback=function(O){var K=S;return function(){var Q=S;S=K;try{return O.apply(this,arguments)}finally{S=Q}}}})(Fu)),Fu}var y0;function Ey(){return y0||(y0=1,Yu.exports=ky()),Yu.exports}var Iu={exports:{}},qt={};var S0;function Ry(){if(S0)return qt;S0=1;var n=Ld();function a(g){var x="https://react.dev/errors/"+g;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(a){console.error(a)}}return n(),Iu.exports=Ry(),Iu.exports}var N0;function Ty(){if(N0)return Zl;N0=1;var n=Ey(),a=Ld(),r=My();function o(e){var t="https://react.dev/errors/"+e;if(1ie||(e.current=J[ie],J[ie]=null,ie--)}function X(e,t){ie++,J[ie]=e.current,e.current=t}var se=T(null),he=T(null),de=T(null),me=T(null);function je(e,t){switch(X(de,t),X(he,e),X(se,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?zp(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=zp(t),e=Up(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}D(se),X(se,e)}function ye(){D(se),D(he),D(de)}function xe(e){e.memoizedState!==null&&X(me,e);var t=se.current,s=Up(t,e.type);t!==s&&(X(he,e),X(se,s))}function De(e){he.current===e&&(D(se),D(he)),me.current===e&&(D(me),Fl._currentValue=Q)}var $e,Te;function Ie(e){if($e===void 0)try{throw Error()}catch(s){var t=s.stack.trim().match(/\n( *(at )?)/);$e=t&&t[1]||"",Te=-1)":-1c||_[i]!==Y[c]){var W=` -`+_[i].replace(" at new "," at ");return e.displayName&&W.includes("")&&(W=W.replace("",e.displayName)),W}while(1<=i&&0<=c);break}}}finally{Ge=!1,Error.prepareStackTrace=s}return(s=e?e.displayName||e.name:"")?Ie(s):""}function re(e,t){switch(e.tag){case 26:case 27:case 5:return Ie(e.type);case 16:return Ie("Lazy");case 13:return e.child!==t&&t!==null?Ie("Suspense Fallback"):Ie("Suspense");case 19:return Ie("SuspenseList");case 0:case 15:return jt(e.type,!1);case 11:return jt(e.type.render,!1);case 1:return jt(e.type,!0);case 31:return Ie("Activity");default:return""}}function ae(e){try{var t="",s=null;do t+=re(e,s),s=e,e=e.return;while(e);return t}catch(i){return` -Error generating stack: `+i.message+` -`+i.stack}}var be=Object.prototype.hasOwnProperty,pe=n.unstable_scheduleCallback,Ne=n.unstable_cancelCallback,ge=n.unstable_shouldYield,et=n.unstable_requestPaint,le=n.unstable_now,fe=n.unstable_getCurrentPriorityLevel,qe=n.unstable_ImmediatePriority,Dt=n.unstable_UserBlockingPriority,Tt=n.unstable_NormalPriority,na=n.unstable_LowPriority,en=n.unstable_IdlePriority,Ae=n.log,nt=n.unstable_setDisableYieldValue,Xe=null,ke=null;function Be(e){if(typeof Ae=="function"&&nt(e),ke&&typeof ke.setStrictMode=="function")try{ke.setStrictMode(Xe,e)}catch{}}var rt=Math.clz32?Math.clz32:bt,Rs=Math.log,sa=Math.LN2;function bt(e){return e>>>=0,e===0?32:31-(Rs(e)/sa|0)|0}var jn=256,Nr=262144,wr=4194304;function Ms(e){var t=e&42;if(t!==0)return t;switch(e&-e){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 e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Ar(e,t,s){var i=e.pendingLanes;if(i===0)return 0;var c=0,d=e.suspendedLanes,y=e.pingedLanes;e=e.warmLanes;var A=i&134217727;return A!==0?(i=A&~d,i!==0?c=Ms(i):(y&=A,y!==0?c=Ms(y):s||(s=A&~e,s!==0&&(c=Ms(s))))):(A=i&~d,A!==0?c=Ms(A):y!==0?c=Ms(y):s||(s=i&~e,s!==0&&(c=Ms(s)))),c===0?0:t!==0&&t!==c&&(t&d)===0&&(d=c&-c,s=t&-t,d>=s||d===32&&(s&4194048)!==0)?t:c}function al(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function uv(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+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 t+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 bf(){var e=wr;return wr<<=1,(wr&62914560)===0&&(wr=4194304),e}function Ro(e){for(var t=[],s=0;31>s;s++)t.push(e);return t}function ll(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function dv(e,t,s,i,c,d){var y=e.pendingLanes;e.pendingLanes=s,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=s,e.entangledLanes&=s,e.errorRecoveryDisabledLanes&=s,e.shellSuspendCounter=0;var A=e.entanglements,_=e.expirationTimes,Y=e.hiddenUpdates;for(s=y&~s;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var gv=/[\n"\\]/g;function nn(e){return e.replace(gv,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Oo(e,t,s,i,c,d,y,A){e.name="",y!=null&&typeof y!="function"&&typeof y!="symbol"&&typeof y!="boolean"?e.type=y:e.removeAttribute("type"),t!=null?y==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+tn(t)):e.value!==""+tn(t)&&(e.value=""+tn(t)):y!=="submit"&&y!=="reset"||e.removeAttribute("value"),t!=null?Bo(e,y,tn(t)):s!=null?Bo(e,y,tn(s)):i!=null&&e.removeAttribute("value"),c==null&&d!=null&&(e.defaultChecked=!!d),c!=null&&(e.checked=c&&typeof c!="function"&&typeof c!="symbol"),A!=null&&typeof A!="function"&&typeof A!="symbol"&&typeof A!="boolean"?e.name=""+tn(A):e.removeAttribute("name")}function qf(e,t,s,i,c,d,y,A){if(d!=null&&typeof d!="function"&&typeof d!="symbol"&&typeof d!="boolean"&&(e.type=d),t!=null||s!=null){if(!(d!=="submit"&&d!=="reset"||t!=null)){_o(e);return}s=s!=null?""+tn(s):"",t=t!=null?""+tn(t):s,A||t===e.value||(e.value=t),e.defaultValue=t}i=i??c,i=typeof i!="function"&&typeof i!="symbol"&&!!i,e.checked=A?e.checked:!!i,e.defaultChecked=!!i,y!=null&&typeof y!="function"&&typeof y!="symbol"&&typeof y!="boolean"&&(e.name=y),_o(e)}function Bo(e,t,s){t==="number"&&Er(e.ownerDocument)===e||e.defaultValue===""+s||(e.defaultValue=""+s)}function ca(e,t,s,i){if(e=e.options,t){t={};for(var c=0;c"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Vo=!1;if(Dn)try{var cl={};Object.defineProperty(cl,"passive",{get:function(){Vo=!0}}),window.addEventListener("test",cl,cl),window.removeEventListener("test",cl,cl)}catch{Vo=!1}var as=null,$o=null,Mr=null;function Uf(){if(Mr)return Mr;var e,t=$o,s=t.length,i,c="value"in as?as.value:as.textContent,d=c.length;for(e=0;e=fl),Ff=" ",If=!1;function Qf(e,t){switch(e){case"keyup":return Fv.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Xf(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var ma=!1;function Qv(e,t){switch(e){case"compositionend":return Xf(t);case"keypress":return t.which!==32?null:(If=!0,Ff);case"textInput":return e=t.data,e===Ff&&If?null:e;default:return null}}function Xv(e,t){if(ma)return e==="compositionend"||!Qo&&Qf(e,t)?(e=Uf(),Mr=$o=as=null,ma=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:s,offset:t-e};e=i}e:{for(;s;){if(s.nextSibling){s=s.nextSibling;break e}s=s.parentNode}s=void 0}s=nm(s)}}function am(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?am(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function lm(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Er(e.document);t instanceof e.HTMLIFrameElement;){try{var s=typeof t.contentWindow.location.href=="string"}catch{s=!1}if(s)e=t.contentWindow;else break;t=Er(e.document)}return t}function Zo(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var nb=Dn&&"documentMode"in document&&11>=document.documentMode,ha=null,Jo=null,xl=null,Po=!1;function rm(e,t,s){var i=s.window===s?s.document:s.nodeType===9?s:s.ownerDocument;Po||ha==null||ha!==Er(i)||(i=ha,"selectionStart"in i&&Zo(i)?i={start:i.selectionStart,end:i.selectionEnd}:(i=(i.ownerDocument&&i.ownerDocument.defaultView||window).getSelection(),i={anchorNode:i.anchorNode,anchorOffset:i.anchorOffset,focusNode:i.focusNode,focusOffset:i.focusOffset}),xl&&pl(xl,i)||(xl=i,i=Ni(Jo,"onSelect"),0>=y,c-=y,Nn=1<<32-rt(t)+c|s<Me?(ze=Se,Se=null):ze=Se.sibling;var Fe=I(U,Se,G[Me],te);if(Fe===null){Se===null&&(Se=ze);break}e&&Se&&Fe.alternate===null&&t(U,Se),L=d(Fe,L,Me),Ye===null?we=Fe:Ye.sibling=Fe,Ye=Fe,Se=ze}if(Me===G.length)return s(U,Se),Ue&&On(U,Me),we;if(Se===null){for(;MeMe?(ze=Se,Se=null):ze=Se.sibling;var Cs=I(U,Se,Fe.value,te);if(Cs===null){Se===null&&(Se=ze);break}e&&Se&&Cs.alternate===null&&t(U,Se),L=d(Cs,L,Me),Ye===null?we=Cs:Ye.sibling=Cs,Ye=Cs,Se=ze}if(Fe.done)return s(U,Se),Ue&&On(U,Me),we;if(Se===null){for(;!Fe.done;Me++,Fe=G.next())Fe=ne(U,Fe.value,te),Fe!==null&&(L=d(Fe,L,Me),Ye===null?we=Fe:Ye.sibling=Fe,Ye=Fe);return Ue&&On(U,Me),we}for(Se=i(Se);!Fe.done;Me++,Fe=G.next())Fe=Z(Se,U,Me,Fe.value,te),Fe!==null&&(e&&Fe.alternate!==null&&Se.delete(Fe.key===null?Me:Fe.key),L=d(Fe,L,Me),Ye===null?we=Fe:Ye.sibling=Fe,Ye=Fe);return e&&Se.forEach(function(jy){return t(U,jy)}),Ue&&On(U,Me),we}function We(U,L,G,te){if(typeof G=="object"&&G!==null&&G.type===N&&G.key===null&&(G=G.props.children),typeof G=="object"&&G!==null){switch(G.$$typeof){case E:e:{for(var we=G.key;L!==null;){if(L.key===we){if(we=G.type,we===N){if(L.tag===7){s(U,L.sibling),te=c(L,G.props.children),te.return=U,U=te;break e}}else if(L.elementType===we||typeof we=="object"&&we!==null&&we.$$typeof===B&&Vs(we)===L.type){s(U,L.sibling),te=c(L,G.props),jl(te,G),te.return=U,U=te;break e}s(U,L);break}else t(U,L);L=L.sibling}G.type===N?(te=Bs(G.props.children,U.mode,te,G.key),te.return=U,U=te):(te=Hr(G.type,G.key,G.props,null,U.mode,te),jl(te,G),te.return=U,U=te)}return y(U);case R:e:{for(we=G.key;L!==null;){if(L.key===we)if(L.tag===4&&L.stateNode.containerInfo===G.containerInfo&&L.stateNode.implementation===G.implementation){s(U,L.sibling),te=c(L,G.children||[]),te.return=U,U=te;break e}else{s(U,L);break}else t(U,L);L=L.sibling}te=lc(G,U.mode,te),te.return=U,U=te}return y(U);case B:return G=Vs(G),We(U,L,G,te)}if(ee(G))return ve(U,L,G,te);if(P(G)){if(we=P(G),typeof we!="function")throw Error(o(150));return G=we.call(G),Ce(U,L,G,te)}if(typeof G.then=="function")return We(U,L,Qr(G),te);if(G.$$typeof===k)return We(U,L,Gr(U,G),te);Xr(U,G)}return typeof G=="string"&&G!==""||typeof G=="number"||typeof G=="bigint"?(G=""+G,L!==null&&L.tag===6?(s(U,L.sibling),te=c(L,G),te.return=U,U=te):(s(U,L),te=ac(G,U.mode,te),te.return=U,U=te),y(U)):s(U,L)}return function(U,L,G,te){try{Sl=0;var we=We(U,L,G,te);return Aa=null,we}catch(Se){if(Se===wa||Se===Fr)throw Se;var Ye=Ft(29,Se,null,U.mode);return Ye.lanes=te,Ye.return=U,Ye}finally{}}}var Gs=Rm(!0),Mm=Rm(!1),cs=!1;function gc(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function vc(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function us(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function ds(e,t,s){var i=e.updateQueue;if(i===null)return null;if(i=i.shared,(Qe&2)!==0){var c=i.pending;return c===null?t.next=t:(t.next=c.next,c.next=t),i.pending=t,t=Ur(e),mm(e,null,s),t}return zr(e,i,t,s),Ur(e)}function Nl(e,t,s){if(t=t.updateQueue,t!==null&&(t=t.shared,(s&4194048)!==0)){var i=t.lanes;i&=e.pendingLanes,s|=i,t.lanes=s,Sf(e,s)}}function bc(e,t){var s=e.updateQueue,i=e.alternate;if(i!==null&&(i=i.updateQueue,s===i)){var c=null,d=null;if(s=s.firstBaseUpdate,s!==null){do{var y={lane:s.lane,tag:s.tag,payload:s.payload,callback:null,next:null};d===null?c=d=y:d=d.next=y,s=s.next}while(s!==null);d===null?c=d=t:d=d.next=t}else c=d=t;s={baseState:i.baseState,firstBaseUpdate:c,lastBaseUpdate:d,shared:i.shared,callbacks:i.callbacks},e.updateQueue=s;return}e=s.lastBaseUpdate,e===null?s.firstBaseUpdate=t:e.next=t,s.lastBaseUpdate=t}var yc=!1;function wl(){if(yc){var e=Na;if(e!==null)throw e}}function Al(e,t,s,i){yc=!1;var c=e.updateQueue;cs=!1;var d=c.firstBaseUpdate,y=c.lastBaseUpdate,A=c.shared.pending;if(A!==null){c.shared.pending=null;var _=A,Y=_.next;_.next=null,y===null?d=Y:y.next=Y,y=_;var W=e.alternate;W!==null&&(W=W.updateQueue,A=W.lastBaseUpdate,A!==y&&(A===null?W.firstBaseUpdate=Y:A.next=Y,W.lastBaseUpdate=_))}if(d!==null){var ne=c.baseState;y=0,W=Y=_=null,A=d;do{var I=A.lane&-536870913,Z=I!==A.lane;if(Z?(Le&I)===I:(i&I)===I){I!==0&&I===ja&&(yc=!0),W!==null&&(W=W.next={lane:0,tag:A.tag,payload:A.payload,callback:null,next:null});e:{var ve=e,Ce=A;I=t;var We=s;switch(Ce.tag){case 1:if(ve=Ce.payload,typeof ve=="function"){ne=ve.call(We,ne,I);break e}ne=ve;break e;case 3:ve.flags=ve.flags&-65537|128;case 0:if(ve=Ce.payload,I=typeof ve=="function"?ve.call(We,ne,I):ve,I==null)break e;ne=b({},ne,I);break e;case 2:cs=!0}}I=A.callback,I!==null&&(e.flags|=64,Z&&(e.flags|=8192),Z=c.callbacks,Z===null?c.callbacks=[I]:Z.push(I))}else Z={lane:I,tag:A.tag,payload:A.payload,callback:A.callback,next:null},W===null?(Y=W=Z,_=ne):W=W.next=Z,y|=I;if(A=A.next,A===null){if(A=c.shared.pending,A===null)break;Z=A,A=Z.next,Z.next=null,c.lastBaseUpdate=Z,c.shared.pending=null}}while(!0);W===null&&(_=ne),c.baseState=_,c.firstBaseUpdate=Y,c.lastBaseUpdate=W,d===null&&(c.shared.lanes=0),xs|=y,e.lanes=y,e.memoizedState=ne}}function Tm(e,t){if(typeof e!="function")throw Error(o(191,e));e.call(t)}function qm(e,t){var s=e.callbacks;if(s!==null)for(e.callbacks=null,e=0;ed?d:8;var y=O.T,A={};O.T=A,Uc(e,!1,t,s);try{var _=c(),Y=O.S;if(Y!==null&&Y(A,_),_!==null&&typeof _=="object"&&typeof _.then=="function"){var W=db(_,i);El(e,t,W,Zt(e))}else El(e,t,i,Zt(e))}catch(ne){El(e,t,{then:function(){},status:"rejected",reason:ne},Zt())}finally{K.p=d,y!==null&&A.types!==null&&(y.types=A.types),O.T=y}}function gb(){}function Lc(e,t,s,i){if(e.tag!==5)throw Error(o(476));var c=uh(e).queue;ch(e,c,t,Q,s===null?gb:function(){return dh(e),s(i)})}function uh(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:Q,baseState:Q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Un,lastRenderedState:Q},next:null};var s={};return t.next={memoizedState:s,baseState:s,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Un,lastRenderedState:s},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function dh(e){var t=uh(e);t.next===null&&(t=e.alternate.memoizedState),El(e,t.next.queue,{},Zt())}function zc(){return kt(Fl)}function fh(){return mt().memoizedState}function mh(){return mt().memoizedState}function vb(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var s=Zt();e=us(s);var i=ds(t,e,s);i!==null&&($t(i,t,s),Nl(i,t,s)),t={cache:mc()},e.payload=t;return}t=t.return}}function bb(e,t,s){var i=Zt();s={lane:i,revertLane:0,gesture:null,action:s,hasEagerState:!1,eagerState:null,next:null},ai(e)?ph(t,s):(s=nc(e,t,s,i),s!==null&&($t(s,e,i),xh(s,t,i)))}function hh(e,t,s){var i=Zt();El(e,t,s,i)}function El(e,t,s,i){var c={lane:i,revertLane:0,gesture:null,action:s,hasEagerState:!1,eagerState:null,next:null};if(ai(e))ph(t,c);else{var d=e.alternate;if(e.lanes===0&&(d===null||d.lanes===0)&&(d=t.lastRenderedReducer,d!==null))try{var y=t.lastRenderedState,A=d(y,s);if(c.hasEagerState=!0,c.eagerState=A,Yt(A,y))return zr(e,t,c,0),tt===null&&Lr(),!1}catch{}finally{}if(s=nc(e,t,c,i),s!==null)return $t(s,e,i),xh(s,t,i),!0}return!1}function Uc(e,t,s,i){if(i={lane:2,revertLane:gu(),gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},ai(e)){if(t)throw Error(o(479))}else t=nc(e,s,i,2),t!==null&&$t(t,e,2)}function ai(e){var t=e.alternate;return e===Re||t!==null&&t===Re}function ph(e,t){ka=Jr=!0;var s=e.pending;s===null?t.next=t:(t.next=s.next,s.next=t),e.pending=t}function xh(e,t,s){if((s&4194048)!==0){var i=t.lanes;i&=e.pendingLanes,s|=i,t.lanes=s,Sf(e,s)}}var Rl={readContext:kt,use:ei,useCallback:ct,useContext:ct,useEffect:ct,useImperativeHandle:ct,useLayoutEffect:ct,useInsertionEffect:ct,useMemo:ct,useReducer:ct,useRef:ct,useState:ct,useDebugValue:ct,useDeferredValue:ct,useTransition:ct,useSyncExternalStore:ct,useId:ct,useHostTransitionStatus:ct,useFormState:ct,useActionState:ct,useOptimistic:ct,useMemoCache:ct,useCacheRefresh:ct};Rl.useEffectEvent=ct;var gh={readContext:kt,use:ei,useCallback:function(e,t){return _t().memoizedState=[e,t===void 0?null:t],e},useContext:kt,useEffect:eh,useImperativeHandle:function(e,t,s){s=s!=null?s.concat([e]):null,ni(4194308,4,ah.bind(null,t,e),s)},useLayoutEffect:function(e,t){return ni(4194308,4,e,t)},useInsertionEffect:function(e,t){ni(4,2,e,t)},useMemo:function(e,t){var s=_t();t=t===void 0?null:t;var i=e();if(Ys){Be(!0);try{e()}finally{Be(!1)}}return s.memoizedState=[i,t],i},useReducer:function(e,t,s){var i=_t();if(s!==void 0){var c=s(t);if(Ys){Be(!0);try{s(t)}finally{Be(!1)}}}else c=t;return i.memoizedState=i.baseState=c,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:c},i.queue=e,e=e.dispatch=bb.bind(null,Re,e),[i.memoizedState,e]},useRef:function(e){var t=_t();return e={current:e},t.memoizedState=e},useState:function(e){e=qc(e);var t=e.queue,s=hh.bind(null,Re,t);return t.dispatch=s,[e.memoizedState,s]},useDebugValue:Oc,useDeferredValue:function(e,t){var s=_t();return Bc(s,e,t)},useTransition:function(){var e=qc(!1);return e=ch.bind(null,Re,e.queue,!0,!1),_t().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,s){var i=Re,c=_t();if(Ue){if(s===void 0)throw Error(o(407));s=s()}else{if(s=t(),tt===null)throw Error(o(349));(Le&127)!==0||zm(i,t,s)}c.memoizedState=s;var d={value:s,getSnapshot:t};return c.queue=d,eh(Hm.bind(null,i,d,e),[e]),i.flags|=2048,Ra(9,{destroy:void 0},Um.bind(null,i,d,s,t),null),s},useId:function(){var e=_t(),t=tt.identifierPrefix;if(Ue){var s=wn,i=Nn;s=(i&~(1<<32-rt(i)-1)).toString(32)+s,t="_"+t+"R_"+s,s=Pr++,0<\/script>",d=d.removeChild(d.firstChild);break;case"select":d=typeof i.is=="string"?y.createElement("select",{is:i.is}):y.createElement("select"),i.multiple?d.multiple=!0:i.size&&(d.size=i.size);break;default:d=typeof i.is=="string"?y.createElement(c,{is:i.is}):y.createElement(c)}}d[At]=t,d[Bt]=i;e:for(y=t.child;y!==null;){if(y.tag===5||y.tag===6)d.appendChild(y.stateNode);else if(y.tag!==4&&y.tag!==27&&y.child!==null){y.child.return=y,y=y.child;continue}if(y===t)break e;for(;y.sibling===null;){if(y.return===null||y.return===t)break e;y=y.return}y.sibling.return=y.return,y=y.sibling}t.stateNode=d;e:switch(Rt(d,c,i),c){case"button":case"input":case"select":case"textarea":i=!!i.autoFocus;break e;case"img":i=!0;break e;default:i=!1}i&&Vn(t)}}return at(t),Wc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,s),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==i&&Vn(t);else{if(typeof i!="string"&&t.stateNode===null)throw Error(o(166));if(e=de.current,ya(t)){if(e=t.stateNode,s=t.memoizedProps,i=null,c=Ct,c!==null)switch(c.tag){case 27:case 5:i=c.memoizedProps}e[At]=t,e=!!(e.nodeValue===s||i!==null&&i.suppressHydrationWarning===!0||Bp(e.nodeValue,s)),e||is(t,!0)}else e=wi(e).createTextNode(i),e[At]=t,t.stateNode=e}return at(t),null;case 31:if(s=t.memoizedState,e===null||e.memoizedState!==null){if(i=ya(t),s!==null){if(e===null){if(!i)throw Error(o(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(o(557));e[At]=t}else Ls(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;at(t),e=!1}else s=cc(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=s),e=!0;if(!e)return t.flags&256?(Qt(t),t):(Qt(t),null);if((t.flags&128)!==0)throw Error(o(558))}return at(t),null;case 13:if(i=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(c=ya(t),i!==null&&i.dehydrated!==null){if(e===null){if(!c)throw Error(o(318));if(c=t.memoizedState,c=c!==null?c.dehydrated:null,!c)throw Error(o(317));c[At]=t}else Ls(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;at(t),c=!1}else c=cc(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=c),c=!0;if(!c)return t.flags&256?(Qt(t),t):(Qt(t),null)}return Qt(t),(t.flags&128)!==0?(t.lanes=s,t):(s=i!==null,e=e!==null&&e.memoizedState!==null,s&&(i=t.child,c=null,i.alternate!==null&&i.alternate.memoizedState!==null&&i.alternate.memoizedState.cachePool!==null&&(c=i.alternate.memoizedState.cachePool.pool),d=null,i.memoizedState!==null&&i.memoizedState.cachePool!==null&&(d=i.memoizedState.cachePool.pool),d!==c&&(i.flags|=2048)),s!==e&&s&&(t.child.flags|=8192),ci(t,t.updateQueue),at(t),null);case 4:return ye(),e===null&&Su(t.stateNode.containerInfo),at(t),null;case 10:return Ln(t.type),at(t),null;case 19:if(D(ft),i=t.memoizedState,i===null)return at(t),null;if(c=(t.flags&128)!==0,d=i.rendering,d===null)if(c)Tl(i,!1);else{if(ut!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(d=Zr(e),d!==null){for(t.flags|=128,Tl(i,!1),e=d.updateQueue,t.updateQueue=e,ci(t,e),t.subtreeFlags=0,e=s,s=t.child;s!==null;)hm(s,e),s=s.sibling;return X(ft,ft.current&1|2),Ue&&On(t,i.treeForkCount),t.child}e=e.sibling}i.tail!==null&&le()>hi&&(t.flags|=128,c=!0,Tl(i,!1),t.lanes=4194304)}else{if(!c)if(e=Zr(d),e!==null){if(t.flags|=128,c=!0,e=e.updateQueue,t.updateQueue=e,ci(t,e),Tl(i,!0),i.tail===null&&i.tailMode==="hidden"&&!d.alternate&&!Ue)return at(t),null}else 2*le()-i.renderingStartTime>hi&&s!==536870912&&(t.flags|=128,c=!0,Tl(i,!1),t.lanes=4194304);i.isBackwards?(d.sibling=t.child,t.child=d):(e=i.last,e!==null?e.sibling=d:t.child=d,i.last=d)}return i.tail!==null?(e=i.tail,i.rendering=e,i.tail=e.sibling,i.renderingStartTime=le(),e.sibling=null,s=ft.current,X(ft,c?s&1|2:s&1),Ue&&On(t,i.treeForkCount),e):(at(t),null);case 22:case 23:return Qt(t),jc(),i=t.memoizedState!==null,e!==null?e.memoizedState!==null!==i&&(t.flags|=8192):i&&(t.flags|=8192),i?(s&536870912)!==0&&(t.flags&128)===0&&(at(t),t.subtreeFlags&6&&(t.flags|=8192)):at(t),s=t.updateQueue,s!==null&&ci(t,s.retryQueue),s=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(s=e.memoizedState.cachePool.pool),i=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(i=t.memoizedState.cachePool.pool),i!==s&&(t.flags|=2048),e!==null&&D(Hs),null;case 24:return s=null,e!==null&&(s=e.memoizedState.cache),t.memoizedState.cache!==s&&(t.flags|=2048),Ln(ht),at(t),null;case 25:return null;case 30:return null}throw Error(o(156,t.tag))}function wb(e,t){switch(ic(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Ln(ht),ye(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return De(t),null;case 31:if(t.memoizedState!==null){if(Qt(t),t.alternate===null)throw Error(o(340));Ls()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(Qt(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(o(340));Ls()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return D(ft),null;case 4:return ye(),null;case 10:return Ln(t.type),null;case 22:case 23:return Qt(t),jc(),e!==null&&D(Hs),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Ln(ht),null;case 25:return null;default:return null}}function Vh(e,t){switch(ic(t),t.tag){case 3:Ln(ht),ye();break;case 26:case 27:case 5:De(t);break;case 4:ye();break;case 31:t.memoizedState!==null&&Qt(t);break;case 13:Qt(t);break;case 19:D(ft);break;case 10:Ln(t.type);break;case 22:case 23:Qt(t),jc(),e!==null&&D(Hs);break;case 24:Ln(ht)}}function ql(e,t){try{var s=t.updateQueue,i=s!==null?s.lastEffect:null;if(i!==null){var c=i.next;s=c;do{if((s.tag&e)===e){i=void 0;var d=s.create,y=s.inst;i=d(),y.destroy=i}s=s.next}while(s!==c)}}catch(A){Ze(t,t.return,A)}}function hs(e,t,s){try{var i=t.updateQueue,c=i!==null?i.lastEffect:null;if(c!==null){var d=c.next;i=d;do{if((i.tag&e)===e){var y=i.inst,A=y.destroy;if(A!==void 0){y.destroy=void 0,c=t;var _=s,Y=A;try{Y()}catch(W){Ze(c,_,W)}}}i=i.next}while(i!==d)}}catch(W){Ze(t,t.return,W)}}function $h(e){var t=e.updateQueue;if(t!==null){var s=e.stateNode;try{qm(t,s)}catch(i){Ze(e,e.return,i)}}}function Gh(e,t,s){s.props=Fs(e.type,e.memoizedProps),s.state=e.memoizedState;try{s.componentWillUnmount()}catch(i){Ze(e,t,i)}}function Dl(e,t){try{var s=e.ref;if(s!==null){switch(e.tag){case 26:case 27:case 5:var i=e.stateNode;break;case 30:i=e.stateNode;break;default:i=e.stateNode}typeof s=="function"?e.refCleanup=s(i):s.current=i}}catch(c){Ze(e,t,c)}}function An(e,t){var s=e.ref,i=e.refCleanup;if(s!==null)if(typeof i=="function")try{i()}catch(c){Ze(e,t,c)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof s=="function")try{s(null)}catch(c){Ze(e,t,c)}else s.current=null}function Yh(e){var t=e.type,s=e.memoizedProps,i=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":s.autoFocus&&i.focus();break e;case"img":s.src?i.src=s.src:s.srcSet&&(i.srcset=s.srcSet)}}catch(c){Ze(e,e.return,c)}}function eu(e,t,s){try{var i=e.stateNode;Ib(i,e.type,s,t),i[Bt]=t}catch(c){Ze(e,e.return,c)}}function Fh(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&Ss(e.type)||e.tag===4}function tu(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Fh(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&Ss(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function nu(e,t,s){var i=e.tag;if(i===5||i===6)e=e.stateNode,t?(s.nodeType===9?s.body:s.nodeName==="HTML"?s.ownerDocument.body:s).insertBefore(e,t):(t=s.nodeType===9?s.body:s.nodeName==="HTML"?s.ownerDocument.body:s,t.appendChild(e),s=s._reactRootContainer,s!=null||t.onclick!==null||(t.onclick=qn));else if(i!==4&&(i===27&&Ss(e.type)&&(s=e.stateNode,t=null),e=e.child,e!==null))for(nu(e,t,s),e=e.sibling;e!==null;)nu(e,t,s),e=e.sibling}function ui(e,t,s){var i=e.tag;if(i===5||i===6)e=e.stateNode,t?s.insertBefore(e,t):s.appendChild(e);else if(i!==4&&(i===27&&Ss(e.type)&&(s=e.stateNode),e=e.child,e!==null))for(ui(e,t,s),e=e.sibling;e!==null;)ui(e,t,s),e=e.sibling}function Ih(e){var t=e.stateNode,s=e.memoizedProps;try{for(var i=e.type,c=t.attributes;c.length;)t.removeAttributeNode(c[0]);Rt(t,i,s),t[At]=e,t[Bt]=s}catch(d){Ze(e,e.return,d)}}var $n=!1,gt=!1,su=!1,Qh=typeof WeakSet=="function"?WeakSet:Set,wt=null;function Ab(e,t){if(e=e.containerInfo,wu=Ti,e=lm(e),Zo(e)){if("selectionStart"in e)var s={start:e.selectionStart,end:e.selectionEnd};else e:{s=(s=e.ownerDocument)&&s.defaultView||window;var i=s.getSelection&&s.getSelection();if(i&&i.rangeCount!==0){s=i.anchorNode;var c=i.anchorOffset,d=i.focusNode;i=i.focusOffset;try{s.nodeType,d.nodeType}catch{s=null;break e}var y=0,A=-1,_=-1,Y=0,W=0,ne=e,I=null;t:for(;;){for(var Z;ne!==s||c!==0&&ne.nodeType!==3||(A=y+c),ne!==d||i!==0&&ne.nodeType!==3||(_=y+i),ne.nodeType===3&&(y+=ne.nodeValue.length),(Z=ne.firstChild)!==null;)I=ne,ne=Z;for(;;){if(ne===e)break t;if(I===s&&++Y===c&&(A=y),I===d&&++W===i&&(_=y),(Z=ne.nextSibling)!==null)break;ne=I,I=ne.parentNode}ne=Z}s=A===-1||_===-1?null:{start:A,end:_}}else s=null}s=s||{start:0,end:0}}else s=null;for(Au={focusedElem:e,selectionRange:s},Ti=!1,wt=t;wt!==null;)if(t=wt,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,wt=e;else for(;wt!==null;){switch(t=wt,d=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(s=0;s title"))),Rt(d,i,s),d[At]=e,Nt(d),i=d;break e;case"link":var y=Wp("link","href",c).get(i+(s.href||""));if(y){for(var A=0;AWe&&(y=We,We=Ce,Ce=y);var U=sm(A,Ce),L=sm(A,We);if(U&&L&&(Z.rangeCount!==1||Z.anchorNode!==U.node||Z.anchorOffset!==U.offset||Z.focusNode!==L.node||Z.focusOffset!==L.offset)){var G=ne.createRange();G.setStart(U.node,U.offset),Z.removeAllRanges(),Ce>We?(Z.addRange(G),Z.extend(L.node,L.offset)):(G.setEnd(L.node,L.offset),Z.addRange(G))}}}}for(ne=[],Z=A;Z=Z.parentNode;)Z.nodeType===1&&ne.push({element:Z,left:Z.scrollLeft,top:Z.scrollTop});for(typeof A.focus=="function"&&A.focus(),A=0;As?32:s,O.T=null,s=uu,uu=null;var d=vs,y=Qn;if(yt=0,_a=vs=null,Qn=0,(Qe&6)!==0)throw Error(o(331));var A=Qe;if(Qe|=4,ap(d.current),tp(d,d.current,y,s),Qe=A,Ul(0,!1),ke&&typeof ke.onPostCommitFiberRoot=="function")try{ke.onPostCommitFiberRoot(Xe,d)}catch{}return!0}finally{K.p=c,O.T=i,jp(e,t)}}function wp(e,t,s){t=an(s,t),t=Gc(e.stateNode,t,2),e=ds(e,t,2),e!==null&&(ll(e,2),Cn(e))}function Ze(e,t,s){if(e.tag===3)wp(e,e,s);else for(;t!==null;){if(t.tag===3){wp(t,e,s);break}else if(t.tag===1){var i=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof i.componentDidCatch=="function"&&(gs===null||!gs.has(i))){e=an(s,e),s=Ah(2),i=ds(t,s,2),i!==null&&(Ch(s,i,t,e),ll(i,2),Cn(i));break}}t=t.return}}function hu(e,t,s){var i=e.pingCache;if(i===null){i=e.pingCache=new Eb;var c=new Set;i.set(t,c)}else c=i.get(t),c===void 0&&(c=new Set,i.set(t,c));c.has(s)||(ru=!0,c.add(s),e=Db.bind(null,e,t,s),t.then(e,e))}function Db(e,t,s){var i=e.pingCache;i!==null&&i.delete(t),e.pingedLanes|=e.suspendedLanes&s,e.warmLanes&=~s,tt===e&&(Le&s)===s&&(ut===4||ut===3&&(Le&62914560)===Le&&300>le()-mi?(Qe&2)===0&&Oa(e,0):iu|=s,Da===Le&&(Da=0)),Cn(e)}function Ap(e,t){t===0&&(t=bf()),e=Os(e,t),e!==null&&(ll(e,t),Cn(e))}function _b(e){var t=e.memoizedState,s=0;t!==null&&(s=t.retryLane),Ap(e,s)}function Ob(e,t){var s=0;switch(e.tag){case 31:case 13:var i=e.stateNode,c=e.memoizedState;c!==null&&(s=c.retryLane);break;case 19:i=e.stateNode;break;case 22:i=e.stateNode._retryCache;break;default:throw Error(o(314))}i!==null&&i.delete(t),Ap(e,s)}function Bb(e,t){return pe(e,t)}var yi=null,La=null,pu=!1,Si=!1,xu=!1,ys=0;function Cn(e){e!==La&&e.next===null&&(La===null?yi=La=e:La=La.next=e),Si=!0,pu||(pu=!0,zb())}function Ul(e,t){if(!xu&&Si){xu=!0;do for(var s=!1,i=yi;i!==null;){if(e!==0){var c=i.pendingLanes;if(c===0)var d=0;else{var y=i.suspendedLanes,A=i.pingedLanes;d=(1<<31-rt(42|e)+1)-1,d&=c&~(y&~A),d=d&201326741?d&201326741|1:d?d|2:0}d!==0&&(s=!0,Rp(i,d))}else d=Le,d=Ar(i,i===tt?d:0,i.cancelPendingCommit!==null||i.timeoutHandle!==-1),(d&3)===0||al(i,d)||(s=!0,Rp(i,d));i=i.next}while(s);xu=!1}}function Lb(){Cp()}function Cp(){Si=pu=!1;var e=0;ys!==0&&Xb()&&(e=ys);for(var t=le(),s=null,i=yi;i!==null;){var c=i.next,d=kp(i,t);d===0?(i.next=null,s===null?yi=c:s.next=c,c===null&&(La=s)):(s=i,(e!==0||(d&3)!==0)&&(Si=!0)),i=c}yt!==0&&yt!==5||Ul(e),ys!==0&&(ys=0)}function kp(e,t){for(var s=e.suspendedLanes,i=e.pingedLanes,c=e.expirationTimes,d=e.pendingLanes&-62914561;0A)break;var W=_.transferSize,ne=_.initiatorType;W&&Lp(ne)&&(_=_.responseEnd,y+=W*(_"u"?null:document;function Kp(e,t,s){var i=za;if(i&&typeof t=="string"&&t){var c=nn(t);c='link[rel="'+e+'"][href="'+c+'"]',typeof s=="string"&&(c+='[crossorigin="'+s+'"]'),Xp.has(c)||(Xp.add(c),e={rel:e,crossOrigin:s,href:t},i.querySelector(c)===null&&(t=i.createElement("link"),Rt(t,"link",e),Nt(t),i.head.appendChild(t)))}}function sy(e){Xn.D(e),Kp("dns-prefetch",e,null)}function ay(e,t){Xn.C(e,t),Kp("preconnect",e,t)}function ly(e,t,s){Xn.L(e,t,s);var i=za;if(i&&e&&t){var c='link[rel="preload"][as="'+nn(t)+'"]';t==="image"&&s&&s.imageSrcSet?(c+='[imagesrcset="'+nn(s.imageSrcSet)+'"]',typeof s.imageSizes=="string"&&(c+='[imagesizes="'+nn(s.imageSizes)+'"]')):c+='[href="'+nn(e)+'"]';var d=c;switch(t){case"style":d=Ua(e);break;case"script":d=Ha(e)}dn.has(d)||(e=b({rel:"preload",href:t==="image"&&s&&s.imageSrcSet?void 0:e,as:t},s),dn.set(d,e),i.querySelector(c)!==null||t==="style"&&i.querySelector(Gl(d))||t==="script"&&i.querySelector(Yl(d))||(t=i.createElement("link"),Rt(t,"link",e),Nt(t),i.head.appendChild(t)))}}function ry(e,t){Xn.m(e,t);var s=za;if(s&&e){var i=t&&typeof t.as=="string"?t.as:"script",c='link[rel="modulepreload"][as="'+nn(i)+'"][href="'+nn(e)+'"]',d=c;switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":d=Ha(e)}if(!dn.has(d)&&(e=b({rel:"modulepreload",href:e},t),dn.set(d,e),s.querySelector(c)===null)){switch(i){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(s.querySelector(Yl(d)))return}i=s.createElement("link"),Rt(i,"link",e),Nt(i),s.head.appendChild(i)}}}function iy(e,t,s){Xn.S(e,t,s);var i=za;if(i&&e){var c=ia(i).hoistableStyles,d=Ua(e);t=t||"default";var y=c.get(d);if(!y){var A={loading:0,preload:null};if(y=i.querySelector(Gl(d)))A.loading=5;else{e=b({rel:"stylesheet",href:e,"data-precedence":t},s),(s=dn.get(d))&&qu(e,s);var _=y=i.createElement("link");Nt(_),Rt(_,"link",e),_._p=new Promise(function(Y,W){_.onload=Y,_.onerror=W}),_.addEventListener("load",function(){A.loading|=1}),_.addEventListener("error",function(){A.loading|=2}),A.loading|=4,Ci(y,t,i)}y={type:"stylesheet",instance:y,count:1,state:A},c.set(d,y)}}}function oy(e,t){Xn.X(e,t);var s=za;if(s&&e){var i=ia(s).hoistableScripts,c=Ha(e),d=i.get(c);d||(d=s.querySelector(Yl(c)),d||(e=b({src:e,async:!0},t),(t=dn.get(c))&&Du(e,t),d=s.createElement("script"),Nt(d),Rt(d,"link",e),s.head.appendChild(d)),d={type:"script",instance:d,count:1,state:null},i.set(c,d))}}function cy(e,t){Xn.M(e,t);var s=za;if(s&&e){var i=ia(s).hoistableScripts,c=Ha(e),d=i.get(c);d||(d=s.querySelector(Yl(c)),d||(e=b({src:e,async:!0,type:"module"},t),(t=dn.get(c))&&Du(e,t),d=s.createElement("script"),Nt(d),Rt(d,"link",e),s.head.appendChild(d)),d={type:"script",instance:d,count:1,state:null},i.set(c,d))}}function Zp(e,t,s,i){var c=(c=de.current)?Ai(c):null;if(!c)throw Error(o(446));switch(e){case"meta":case"title":return null;case"style":return typeof s.precedence=="string"&&typeof s.href=="string"?(t=Ua(s.href),s=ia(c).hoistableStyles,i=s.get(t),i||(i={type:"style",instance:null,count:0,state:null},s.set(t,i)),i):{type:"void",instance:null,count:0,state:null};case"link":if(s.rel==="stylesheet"&&typeof s.href=="string"&&typeof s.precedence=="string"){e=Ua(s.href);var d=ia(c).hoistableStyles,y=d.get(e);if(y||(c=c.ownerDocument||c,y={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},d.set(e,y),(d=c.querySelector(Gl(e)))&&!d._p&&(y.instance=d,y.state.loading=5),dn.has(e)||(s={rel:"preload",as:"style",href:s.href,crossOrigin:s.crossOrigin,integrity:s.integrity,media:s.media,hrefLang:s.hrefLang,referrerPolicy:s.referrerPolicy},dn.set(e,s),d||uy(c,e,s,y.state))),t&&i===null)throw Error(o(528,""));return y}if(t&&i!==null)throw Error(o(529,""));return null;case"script":return t=s.async,s=s.src,typeof s=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=Ha(s),s=ia(c).hoistableScripts,i=s.get(t),i||(i={type:"script",instance:null,count:0,state:null},s.set(t,i)),i):{type:"void",instance:null,count:0,state:null};default:throw Error(o(444,e))}}function Ua(e){return'href="'+nn(e)+'"'}function Gl(e){return'link[rel="stylesheet"]['+e+"]"}function Jp(e){return b({},e,{"data-precedence":e.precedence,precedence:null})}function uy(e,t,s,i){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?i.loading=1:(t=e.createElement("link"),i.preload=t,t.addEventListener("load",function(){return i.loading|=1}),t.addEventListener("error",function(){return i.loading|=2}),Rt(t,"link",s),Nt(t),e.head.appendChild(t))}function Ha(e){return'[src="'+nn(e)+'"]'}function Yl(e){return"script[async]"+e}function Pp(e,t,s){if(t.count++,t.instance===null)switch(t.type){case"style":var i=e.querySelector('style[data-href~="'+nn(s.href)+'"]');if(i)return t.instance=i,Nt(i),i;var c=b({},s,{"data-href":s.href,"data-precedence":s.precedence,href:null,precedence:null});return i=(e.ownerDocument||e).createElement("style"),Nt(i),Rt(i,"style",c),Ci(i,s.precedence,e),t.instance=i;case"stylesheet":c=Ua(s.href);var d=e.querySelector(Gl(c));if(d)return t.state.loading|=4,t.instance=d,Nt(d),d;i=Jp(s),(c=dn.get(c))&&qu(i,c),d=(e.ownerDocument||e).createElement("link"),Nt(d);var y=d;return y._p=new Promise(function(A,_){y.onload=A,y.onerror=_}),Rt(d,"link",i),t.state.loading|=4,Ci(d,s.precedence,e),t.instance=d;case"script":return d=Ha(s.src),(c=e.querySelector(Yl(d)))?(t.instance=c,Nt(c),c):(i=s,(c=dn.get(d))&&(i=b({},s),Du(i,c)),e=e.ownerDocument||e,c=e.createElement("script"),Nt(c),Rt(c,"link",i),e.head.appendChild(c),t.instance=c);case"void":return null;default:throw Error(o(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(i=t.instance,t.state.loading|=4,Ci(i,s.precedence,e));return t.instance}function Ci(e,t,s){for(var i=s.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),c=i.length?i[i.length-1]:null,d=c,y=0;y title"):null)}function dy(e,t,s){if(s===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;switch(t.rel){case"stylesheet":return e=t.disabled,typeof t.precedence=="string"&&e==null;default:return!0}case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function t0(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function fy(e,t,s,i){if(s.type==="stylesheet"&&(typeof i.media!="string"||matchMedia(i.media).matches!==!1)&&(s.state.loading&4)===0){if(s.instance===null){var c=Ua(i.href),d=t.querySelector(Gl(c));if(d){t=d._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=Ei.bind(e),t.then(e,e)),s.state.loading|=4,s.instance=d,Nt(d);return}d=t.ownerDocument||t,i=Jp(i),(c=dn.get(c))&&qu(i,c),d=d.createElement("link"),Nt(d);var y=d;y._p=new Promise(function(A,_){y.onload=A,y.onerror=_}),Rt(d,"link",i),s.instance=d}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(s,t),(t=s.state.preload)&&(s.state.loading&3)===0&&(e.count++,s=Ei.bind(e),t.addEventListener("load",s),t.addEventListener("error",s))}}var _u=0;function my(e,t){return e.stylesheets&&e.count===0&&Mi(e,e.stylesheets),0_u?50:800)+t);return e.unsuspend=s,function(){e.unsuspend=null,clearTimeout(i),clearTimeout(c)}}:null}function Ei(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)Mi(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var Ri=null;function Mi(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,Ri=new Map,t.forEach(hy,e),Ri=null,Ei.call(e))}function hy(e,t){if(!(t.state.loading&4)){var s=Ri.get(e);if(s)var i=s.get(null);else{s=new Map,Ri.set(e,s);for(var c=e.querySelectorAll("link[data-precedence],style[data-precedence]"),d=0;d"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(a){console.error(a)}}return n(),Gu.exports=Ty(),Gu.exports}var Dy=qy();var A0="popstate";function _y(n={}){function a(o,u){let{pathname:f,search:p,hash:h}=o.location;return vd("",{pathname:f,search:p,hash:h},u.state&&u.state.usr||null,u.state&&u.state.key||"default")}function r(o,u){return typeof u=="string"?u:or(u)}return By(a,r,null,n)}function ot(n,a){if(n===!1||n===null||typeof n>"u")throw new Error(a)}function Sn(n,a){if(!n){typeof console<"u"&&console.warn(a);try{throw new Error(a)}catch{}}}function Oy(){return Math.random().toString(36).substring(2,10)}function C0(n,a){return{usr:n.state,key:n.key,idx:a}}function vd(n,a,r=null,o){return{pathname:typeof n=="string"?n:n.pathname,search:"",hash:"",...typeof a=="string"?tl(a):a,state:r,key:a&&a.key||o||Oy()}}function or({pathname:n="/",search:a="",hash:r=""}){return a&&a!=="?"&&(n+=a.charAt(0)==="?"?a:"?"+a),r&&r!=="#"&&(n+=r.charAt(0)==="#"?r:"#"+r),n}function tl(n){let a={};if(n){let r=n.indexOf("#");r>=0&&(a.hash=n.substring(r),n=n.substring(0,r));let o=n.indexOf("?");o>=0&&(a.search=n.substring(o),n=n.substring(0,o)),n&&(a.pathname=n)}return a}function By(n,a,r,o={}){let{window:u=document.defaultView,v5Compat:f=!1}=o,p=u.history,h="POP",g=null,x=v();x==null&&(x=0,p.replaceState({...p.state,idx:x},""));function v(){return(p.state||{idx:null}).idx}function b(){h="POP";let w=v(),j=w==null?null:w-x;x=w,g&&g({action:h,location:N.location,delta:j})}function S(w,j){h="PUSH";let M=vd(N.location,w,j);x=v()+1;let k=C0(M,x),C=N.createHref(M);try{p.pushState(k,"",C)}catch(z){if(z instanceof DOMException&&z.name==="DataCloneError")throw z;u.location.assign(C)}f&&g&&g({action:h,location:N.location,delta:1})}function E(w,j){h="REPLACE";let M=vd(N.location,w,j);x=v();let k=C0(M,x),C=N.createHref(M);p.replaceState(k,"",C),f&&g&&g({action:h,location:N.location,delta:0})}function R(w){return Ly(w)}let N={get action(){return h},get location(){return n(u,p)},listen(w){if(g)throw new Error("A history only accepts one active listener");return u.addEventListener(A0,b),g=w,()=>{u.removeEventListener(A0,b),g=null}},createHref(w){return a(u,w)},createURL:R,encodeLocation(w){let j=R(w);return{pathname:j.pathname,search:j.search,hash:j.hash}},push:S,replace:E,go(w){return p.go(w)}};return N}function Ly(n,a=!1){let r="http://localhost";typeof window<"u"&&(r=window.location.origin!=="null"?window.location.origin:window.location.href),ot(r,"No window.location.(origin|href) available to create URL");let o=typeof n=="string"?n:or(n);return o=o.replace(/ $/,"%20"),!a&&o.startsWith("//")&&(o=r+o),new URL(o,r)}function Mx(n,a,r="/"){return zy(n,a,r,!1)}function zy(n,a,r,o){let u=typeof a=="string"?tl(a):a,f=es(u.pathname||"/",r);if(f==null)return null;let p=Tx(n);Uy(p);let h=null;for(let g=0;h==null&&g{let v={relativePath:x===void 0?p.path||"":x,caseSensitive:p.caseSensitive===!0,childrenIndex:h,route:p};if(v.relativePath.startsWith("/")){if(!v.relativePath.startsWith(o)&&g)return;ot(v.relativePath.startsWith(o),`Absolute route path "${v.relativePath}" nested under path "${o}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),v.relativePath=v.relativePath.slice(o.length)}let b=Wn([o,v.relativePath]),S=r.concat(v);p.children&&p.children.length>0&&(ot(p.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${b}".`),Tx(p.children,a,S,b,g)),!(p.path==null&&!p.index)&&a.push({path:b,score:Iy(b,p.index),routesMeta:S})};return n.forEach((p,h)=>{if(p.path===""||!p.path?.includes("?"))f(p,h);else for(let g of qx(p.path))f(p,h,!0,g)}),a}function qx(n){let a=n.split("/");if(a.length===0)return[];let[r,...o]=a,u=r.endsWith("?"),f=r.replace(/\?$/,"");if(o.length===0)return u?[f,""]:[f];let p=qx(o.join("/")),h=[];return h.push(...p.map(g=>g===""?f:[f,g].join("/"))),u&&h.push(...p),h.map(g=>n.startsWith("/")&&g===""?"/":g)}function Uy(n){n.sort((a,r)=>a.score!==r.score?r.score-a.score:Qy(a.routesMeta.map(o=>o.childrenIndex),r.routesMeta.map(o=>o.childrenIndex)))}var Hy=/^:[\w-]+$/,Vy=3,$y=2,Gy=1,Yy=10,Fy=-2,k0=n=>n==="*";function Iy(n,a){let r=n.split("/"),o=r.length;return r.some(k0)&&(o+=Fy),a&&(o+=$y),r.filter(u=>!k0(u)).reduce((u,f)=>u+(Hy.test(f)?Vy:f===""?Gy:Yy),o)}function Qy(n,a){return n.length===a.length&&n.slice(0,-1).every((o,u)=>o===a[u])?n[n.length-1]-a[a.length-1]:0}function Xy(n,a,r=!1){let{routesMeta:o}=n,u={},f="/",p=[];for(let h=0;h{if(v==="*"){let R=h[S]||"";p=f.slice(0,f.length-R.length).replace(/(.)\/+$/,"$1")}const E=h[S];return b&&!E?x[v]=void 0:x[v]=(E||"").replace(/%2F/g,"/"),x},{}),pathname:f,pathnameBase:p,pattern:n}}function Ky(n,a=!1,r=!0){Sn(n==="*"||!n.endsWith("*")||n.endsWith("/*"),`Route path "${n}" will be treated as if it were "${n.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${n.replace(/\*$/,"/*")}".`);let o=[],u="^"+n.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(p,h,g)=>(o.push({paramName:h,isOptional:g!=null}),g?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return n.endsWith("*")?(o.push({paramName:"*"}),u+=n==="*"||n==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):r?u+="\\/*$":n!==""&&n!=="/"&&(u+="(?:(?=\\/|$))"),[new RegExp(u,a?void 0:"i"),o]}function Zy(n){try{return n.split("/").map(a=>decodeURIComponent(a).replace(/\//g,"%2F")).join("/")}catch(a){return Sn(!1,`The URL path "${n}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${a}).`),n}}function es(n,a){if(a==="/")return n;if(!n.toLowerCase().startsWith(a.toLowerCase()))return null;let r=a.endsWith("/")?a.length-1:a.length,o=n.charAt(r);return o&&o!=="/"?null:n.slice(r)||"/"}var Jy=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Py=n=>Jy.test(n);function Wy(n,a="/"){let{pathname:r,search:o="",hash:u=""}=typeof n=="string"?tl(n):n,f;if(r)if(Py(r))f=r;else{if(r.includes("//")){let p=r;r=r.replace(/\/\/+/g,"/"),Sn(!1,`Pathnames cannot have embedded double slashes - normalizing ${p} -> ${r}`)}r.startsWith("/")?f=E0(r.substring(1),"/"):f=E0(r,a)}else f=a;return{pathname:f,search:n1(o),hash:s1(u)}}function E0(n,a){let r=a.replace(/\/+$/,"").split("/");return n.split("/").forEach(u=>{u===".."?r.length>1&&r.pop():u!=="."&&r.push(u)}),r.length>1?r.join("/"):"/"}function Qu(n,a,r,o){return`Cannot include a '${n}' character in a manually specified \`to.${a}\` field [${JSON.stringify(o)}]. Please separate it out to the \`to.${r}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function e1(n){return n.filter((a,r)=>r===0||a.route.path&&a.route.path.length>0)}function Dx(n){let a=e1(n);return a.map((r,o)=>o===a.length-1?r.pathname:r.pathnameBase)}function _x(n,a,r,o=!1){let u;typeof n=="string"?u=tl(n):(u={...n},ot(!u.pathname||!u.pathname.includes("?"),Qu("?","pathname","search",u)),ot(!u.pathname||!u.pathname.includes("#"),Qu("#","pathname","hash",u)),ot(!u.search||!u.search.includes("#"),Qu("#","search","hash",u)));let f=n===""||u.pathname==="",p=f?"/":u.pathname,h;if(p==null)h=r;else{let b=a.length-1;if(!o&&p.startsWith("..")){let S=p.split("/");for(;S[0]==="..";)S.shift(),b-=1;u.pathname=S.join("/")}h=b>=0?a[b]:"/"}let g=Wy(u,h),x=p&&p!=="/"&&p.endsWith("/"),v=(f||p===".")&&r.endsWith("/");return!g.pathname.endsWith("/")&&(x||v)&&(g.pathname+="/"),g}var Wn=n=>n.join("/").replace(/\/\/+/g,"/"),t1=n=>n.replace(/\/+$/,"").replace(/^\/*/,"/"),n1=n=>!n||n==="?"?"":n.startsWith("?")?n:"?"+n,s1=n=>!n||n==="#"?"":n.startsWith("#")?n:"#"+n;function a1(n){return n!=null&&typeof n.status=="number"&&typeof n.statusText=="string"&&typeof n.internal=="boolean"&&"data"in n}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Ox=["POST","PUT","PATCH","DELETE"];new Set(Ox);var l1=["GET",...Ox];new Set(l1);var nl=m.createContext(null);nl.displayName="DataRouter";var vo=m.createContext(null);vo.displayName="DataRouterState";m.createContext(!1);var Bx=m.createContext({isTransitioning:!1});Bx.displayName="ViewTransition";var r1=m.createContext(new Map);r1.displayName="Fetchers";var i1=m.createContext(null);i1.displayName="Await";var Mn=m.createContext(null);Mn.displayName="Navigation";var pr=m.createContext(null);pr.displayName="Location";var ns=m.createContext({outlet:null,matches:[],isDataRoute:!1});ns.displayName="Route";var zd=m.createContext(null);zd.displayName="RouteError";function o1(n,{relative:a}={}){ot(xr(),"useHref() may be used only in the context of a component.");let{basename:r,navigator:o}=m.useContext(Mn),{hash:u,pathname:f,search:p}=gr(n,{relative:a}),h=f;return r!=="/"&&(h=f==="/"?r:Wn([r,f])),o.createHref({pathname:h,search:p,hash:u})}function xr(){return m.useContext(pr)!=null}function ta(){return ot(xr(),"useLocation() may be used only in the context of a component."),m.useContext(pr).location}var Lx="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function zx(n){m.useContext(Mn).static||m.useLayoutEffect(n)}function c1(){let{isDataRoute:n}=m.useContext(ns);return n?j1():u1()}function u1(){ot(xr(),"useNavigate() may be used only in the context of a component.");let n=m.useContext(nl),{basename:a,navigator:r}=m.useContext(Mn),{matches:o}=m.useContext(ns),{pathname:u}=ta(),f=JSON.stringify(Dx(o)),p=m.useRef(!1);return zx(()=>{p.current=!0}),m.useCallback((g,x={})=>{if(Sn(p.current,Lx),!p.current)return;if(typeof g=="number"){r.go(g);return}let v=_x(g,JSON.parse(f),u,x.relative==="path");n==null&&a!=="/"&&(v.pathname=v.pathname==="/"?a:Wn([a,v.pathname])),(x.replace?r.replace:r.push)(v,x.state,x)},[a,r,f,u,n])}m.createContext(null);function gr(n,{relative:a}={}){let{matches:r}=m.useContext(ns),{pathname:o}=ta(),u=JSON.stringify(Dx(r));return m.useMemo(()=>_x(n,JSON.parse(u),o,a==="path"),[n,u,o,a])}function d1(n,a){return Ux(n,a)}function Ux(n,a,r,o,u){ot(xr(),"useRoutes() may be used only in the context of a component.");let{navigator:f}=m.useContext(Mn),{matches:p}=m.useContext(ns),h=p[p.length-1],g=h?h.params:{},x=h?h.pathname:"/",v=h?h.pathnameBase:"/",b=h&&h.route;{let M=b&&b.path||"";Hx(x,!b||M.endsWith("*")||M.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${x}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let S=ta(),E;if(a){let M=typeof a=="string"?tl(a):a;ot(v==="/"||M.pathname?.startsWith(v),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${v}" but pathname "${M.pathname}" was given in the \`location\` prop.`),E=M}else E=S;let R=E.pathname||"/",N=R;if(v!=="/"){let M=v.replace(/^\//,"").split("/");N="/"+R.replace(/^\//,"").split("/").slice(M.length).join("/")}let w=Mx(n,{pathname:N});Sn(b||w!=null,`No routes matched location "${E.pathname}${E.search}${E.hash}" `),Sn(w==null||w[w.length-1].route.element!==void 0||w[w.length-1].route.Component!==void 0||w[w.length-1].route.lazy!==void 0,`Matched leaf route at location "${E.pathname}${E.search}${E.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let j=x1(w&&w.map(M=>Object.assign({},M,{params:Object.assign({},g,M.params),pathname:Wn([v,f.encodeLocation?f.encodeLocation(M.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathname]),pathnameBase:M.pathnameBase==="/"?v:Wn([v,f.encodeLocation?f.encodeLocation(M.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:M.pathnameBase])})),p,r,o,u);return a&&j?m.createElement(pr.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...E},navigationType:"POP"}},j):j}function f1(){let n=S1(),a=a1(n)?`${n.status} ${n.statusText}`:n instanceof Error?n.message:JSON.stringify(n),r=n instanceof Error?n.stack:null,o="rgba(200,200,200, 0.5)",u={padding:"0.5rem",backgroundColor:o},f={padding:"2px 4px",backgroundColor:o},p=null;return console.error("Error handled by React Router default ErrorBoundary:",n),p=m.createElement(m.Fragment,null,m.createElement("p",null,"💿 Hey developer 👋"),m.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",m.createElement("code",{style:f},"ErrorBoundary")," or"," ",m.createElement("code",{style:f},"errorElement")," prop on your route.")),m.createElement(m.Fragment,null,m.createElement("h2",null,"Unexpected Application Error!"),m.createElement("h3",{style:{fontStyle:"italic"}},a),r?m.createElement("pre",{style:u},r):null,p)}var m1=m.createElement(f1,null),h1=class extends m.Component{constructor(n){super(n),this.state={location:n.location,revalidation:n.revalidation,error:n.error}}static getDerivedStateFromError(n){return{error:n}}static getDerivedStateFromProps(n,a){return a.location!==n.location||a.revalidation!=="idle"&&n.revalidation==="idle"?{error:n.error,location:n.location,revalidation:n.revalidation}:{error:n.error!==void 0?n.error:a.error,location:a.location,revalidation:n.revalidation||a.revalidation}}componentDidCatch(n,a){this.props.onError?this.props.onError(n,a):console.error("React Router caught the following error during render",n)}render(){return this.state.error!==void 0?m.createElement(ns.Provider,{value:this.props.routeContext},m.createElement(zd.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function p1({routeContext:n,match:a,children:r}){let o=m.useContext(nl);return o&&o.static&&o.staticContext&&(a.route.errorElement||a.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=a.route.id),m.createElement(ns.Provider,{value:n},r)}function x1(n,a=[],r=null,o=null,u=null){if(n==null){if(!r)return null;if(r.errors)n=r.matches;else if(a.length===0&&!r.initialized&&r.matches.length>0)n=r.matches;else return null}let f=n,p=r?.errors;if(p!=null){let v=f.findIndex(b=>b.route.id&&p?.[b.route.id]!==void 0);ot(v>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(p).join(",")}`),f=f.slice(0,Math.min(f.length,v+1))}let h=!1,g=-1;if(r)for(let v=0;v=0?f=f.slice(0,g+1):f=[f[0]];break}}}let x=r&&o?(v,b)=>{o(v,{location:r.location,params:r.matches?.[0]?.params??{},errorInfo:b})}:void 0;return f.reduceRight((v,b,S)=>{let E,R=!1,N=null,w=null;r&&(E=p&&b.route.id?p[b.route.id]:void 0,N=b.route.errorElement||m1,h&&(g<0&&S===0?(Hx("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),R=!0,w=null):g===S&&(R=!0,w=b.route.hydrateFallbackElement||null)));let j=a.concat(f.slice(0,S+1)),M=()=>{let k;return E?k=N:R?k=w:b.route.Component?k=m.createElement(b.route.Component,null):b.route.element?k=b.route.element:k=v,m.createElement(p1,{match:b,routeContext:{outlet:v,matches:j,isDataRoute:r!=null},children:k})};return r&&(b.route.ErrorBoundary||b.route.errorElement||S===0)?m.createElement(h1,{location:r.location,revalidation:r.revalidation,component:N,error:E,children:M(),routeContext:{outlet:null,matches:j,isDataRoute:!0},onError:x}):M()},null)}function Ud(n){return`${n} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function g1(n){let a=m.useContext(nl);return ot(a,Ud(n)),a}function v1(n){let a=m.useContext(vo);return ot(a,Ud(n)),a}function b1(n){let a=m.useContext(ns);return ot(a,Ud(n)),a}function Hd(n){let a=b1(n),r=a.matches[a.matches.length-1];return ot(r.route.id,`${n} can only be used on routes that contain a unique "id"`),r.route.id}function y1(){return Hd("useRouteId")}function S1(){let n=m.useContext(zd),a=v1("useRouteError"),r=Hd("useRouteError");return n!==void 0?n:a.errors?.[r]}function j1(){let{router:n}=g1("useNavigate"),a=Hd("useNavigate"),r=m.useRef(!1);return zx(()=>{r.current=!0}),m.useCallback(async(u,f={})=>{Sn(r.current,Lx),r.current&&(typeof u=="number"?n.navigate(u):await n.navigate(u,{fromRouteId:a,...f}))},[n,a])}var R0={};function Hx(n,a,r){!a&&!R0[n]&&(R0[n]=!0,Sn(!1,r))}m.memo(N1);function N1({routes:n,future:a,state:r,unstable_onError:o}){return Ux(n,void 0,r,o,a)}function Ki(n){ot(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function w1({basename:n="/",children:a=null,location:r,navigationType:o="POP",navigator:u,static:f=!1}){ot(!xr(),"You cannot render a inside another . You should never have more than one in your app.");let p=n.replace(/^\/*/,"/"),h=m.useMemo(()=>({basename:p,navigator:u,static:f,future:{}}),[p,u,f]);typeof r=="string"&&(r=tl(r));let{pathname:g="/",search:x="",hash:v="",state:b=null,key:S="default"}=r,E=m.useMemo(()=>{let R=es(g,p);return R==null?null:{location:{pathname:R,search:x,hash:v,state:b,key:S},navigationType:o}},[p,g,x,v,b,S,o]);return Sn(E!=null,` is not able to match the URL "${g}${x}${v}" because it does not start with the basename, so the won't render anything.`),E==null?null:m.createElement(Mn.Provider,{value:h},m.createElement(pr.Provider,{children:a,value:E}))}function A1({children:n,location:a}){return d1(bd(n),a)}function bd(n,a=[]){let r=[];return m.Children.forEach(n,(o,u)=>{if(!m.isValidElement(o))return;let f=[...a,u];if(o.type===m.Fragment){r.push.apply(r,bd(o.props.children,f));return}ot(o.type===Ki,`[${typeof o.type=="string"?o.type:o.type.name}] is not a component. All component children of must be a or `),ot(!o.props.index||!o.props.children,"An index route cannot have child routes.");let p={id:o.props.id||f.join("-"),caseSensitive:o.props.caseSensitive,element:o.props.element,Component:o.props.Component,index:o.props.index,path:o.props.path,middleware:o.props.middleware,loader:o.props.loader,action:o.props.action,hydrateFallbackElement:o.props.hydrateFallbackElement,HydrateFallback:o.props.HydrateFallback,errorElement:o.props.errorElement,ErrorBoundary:o.props.ErrorBoundary,hasErrorBoundary:o.props.hasErrorBoundary===!0||o.props.ErrorBoundary!=null||o.props.errorElement!=null,shouldRevalidate:o.props.shouldRevalidate,handle:o.props.handle,lazy:o.props.lazy};o.props.children&&(p.children=bd(o.props.children,f)),r.push(p)}),r}var Zi="get",Ji="application/x-www-form-urlencoded";function bo(n){return n!=null&&typeof n.tagName=="string"}function C1(n){return bo(n)&&n.tagName.toLowerCase()==="button"}function k1(n){return bo(n)&&n.tagName.toLowerCase()==="form"}function E1(n){return bo(n)&&n.tagName.toLowerCase()==="input"}function R1(n){return!!(n.metaKey||n.altKey||n.ctrlKey||n.shiftKey)}function M1(n,a){return n.button===0&&(!a||a==="_self")&&!R1(n)}var zi=null;function T1(){if(zi===null)try{new FormData(document.createElement("form"),0),zi=!1}catch{zi=!0}return zi}var q1=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Xu(n){return n!=null&&!q1.has(n)?(Sn(!1,`"${n}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Ji}"`),null):n}function D1(n,a){let r,o,u,f,p;if(k1(n)){let h=n.getAttribute("action");o=h?es(h,a):null,r=n.getAttribute("method")||Zi,u=Xu(n.getAttribute("enctype"))||Ji,f=new FormData(n)}else if(C1(n)||E1(n)&&(n.type==="submit"||n.type==="image")){let h=n.form;if(h==null)throw new Error('Cannot submit a @@ -106,12 +107,14 @@ export default function DriveDockAction({ state: { roverId, keymap }, actions, } = useControlSystem(); + const dockAssist = useManualDockAssist(); const frame = useTelemetryFrame(roverId); const state = driveDockState ?? deriveDriveDockState(frame); const { driving, docked, charging, dockingInProgress } = state; const [pending, setPending] = useState(null); const [confirmOpen, setConfirmOpen] = useState(false); const [showModal, setShowModal] = useState(false); + const manualAssistActive = Boolean(dockAssist.active); const driveDisabled = !roverId || pending !== null; const dockDisabled = !roverId || pending !== null; @@ -120,10 +123,13 @@ export default function DriveDockAction({ const dockKeyLabel = formatKeyLabel(keymap?.dockMacro?.[0]); const dockInstructions = { - summary: 'You are about to trigger an automatic docking attempt! To have a successful dock:', - steps: ['Line up the rover straight in front of the dock', 'Make sure the rover is about one foot or half a meter away from the dock', 'Press Confirm Dock to begin the docking process'], + summary: 'Use assist mode to manually line up with the dock.', + steps: [ + 'Line up the center dot of your rover with the center of the dock', + 'The UI will indicate when you are successfully docked', + 'When good contact is made, charging will start in about 5 seconds.', + ], }; - const dockButtonCaption = 'Click this button to initate auto-dock.'; const startDriveInstructions = { summary: 'You must enable driving mode before you can move the rover.', @@ -131,13 +137,14 @@ export default function DriveDockAction({ }; const dockValue = docked ? 'Docked' : 'Undocked'; const dockTone = docked ? 'good' : 'bad'; - const chargeValue = charging ? 'Charging' : docked ? 'Charging in 5s' : '—'; + const chargeValue = charging ? 'Charging' : docked ? 'Charging soon...' : '—'; const chargeTone = charging ? 'good' : docked ? 'warn' : 'bad'; const handleReturnToDrive = async () => { if (!roverId || pending) return; setPending('drive'); try { + dockAssist.exitAssist(); actions.setMode('drive'); await actions.runMacro('drive-sequence'); } catch (err) { @@ -153,6 +160,7 @@ export default function DriveDockAction({ setShowModal(false); setPending('drive'); try { + dockAssist.exitAssist(); actions.setMode('drive'); await actions.runMacro('drive-sequence'); } catch (err) { @@ -166,8 +174,7 @@ export default function DriveDockAction({ if (!roverId || pending) return; setPending('dock'); try { - actions.setMode('dock'); - await actions.runMacro('seek-dock'); + dockAssist.enterAssist(); } catch (err) { alert(err.message); } finally { @@ -179,6 +186,10 @@ export default function DriveDockAction({ const handleOpenDock = () => { if (dockDisabled) return; + if (manualAssistActive) { + dockAssist.exitAssist(); + return; + } setShowModal(true); setConfirmOpen(true); }; @@ -188,7 +199,6 @@ export default function DriveDockAction({ const ctaText = 'text-center'; const ctaLayout = 'items-center justify-between'; const compactLayout = 'items-center justify-center'; - const ctaTextAndLayout = `${ctaText} ${ctaLayout}`; const ctaSize = isMobile ? 'text-sm font-semibold' : ''; const emeraldCta = 'border-emerald-300/70 bg-emerald-800 text-emerald-50 hover:bg-emerald-700 focus-visible:ring-emerald-300'; @@ -196,6 +206,8 @@ export default function DriveDockAction({ 'border-amber-300/70 bg-amber-900 text-amber-50 hover:bg-amber-800 focus-visible:ring-amber-300'; const indigoCta = 'border-indigo-300/70 bg-indigo-900 text-indigo-50 hover:bg-indigo-800 focus-visible:ring-indigo-300'; + const cyanCta = + 'border-cyan-300/70 bg-cyan-900 text-cyan-50 hover:bg-cyan-800 focus-visible:ring-cyan-300'; const orangeCta = 'border-amber-300/70 bg-amber-900 text-amber-50 hover:bg-amber-800 focus-visible:ring-amber-300'; const forceExpanded = dockingInProgress; @@ -243,8 +255,8 @@ export default function DriveDockAction({ if (dockingInProgress) { const inProgressCopy = { - summary: 'If the rover is failing to dock, click to drive and try again.', - steps: ['The rover should be slowly wiggling towards the dock', 'If it is obviously not working, press this button to enter driving mode and try again'], + summary: 'The rover is currently attempting to dock itself due to being idle.', + steps: ['Click to return to driving mode.'], }; return ( @@ -257,7 +269,7 @@ export default function DriveDockAction({ >
- Docking in Progress + Attempting to auto-dock... {/* {!isMobile && expanded ? ( */} {expanded ? (
@@ -292,32 +304,31 @@ export default function DriveDockAction({ className={ isMobile ? `flex w-full ${baseCardClasses} ${compactHeight} ${ctaText} ${layoutClass} ${ctaSize} ${ - compactDockedDriving ? orangeCta : indigoCta + manualAssistActive ? cyanCta : compactDockedDriving ? orangeCta : indigoCta }` - : `${baseCardClasses} ${filledHeight} ${ctaText} ${ctaLayout} ${ctaSize} ${indigoCta}` + : `${baseCardClasses} ${filledHeight} ${ctaText} ${ctaLayout} ${ctaSize} ${manualAssistActive ? cyanCta : indigoCta}` } >
- {compactDockedDriving ? 'Docked!' : 'Dock and Charge'} + {compactDockedDriving ? 'Docked!' : manualAssistActive ? 'Exit Docking Assist' : 'Dock and charge'} {!isMobile && expanded ? (
{dockKeyLabel ? : null} - +
) : null}
{!isMobile && expanded && ( <> -

{dockButtonCaption}

+

+ Get near a dock and press this button to enter docking assist mode. +

-
- Line up straight, about 1 foot away, before docking. -
)} diff --git a/webui/src/components/DriverVideo/index.jsx b/webui/src/components/DriverVideo/index.jsx index eff3c83a..375fef1c 100644 --- a/webui/src/components/DriverVideo/index.jsx +++ b/webui/src/components/DriverVideo/index.jsx @@ -4,6 +4,7 @@ import { useControlSystem } from '../../controls/index.js'; import { useDriverVideoModePolicy } from '../../hooks/useDriverVideoModePolicy.js'; import TurnsOverlay from '../HudOverlays/TurnsOverlay/index.jsx'; import HudOverlay from '../HudOverlays/HudOverlay/index.jsx'; +import ManualDockAssistOverlay from '../HudOverlays/ManualDockAssistOverlay/index.jsx'; import RoverDescriptionOverlay from '../HudOverlays/RoverDescriptionOverlay/index.jsx'; import OvercurrentOverlay from '../HudOverlays/OvercurrentOverlay/index.jsx'; import LowBatteryOverlay from '../HudOverlays/LowBatteryOverlay/index.jsx'; @@ -50,6 +51,7 @@ export default function DriverVideo({ layoutFormat = 'desktop' }) { mobileHud={mobileHud} labelScale={1} /> + diff --git a/webui/src/components/HudOverlays/ManualDockAssistOverlay/index.jsx b/webui/src/components/HudOverlays/ManualDockAssistOverlay/index.jsx new file mode 100644 index 00000000..9cb0c26b --- /dev/null +++ b/webui/src/components/HudOverlays/ManualDockAssistOverlay/index.jsx @@ -0,0 +1,72 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useManualDockAssist } from '../../../features/manualDockAssist/useManualDockAssist.js'; + +function ManualDockAssistOverlay({ mobileHud = false }) { + const { visible, statusLabel, statusTone, active, charging } = useManualDockAssist({ manageLifecycle: true }); + const [popupMessage, setPopupMessage] = useState(''); + const timerRef = useRef(null); + const prevActiveRef = useRef(active); + + useEffect(() => { + const prevActive = prevActiveRef.current; + const enabledNow = active && !prevActive; + const autoDisabledOnCharge = !active && prevActive && charging; + if (enabledNow) { + setPopupMessage('Dock assist mode enabled'); + } else if (autoDisabledOnCharge) { + setPopupMessage('Docking successful! Thank you!'); + } + prevActiveRef.current = active; + }, [active, charging]); + + useEffect(() => { + if (!popupMessage) return undefined; + if (timerRef.current) { + clearTimeout(timerRef.current); + } + timerRef.current = setTimeout(() => { + setPopupMessage(''); + timerRef.current = null; + }, 2500); + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + }; + }, [popupMessage]); + + if (!visible) return null; + const toneClass = + statusTone === 'good' + ? 'border-emerald-200/90 bg-emerald-900/90 text-emerald-50' + : statusTone === 'warn' + ? 'border-amber-200/90 bg-amber-900/90 text-amber-50' + : 'border-cyan-200/90 bg-cyan-900/90 text-cyan-50'; + + return ( +
+ {popupMessage ? ( +
+
+ {popupMessage} +
+
+ ) : ( +
+ {statusLabel} +
+ )} +
+ ); +} + +export default React.memo(ManualDockAssistOverlay); diff --git a/webui/src/components/MobileControls/MobileControlsContent.jsx b/webui/src/components/MobileControls/MobileControlsContent.jsx index 7d90a2b9..a4551fee 100644 --- a/webui/src/components/MobileControls/MobileControlsContent.jsx +++ b/webui/src/components/MobileControls/MobileControlsContent.jsx @@ -18,6 +18,7 @@ import { AUX_ALL_FORWARD, AUX_ALL_BACKWARD, } from './constants.js'; +import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js'; function MobileJoystickPanel({ layout }) { const { @@ -99,6 +100,7 @@ function MobileActionsColumnContent({ layout }) { pipeline, actions: { setServoAngle, setNightVision, setAuxMotors, startHorn, stopHorn }, } = useControlSystem(); + const dockAssist = useManualDockAssist(); const disabled = !roverId; const activeRef = useRef(null); const cameraConfig = camera?.config; @@ -115,6 +117,7 @@ function MobileActionsColumnContent({ layout }) { : typeof cameraConfig?.homeAngle === 'number' ? cameraConfig.homeAngle : (cameraMin + cameraMax) / 2; + const cameraDisabled = Boolean(disabled || dockAssist.cameraLocked); const handleNightVisionToggle = useCallback( (nextOn) => { @@ -174,6 +177,7 @@ function MobileActionsColumnContent({ layout }) { min={cameraMin} max={cameraMax} step={0.5} + disabled={cameraDisabled} onChange={setServoAngle} orientation="vertical" label="Camera Tilt" diff --git a/webui/src/components/QuickstartOverlay/index.jsx b/webui/src/components/QuickstartOverlay/index.jsx index c12d25d1..a10c2e2e 100644 --- a/webui/src/components/QuickstartOverlay/index.jsx +++ b/webui/src/components/QuickstartOverlay/index.jsx @@ -30,7 +30,7 @@ function DesktopQuickstart({ keymap }) {
-

3. When done, please dock your rover! Line up with the dock and press "Dock and Charge".

+

3. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.

); } @@ -41,7 +41,7 @@ function MobileQuickstart() {

1. Press "Start Driving" put your rover into driving mode.

2. Touch and hold in Joystick area to move.

3. Use the other column for motor, horn, and camera controls.

-

4. When done, please dock your rover! Line up with the dock and press "Dock and Charge".

+

4. When done, enter "Docking Assist" and line up the front sensor with the dock sensor.

); } diff --git a/webui/src/components/RightPaneTabs/index.jsx b/webui/src/components/RightPaneTabs/index.jsx index b5ac9b8d..27eb3156 100644 --- a/webui/src/components/RightPaneTabs/index.jsx +++ b/webui/src/components/RightPaneTabs/index.jsx @@ -24,6 +24,7 @@ import { useSessionSelector } from '../../context/SessionContext.jsx'; import { useSettingsNamespace } from '../../settings/index.js'; import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx'; import { useState } from 'react'; +import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js'; function TopDownMapPanel() { const { @@ -47,6 +48,7 @@ function DriveDockPanel() { pipeline, actions: { setServoAngle, setNightVision, startHorn, stopHorn }, } = useControlSystem(); + const dockAssist = useManualDockAssist(); const driveDockState = useDriveDockState(roverId); const hideInlineControls = driveDockState.docked && !driveDockState.driving; @@ -68,6 +70,7 @@ function DriveDockPanel() { const hornLabel = formatKeyLabel(keymap?.hornHonk?.[0]); const upLabel = formatKeyLabel(keymap?.cameraUp?.[0]); const downLabel = formatKeyLabel(keymap?.cameraDown?.[0]); + const cameraDisabled = Boolean(!roverId || dockAssist.cameraLocked); return (
@@ -97,6 +100,7 @@ function DriveDockPanel() { value={value} min={min} max={max} + disabled={cameraDisabled} onChange={setServoAngle} keyDownLabel={downLabel} keyUpLabel={upLabel} diff --git a/webui/src/controls/ControlContext.jsx b/webui/src/controls/ControlContext.jsx index ad1aeb0d..2d583895 100644 --- a/webui/src/controls/ControlContext.jsx +++ b/webui/src/controls/ControlContext.jsx @@ -11,6 +11,7 @@ import { HORN_HEAT_RESUME_THRESHOLD, HORN_HEAT_UP_PER_SEC, HORN_MAX_MS, + MANUAL_DOCK_ASSIST_MAX_SPEED, SONG_DEFAULT_NOTE, } from './constants.js'; import { canonicalizeKeyInput } from './keymapUtils.js'; @@ -212,7 +213,23 @@ export function ControlSystemProvider({ children }) { const setDriveVector = useCallback( (vector, meta = {}) => { - const computed = computeDifferentialSpeeds(vector, meta.speedOptions); + const speedOptions = { ...(meta.speedOptions || {}) }; + if (state.manualDockAssist?.active) { + const capped = Math.max(1, Math.min(MANUAL_DOCK_ASSIST_MAX_SPEED, 500)); + speedOptions.maxSpeed = Math.min( + typeof speedOptions.maxSpeed === 'number' ? speedOptions.maxSpeed : capped, + capped, + ); + speedOptions.baseSpeed = Math.min( + typeof speedOptions.baseSpeed === 'number' ? speedOptions.baseSpeed : capped, + capped, + ); + speedOptions.boostSpeed = Math.min( + typeof speedOptions.boostSpeed === 'number' ? speedOptions.boostSpeed : capped, + capped, + ); + } + const computed = computeDifferentialSpeeds(vector, speedOptions); dispatch({ type: 'control/update-drive', payload: { ...computed, source: meta.source ?? null }, @@ -220,7 +237,7 @@ export function ControlSystemProvider({ children }) { recordControlIntent(); pipeline.sendDriveDirect(computed.speeds); }, - [pipeline, recordControlIntent], + [pipeline, recordControlIntent, state.manualDockAssist?.active], ); const setAuxMotors = useCallback( @@ -262,15 +279,17 @@ export function ControlSystemProvider({ children }) { }, [saveControlSettings]); const setServoAngle = useCallback( - (value) => { + (value, options = {}) => { if (!pipeline.servoConfig) return; + const force = Boolean(options?.force); + if (state.manualDockAssist?.active && !force) return; const clamped = clampServoAngle(pipeline.servoConfig, value); dispatch({ type: 'control/set-camera-angle', payload: clamped }); pipeline.sendServoAngle(clamped); servoAngleRef.current = clamped; recordControlIntent(); }, - [pipeline, recordControlIntent], + [pipeline, recordControlIntent, state.manualDockAssist?.active], ); const nudgeServo = useCallback( @@ -318,8 +337,6 @@ export function ControlSystemProvider({ children }) { steps: removeDriveSequenceBackoff(macro.steps), }; } - } else if (macroId === 'seek-dock') { - recordControlIntent(); } await pipeline.runMacroSteps(macroToRun); }, @@ -509,6 +526,32 @@ export function ControlSystemProvider({ children }) { return () => clearInterval(interval); }, [dispatch, hornNeedsTick, HORN_HEAT_COOL_PER_SEC, HORN_HEAT_RESUME_THRESHOLD, HORN_HEAT_UP_PER_SEC]); + const setManualDockAssistActive = useCallback( + (active) => { + const nextActive = Boolean(active); + const prevActive = Boolean(state.manualDockAssist?.active); + if (prevActive === nextActive) return; + dispatch({ type: 'control/set-manual-dock-assist', payload: nextActive }); + if (!nextActive) { + pipeline.sendSong([{ note: 83, duration: 10 }, { note: 76, duration: 10 }], { slot: 0 }); + setServoAngle(0, { force: true }); + return; + } + const minAngle = + typeof pipeline.servoConfig?.minAngle === 'number' + ? pipeline.servoConfig.minAngle + : -45; + setServoAngle(minAngle, { force: true }); + pipeline.sendSong([{ note: 76, duration: 10 }, { note: 83, duration: 10 }], { slot: 0 }); + recordControlIntent(); + }, + [pipeline, recordControlIntent, setServoAngle, state.manualDockAssist?.active], + ); + + const toggleManualDockAssist = useCallback(() => { + setManualDockAssistActive(!state.manualDockAssist?.active); + }, [setManualDockAssistActive, state.manualDockAssist?.active]); + const registerInputState = useCallback((source, data) => { dispatch({ type: 'control/register-input-state', payload: { source, state: data } }); }, []); @@ -539,6 +582,8 @@ export function ControlSystemProvider({ children }) { updateKeyBinding, resetKeyBindings, registerInputState, + setManualDockAssistActive, + toggleManualDockAssist, setSongNote, sendSong, startHorn, @@ -565,6 +610,8 @@ export function ControlSystemProvider({ children }) { updateKeyBinding, resetKeyBindings, registerInputState, + setManualDockAssistActive, + toggleManualDockAssist, setSongNote, sendSong, startHorn, diff --git a/webui/src/controls/constants.js b/webui/src/controls/constants.js index 57ae90f4..8beccf92 100644 --- a/webui/src/controls/constants.js +++ b/webui/src/controls/constants.js @@ -12,6 +12,8 @@ export const DRIVE_LIMITS = { boostSpeed: 400, }; +export const MANUAL_DOCK_ASSIST_MAX_SPEED = 60; + export const COMMAND_DELAY_MS = 100; export const SONG_NOTE_RANGE = [31, 127]; @@ -78,12 +80,4 @@ export const DEFAULT_MACROS = [ { type: 'drive', speeds: { left: 0, right: 0 } }, ], }, - { - id: 'seek-dock', - label: 'Dock', - description: 'Send the seek dock command.', - steps: [ - { type: 'oi', command: 'dock' } - ], - }, ]; diff --git a/webui/src/controls/controlReducer.js b/webui/src/controls/controlReducer.js index 1b2275f9..e2dd251f 100644 --- a/webui/src/controls/controlReducer.js +++ b/webui/src/controls/controlReducer.js @@ -43,6 +43,12 @@ function createMicState() { }; } +function createManualDockAssistState() { + return { + active: false, + }; +} + export const initialControlState = { roverId: null, mode: 'drive', @@ -52,6 +58,7 @@ export const initialControlState = { song: createSongState(), horn: createHornState(), mic: createMicState(), + manualDockAssist: createManualDockAssistState(), lastControlIntentAt: 0, macros: DEFAULT_MACROS, keymap: DEFAULT_KEYMAP, @@ -69,6 +76,7 @@ export function controlReducer(state, action) { song: action.payload ? state.song : createSongState(), horn: action.payload ? state.horn : createHornState(), mic: action.payload ? state.mic : createMicState(), + manualDockAssist: action.payload ? state.manualDockAssist : createManualDockAssistState(), lastControlIntentAt: action.payload ? state.lastControlIntentAt : 0, }; case 'control/set-mode': @@ -187,6 +195,14 @@ export function controlReducer(state, action) { pttActive: Boolean(action.payload), }, }; + case 'control/set-manual-dock-assist': + return { + ...state, + manualDockAssist: { + ...(state.manualDockAssist || createManualDockAssistState()), + active: Boolean(action.payload), + }, + }; default: return state; } diff --git a/webui/src/controls/inputs/GamepadInputManager.jsx b/webui/src/controls/inputs/GamepadInputManager.jsx index a9eaf509..db4c3bdf 100644 --- a/webui/src/controls/inputs/GamepadInputManager.jsx +++ b/webui/src/controls/inputs/GamepadInputManager.jsx @@ -11,6 +11,7 @@ import { } from './gamepadBindings.js'; import { subscribeGamepadHub } from './gamepadHub.js'; import { isTextEntryActive } from './inputFocusUtils.js'; +import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js'; const SOURCE = 'gamepad'; const ZERO_VECTOR = { x: 0, y: 0, boost: false }; @@ -58,6 +59,7 @@ export default function GamepadInputManager() { registerInputState, }, } = useControlSystem(); + const dockAssist = useManualDockAssist(); const { value: gamepadSettings, save: saveGamepadSettings } = useSettingsNamespace( 'gamepad', GAMEPAD_SETTINGS_DEFAULTS, @@ -243,6 +245,7 @@ export default function GamepadInputManager() { } if (outputs.buttons.driveMacro && handleButtonEdge('driveMacro', true)) { + dockAssist.exitAssist(); setMode('drive'); runMacro('drive-sequence'); } else if (!outputs.buttons.driveMacro) { @@ -250,8 +253,7 @@ export default function GamepadInputManager() { } if (outputs.buttons.dockMacro && handleButtonEdge('dockMacro', true)) { - setMode('dock'); - runMacro('seek-dock'); + dockAssist.toggleAssist(); } else if (!outputs.buttons.dockMacro) { handleButtonEdge('dockMacro', false); } @@ -292,6 +294,7 @@ export default function GamepadInputManager() { setDriveVector, setMode, toggleNightVision, + dockAssist, ]); return null; diff --git a/webui/src/controls/inputs/KeyboardInputManager.jsx b/webui/src/controls/inputs/KeyboardInputManager.jsx index 1961e232..ece2ba79 100644 --- a/webui/src/controls/inputs/KeyboardInputManager.jsx +++ b/webui/src/controls/inputs/KeyboardInputManager.jsx @@ -9,6 +9,7 @@ import { isKeyboardCaptureLocked } from './keyboardCaptureLock.js'; import { isTextInputElement } from './inputFocusUtils.js'; import { useSettingsNamespace } from '../../settings/index.js'; import { INPUT_SETTINGS_DEFAULTS } from '../../settings/namespaces.js'; +import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js'; import { SONG_DEFAULT_DURATION, SONG_DEFAULT_NOTE, @@ -137,6 +138,7 @@ export default function KeyboardInputManager() { }, } = useControlSystem(); const homeAssistant = useSessionSelector((state) => state.session?.homeAssistant || null); + const dockAssist = useManualDockAssist(); const { homeAssistantSetState } = useSessionActions(); const { focusChat, blurChat, isChatFocused } = useChat(); const { value: inputSettings } = useSettingsNamespace('inputs', INPUT_SETTINGS_DEFAULTS); @@ -372,11 +374,11 @@ export default function KeyboardInputManager() { if (newlyPressed.length > 0) { if (newlyPressed.some((token) => keymap.driveMacro?.has(token))) { + dockAssist.exitAssist(); setMode('drive'); runMacro('drive-sequence'); } else if (newlyPressed.some((token) => keymap.dockMacro?.has(token))) { - setMode('dock'); - runMacro('seek-dock'); + dockAssist.toggleAssist(); } else if (newlyPressed.some((token) => keymap.nightVisionToggle?.has(token))) { toggleNightVision(); } else if (newlyPressed.some((token) => keymap.hornHonk?.has(token))) { @@ -448,6 +450,7 @@ export default function KeyboardInputManager() { startHorn, stopHorn, triggerHomeAssistantCycle, + dockAssist, ]); const latestResetAllRef = useRef(resetAll); diff --git a/webui/src/features/manualDockAssist/useManualDockAssist.js b/webui/src/features/manualDockAssist/useManualDockAssist.js new file mode 100644 index 00000000..ec17a0a4 --- /dev/null +++ b/webui/src/features/manualDockAssist/useManualDockAssist.js @@ -0,0 +1,67 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useControlSystem } from '../../controls/index.js'; +import { useTelemetryFrame } from '../../context/TelemetryContext.jsx'; + +export function useManualDockAssist(options = {}) { + const { manageLifecycle = false } = options; + const { + state: { roverId, manualDockAssist }, + actions, + } = useControlSystem(); + const frame = useTelemetryFrame(roverId); + const sensors = frame?.sensors || {}; + const chargingLabel = sensors?.chargingState?.label || ''; + const docked = Boolean(sensors?.chargingSources?.homeBase); + const charging = docked && chargingLabel.toLowerCase() !== 'not charging' && chargingLabel !== ''; + const active = Boolean(manualDockAssist?.active); + const wasDockedRef = useRef(false); + + const enterAssist = useCallback(() => { + actions.setManualDockAssistActive(true); + }, [actions]); + + const exitAssist = useCallback(() => { + actions.setManualDockAssistActive(false); + }, [actions]); + + const toggleAssist = useCallback(() => { + actions.toggleManualDockAssist(); + }, [actions]); + + useEffect(() => { + if (!manageLifecycle) return; + const justDocked = docked && !wasDockedRef.current; + const justUndocked = !docked && wasDockedRef.current; + if (active && justDocked) { + actions.sendSong([{ note: 84, duration: 6 }], { slot: 1 }); + } else if (active && justUndocked) { + actions.sendSong([{ note: 72, duration: 6 }], { slot: 1 }); + } + wasDockedRef.current = docked; + }, [actions, active, docked, manageLifecycle]); + + useEffect(() => { + if (!manageLifecycle || !active || !charging) return; + exitAssist(); + }, [active, charging, exitAssist, manageLifecycle]); + + const statusLabel = charging ? 'Docked and charging' : docked ? 'Docked' : 'Docking assist active'; + const statusTone = charging ? 'good' : docked ? 'warn' : 'active'; + const visible = active || docked; + + return useMemo( + () => ({ + active, + docked, + charging, + cameraLocked: active, + visible, + statusLabel, + statusTone, + enterAssist, + exitAssist, + toggleAssist, + }), + [active, charging, docked, enterAssist, exitAssist, statusLabel, statusTone, toggleAssist, visible], + ); +} diff --git a/webui/src/help/content.js b/webui/src/help/content.js index f49585f7..72e1f4c3 100644 --- a/webui/src/help/content.js +++ b/webui/src/help/content.js @@ -27,9 +27,10 @@ export const HELP_CONTENT = { type: 'list', title: 'Docking the rover', items: [ - 'Line up the rover to the dock, about a foot away, then:', - { segments: ['Press the "Dock and Charge" button onscreen, or press ', { action: 'dockMacro' }, ' on your keyboard to start docking.'] }, - 'Wait for the rover to confirm it is docked and charging before leaving it unattended.', + 'If the rover shows "Docking in Progress", it is already auto-seeking the dock.', + 'To dock manually, enter Docking Assist from the drive panel.', + { segments: ['Press "Enter Docking Assist", or press ', { action: 'dockMacro' }, '.'] }, + 'In assist mode, camera tilts down and driving speed is limited for precise alignment.', ], }, ], @@ -56,7 +57,7 @@ export const HELP_CONTENT = { title: 'Rover modes & chat', items: [ { action: 'driveMacro', label: 'Drive macro' }, - { action: 'dockMacro', label: 'Dock macro' }, + { action: 'dockMacro', label: 'Docking assist toggle' }, { action: 'chatFocus', label: 'Chat focus' }, ], }, @@ -133,9 +134,9 @@ export const HELP_CONTENT = { type: 'list', title: 'Docking the rover', items: [ - 'Line up the rover to the dock, about a foot away, then:', - { segments: ['Press the "Dock and Charge" button onscreen to start docking.'] }, - 'Wait for the rover to confirm it is docked and charging before leaving it unattended.', + 'If you see "Docking in Progress", the rover is currently auto-seeking the dock.', + { segments: ['For manual docking, press "Enter Docking Assist".'] }, + 'Assist mode tilts camera down and limits speed for precise alignment.', ], } ], @@ -163,9 +164,9 @@ export const HELP_CONTENT = { type: 'list', title: 'Docking the rover', items: [ - 'Line up the rover to the dock, about a foot away, then:', - { segments: ['Press the "Dock and Charge" or the "Dock" button onscreen to start docking.'] }, - 'Wait for the rover to confirm it is docked and charging before leaving it unattended.', + 'If you see "Docking in Progress", the rover is currently auto-seeking the dock.', + { segments: ['For manual docking, press "Enter Docking Assist" (or "Dock" button).'] }, + 'Assist mode tilts camera down and limits speed for precise alignment.', ], } ],