diff --git a/server/config.example.yaml b/server/config.example.yaml index d742fc79..70ed0ff4 100644 --- a/server/config.example.yaml +++ b/server/config.example.yaml @@ -10,6 +10,18 @@ admins: timezone: "America/New_York" +interInstance: + enabled: false + directoryUrls: + - "https://raw.githubusercontent.com/legop3/multi-roomba-rover-instance-directory/refs/heads/main/directory.json" + pollIntervalMs: 30000 + requestTimeoutMs: 5000 + profile: + publicUrl: "https://rover.example.com" + name: "Example Rover Server" + description: "A short public description of this rover server." + color: "#38bdf8" + llmCommentary: enabled: false model: "qwen2.5:7b-instruct" diff --git a/server/index.js b/server/index.js index 5bbe825e..e2f8a7ec 100644 --- a/server/index.js +++ b/server/index.js @@ -30,6 +30,7 @@ require('./src/services/videoAuthService'); require('./src/services/videoSocketService'); require('./src/services/roomCameraService'); require('./src/services/roverSnapshotService'); +require('./src/services/interInstanceService'); require('./src/services/humanAlertButtonService'); require('./src/services/embedHttpService'); require('./src/services/logStreamService'); diff --git a/server/public/assets/index-Buubck0y.js b/server/public/assets/index-Buubck0y.js deleted file mode 100644 index d6cc8ea2..00000000 --- a/server/public/assets/index-Buubck0y.js +++ /dev/null @@ -1,148 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/OrbitControls-BJ-UXxdt.js","assets/three.module--MGUDD-H.js"])))=>i.map(i=>d[i]); -(function(){const c=document.createElement("link").relList;if(c&&c.supports&&c.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))i(u);new MutationObserver(u=>{for(const h of u)if(h.type==="childList")for(const d of h.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&i(d)}).observe(document,{childList:!0,subtree:!0});function n(u){const h={};return u.integrity&&(h.integrity=u.integrity),u.referrerPolicy&&(h.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?h.credentials="include":u.crossOrigin==="anonymous"?h.credentials="omit":h.credentials="same-origin",h}function i(u){if(u.ep)return;u.ep=!0;const h=n(u);fetch(u.href,h)}})();function tv(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var K9={exports:{}},L8={};var ts;function ev(){if(ts)return L8;ts=1;var t=Symbol.for("react.transitional.element"),c=Symbol.for("react.fragment");function n(i,u,h){var d=null;if(h!==void 0&&(d=""+h),u.key!==void 0&&(d=""+u.key),"key"in u){h={};for(var v in u)v!=="key"&&(h[v]=u[v])}else h=u;return u=h.ref,{$$typeof:t,type:i,key:d,ref:u!==void 0?u:null,props:h}}return L8.Fragment=c,L8.jsx=n,L8.jsxs=n,L8}var es;function cv(){return es||(es=1,K9.exports=ev()),K9.exports}var r=cv(),Q9={exports:{}},U1={};var cs;function av(){if(cs)return U1;cs=1;var t=Symbol.for("react.transitional.element"),c=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),u=Symbol.for("react.profiler"),h=Symbol.for("react.consumer"),d=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),g=Symbol.for("react.lazy"),b=Symbol.for("react.activity"),y=Symbol.iterator;function M(T){return T===null||typeof T!="object"?null:(T=y&&T[y]||T["@@iterator"],typeof T=="function"?T:null)}var L={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},S=Object.assign,C={};function B(T,O,K){this.props=T,this.context=O,this.refs=C,this.updater=K||L}B.prototype.isReactComponent={},B.prototype.setState=function(T,O){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,O,"setState")},B.prototype.forceUpdate=function(T){this.updater.enqueueForceUpdate(this,T,"forceUpdate")};function A(){}A.prototype=B.prototype;function H(T,O,K){this.props=T,this.context=O,this.refs=C,this.updater=K||L}var N=H.prototype=new A;N.constructor=H,S(N,B.prototype),N.isPureReactComponent=!0;var _=Array.isArray;function k(){}var F={H:null,A:null,T:null,S:null},V=Object.prototype.hasOwnProperty;function R(T,O,K){var i1=K.ref;return{$$typeof:t,type:T,key:O,ref:i1!==void 0?i1:null,props:K}}function q(T,O){return R(T.type,O,T.props)}function $(T){return typeof T=="object"&&T!==null&&T.$$typeof===t}function D(T){var O={"=":"=0",":":"=2"};return"$"+T.replace(/[=:]/g,function(K){return O[K]})}var W=/\/+/g;function n1(T,O){return typeof T=="object"&&T!==null&&T.key!=null?D(""+T.key):O.toString(36)}function Y(T){switch(T.status){case"fulfilled":return T.value;case"rejected":throw T.reason;default:switch(typeof T.status=="string"?T.then(k,k):(T.status="pending",T.then(function(O){T.status==="pending"&&(T.status="fulfilled",T.value=O)},function(O){T.status==="pending"&&(T.status="rejected",T.reason=O)})),T.status){case"fulfilled":return T.value;case"rejected":throw T.reason}}throw T}function E(T,O,K,i1,m1){var v1=typeof T;(v1==="undefined"||v1==="boolean")&&(T=null);var S1=!1;if(T===null)S1=!0;else switch(v1){case"bigint":case"string":case"number":S1=!0;break;case"object":switch(T.$$typeof){case t:case c:S1=!0;break;case g:return S1=T._init,E(S1(T._payload),O,K,i1,m1)}}if(S1)return m1=m1(T),S1=i1===""?"."+n1(T,0):i1,_(m1)?(K="",S1!=null&&(K=S1.replace(W,"$&/")+"/"),E(m1,O,K,"",function(E1){return E1})):m1!=null&&($(m1)&&(m1=q(m1,K+(m1.key==null||T&&T.key===m1.key?"":(""+m1.key).replace(W,"$&/")+"/")+S1)),O.push(m1)),1;S1=0;var k1=i1===""?".":i1+":";if(_(T))for(var F1=0;F1>>1,f1=E[e1];if(0>>1;e1u(K,Z))i1u(m1,K)?(E[e1]=m1,E[i1]=Z,e1=i1):(E[e1]=K,E[O]=Z,e1=O);else if(i1u(m1,Z))E[e1]=m1,E[i1]=Z,e1=i1;else break t}}return I}function u(E,I){var Z=E.sortIndex-I.sortIndex;return Z!==0?Z:E.id-I.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var h=performance;t.unstable_now=function(){return h.now()}}else{var d=Date,v=d.now();t.unstable_now=function(){return d.now()-v}}var p=[],x=[],g=1,b=null,y=3,M=!1,L=!1,S=!1,C=!1,B=typeof setTimeout=="function"?setTimeout:null,A=typeof clearTimeout=="function"?clearTimeout:null,H=typeof setImmediate<"u"?setImmediate:null;function N(E){for(var I=n(x);I!==null;){if(I.callback===null)i(x);else if(I.startTime<=E)i(x),I.sortIndex=I.expirationTime,c(p,I);else break;I=n(x)}}function _(E){if(S=!1,N(E),!L)if(n(p)!==null)L=!0,k||(k=!0,D());else{var I=n(x);I!==null&&Y(_,I.startTime-E)}}var k=!1,F=-1,V=5,R=-1;function q(){return C?!0:!(t.unstable_now()-RE&&q());){var e1=b.callback;if(typeof e1=="function"){b.callback=null,y=b.priorityLevel;var f1=e1(b.expirationTime<=E);if(E=t.unstable_now(),typeof f1=="function"){b.callback=f1,N(E),I=!0;break e}b===n(p)&&i(p),N(E)}else i(p);b=n(p)}if(b!==null)I=!0;else{var T=n(x);T!==null&&Y(_,T.startTime-E),I=!1}}break t}finally{b=null,y=Z,M=!1}I=void 0}}finally{I?D():k=!1}}}var D;if(typeof H=="function")D=function(){H($)};else if(typeof MessageChannel<"u"){var W=new MessageChannel,n1=W.port2;W.port1.onmessage=$,D=function(){n1.postMessage(null)}}else D=function(){B($,0)};function Y(E,I){F=B(function(){E(t.unstable_now())},I)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(E){E.callback=null},t.unstable_forceFrameRate=function(E){0>E||125e1?(E.sortIndex=Z,c(x,E),n(p)===null&&E===n(x)&&(S?(A(F),F=-1):S=!0,Y(_,Z-e1))):(E.sortIndex=f1,c(p,E),L||M||(L=!0,k||(k=!0,D()))),E},t.unstable_shouldYield=q,t.unstable_wrapCallback=function(E){var I=y;return function(){var Z=y;y=I;try{return E.apply(this,arguments)}finally{y=Z}}}})(J9)),J9}var rs;function rv(){return rs||(rs=1,W9.exports=nv()),W9.exports}var tc={exports:{}},J2={};var ls;function lv(){if(ls)return J2;ls=1;var t=pa();function c(p){var x="https://react.dev/errors/"+p;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(c){console.error(c)}}return t(),tc.exports=lv(),tc.exports}var ss;function iv(){if(ss)return V8;ss=1;var t=rv(),c=pa(),n=du();function i(e){var a="https://react.dev/errors/"+e;if(1f1||(e.current=e1[f1],e1[f1]=null,f1--)}function K(e,a){f1++,e1[f1]=e.current,e.current=a}var i1=T(null),m1=T(null),v1=T(null),S1=T(null);function k1(e,a){switch(K(v1,a),K(m1,e),K(i1,null),a.nodeType){case 9:case 11:e=(e=a.documentElement)&&(e=e.namespaceURI)?wi(e):0;break;default:if(e=a.tagName,a=a.namespaceURI)a=wi(a),e=Ci(a,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}O(i1),K(i1,e)}function F1(){O(i1),O(m1),O(v1)}function E1(e){e.memoizedState!==null&&K(S1,e);var a=i1.current,l=Ci(a,e.type);a!==l&&(K(m1,e),K(i1,l))}function Q1(e){m1.current===e&&(O(i1),O(m1)),S1.current===e&&(O(S1),C8._currentValue=Z)}var P1,u2;function l1(e){if(P1===void 0)try{throw Error()}catch(l){var a=l.stack.trim().match(/\n( *(at )?)/);P1=a&&a[1]||"",u2=-1)":-1f||U[o]!==X[f]){var r1=` -`+U[o].replace(" at new "," at ");return e.displayName&&r1.includes("")&&(r1=r1.replace("",e.displayName)),r1}while(1<=o&&0<=f);break}}}finally{L1=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?l1(l):""}function O1(e,a){switch(e.tag){case 26:case 27:case 5:return l1(e.type);case 16:return l1("Lazy");case 13:return e.child!==a&&a!==null?l1("Suspense Fallback"):l1("Suspense");case 19:return l1("SuspenseList");case 0:case 15:return $1(e.type,!1);case 11:return $1(e.type.render,!1);case 1:return $1(e.type,!0);case 31:return l1("Activity");default:return""}}function b1(e){try{var a="",l=null;do a+=O1(e,l),l=e,e=e.return;while(e);return a}catch(o){return` -Error generating stack: `+o.message+` -`+o.stack}}var A1=Object.prototype.hasOwnProperty,x1=t.unstable_scheduleCallback,C1=t.unstable_cancelCallback,g1=t.unstable_shouldYield,T1=t.unstable_requestPaint,a1=t.unstable_now,y1=t.unstable_getCurrentPriorityLevel,M1=t.unstable_ImmediatePriority,N1=t.unstable_UserBlockingPriority,K1=t.unstable_NormalPriority,N2=t.unstable_LowPriority,J1=t.unstable_IdlePriority,p1=t.log,t1=t.unstable_setDisableYieldValue,h1=null,w1=null;function q1(e){if(typeof p1=="function"&&t1(e),w1&&typeof w1.setStrictMode=="function")try{w1.setStrictMode(h1,e)}catch{}}var z1=Math.clz32?Math.clz32:t2,D1=Math.log,s2=Math.LN2;function t2(e){return e>>>=0,e===0?32:31-(D1(e)/s2|0)|0}var e2=256,U2=262144,W2=4194304;function I2(e){var a=e&42;if(a!==0)return a;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 w2(e,a,l){var o=e.pendingLanes;if(o===0)return 0;var f=0,z=e.suspendedLanes,w=e.pingedLanes;e=e.warmLanes;var j=o&134217727;return j!==0?(o=j&~z,o!==0?f=I2(o):(w&=j,w!==0?f=I2(w):l||(l=j&~e,l!==0&&(f=I2(l))))):(j=o&~z,j!==0?f=I2(j):w!==0?f=I2(w):l||(l=o&~e,l!==0&&(f=I2(l)))),f===0?0:a!==0&&a!==f&&(a&z)===0&&(z=f&-f,l=a&-a,z>=l||z===32&&(l&4194048)!==0)?a:f}function F4(e,a){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&a)===0}function Dd(e,a){switch(e){case 1:case 2:case 4:case 8:case 64:return a+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 a+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 an(){var e=W2;return W2<<=1,(W2&62914560)===0&&(W2=4194304),e}function Te(e){for(var a=[],l=0;31>l;l++)a.push(e);return a}function _0(e,a){e.pendingLanes|=a,a!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Od(e,a,l,o,f,z){var w=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var j=e.entanglements,U=e.expirationTimes,X=e.hiddenUpdates;for(l=w&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Gd=/[\n"\\]/g;function x4(e){return e.replace(Gd,function(a){return"\\"+a.charCodeAt(0).toString(16)+" "})}function Ie(e,a,l,o,f,z,w,j){e.name="",w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"?e.type=w:e.removeAttribute("type"),a!=null?w==="number"?(a===0&&e.value===""||e.value!=a)&&(e.value=""+g4(a)):e.value!==""+g4(a)&&(e.value=""+g4(a)):w!=="submit"&&w!=="reset"||e.removeAttribute("value"),a!=null?Pe(e,w,g4(a)):l!=null?Pe(e,w,g4(l)):o!=null&&e.removeAttribute("value"),f==null&&z!=null&&(e.defaultChecked=!!z),f!=null&&(e.checked=f&&typeof f!="function"&&typeof f!="symbol"),j!=null&&typeof j!="function"&&typeof j!="symbol"&&typeof j!="boolean"?e.name=""+g4(j):e.removeAttribute("name")}function gn(e,a,l,o,f,z,w,j){if(z!=null&&typeof z!="function"&&typeof z!="symbol"&&typeof z!="boolean"&&(e.type=z),a!=null||l!=null){if(!(z!=="submit"&&z!=="reset"||a!=null)){Ue(e);return}l=l!=null?""+g4(l):"",a=a!=null?""+g4(a):l,j||a===e.value||(e.value=a),e.defaultValue=a}o=o??f,o=typeof o!="function"&&typeof o!="symbol"&&!!o,e.checked=j?e.checked:!!o,e.defaultChecked=!!o,w!=null&&typeof w!="function"&&typeof w!="symbol"&&typeof w!="boolean"&&(e.name=w),Ue(e)}function Pe(e,a,l){a==="number"&&u5(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function j6(e,a,l,o){if(e=e.options,a){a={};for(var f=0;f"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ke=!1;if(W4)try{var U0={};Object.defineProperty(U0,"passive",{get:function(){Ke=!0}}),window.addEventListener("test",U0,U0),window.removeEventListener("test",U0,U0)}catch{Ke=!1}var w3=null,Qe=null,d5=null;function Cn(){if(d5)return d5;var e,a=Qe,l=a.length,o,f="value"in w3?w3.value:w3.textContent,z=f.length;for(e=0;e=$0),Bn=" ",Nn=!1;function jn(e,a){switch(e){case"keyup":return Mf.indexOf(a.keyCode)!==-1;case"keydown":return a.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function kn(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var E6=!1;function Cf(e,a){switch(e){case"compositionend":return kn(a);case"keypress":return a.which!==32?null:(Nn=!0,Bn);case"textInput":return e=a.data,e===Bn&&Nn?null:e;default:return null}}function Sf(e,a){if(E6)return e==="compositionend"||!e7&&jn(e,a)?(e=Cn(),d5=Qe=w3=null,E6=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(a.ctrlKey||a.altKey||a.metaKey)||a.ctrlKey&&a.altKey){if(a.char&&1=a)return{node:l,offset:a-e};e=o}t:{for(;l;){if(l.nextSibling){l=l.nextSibling;break t}l=l.parentNode}l=void 0}l=On(l)}}function In(e,a){return e&&a?e===a?!0:e&&e.nodeType===3?!1:a&&a.nodeType===3?In(e,a.parentNode):"contains"in e?e.contains(a):e.compareDocumentPosition?!!(e.compareDocumentPosition(a)&16):!1:!1}function Pn(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var a=u5(e.document);a instanceof e.HTMLIFrameElement;){try{var l=typeof a.contentWindow.location.href=="string"}catch{l=!1}if(l)e=a.contentWindow;else break;a=u5(e.document)}return a}function n7(e){var a=e&&e.nodeName&&e.nodeName.toLowerCase();return a&&(a==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||a==="textarea"||e.contentEditable==="true")}var kf=W4&&"documentMode"in document&&11>=document.documentMode,T6=null,r7=null,K0=null,l7=!1;function $n(e,a,l){var o=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;l7||T6==null||T6!==u5(o)||(o=T6,"selectionStart"in o&&n7(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),K0&&Y0(K0,o)||(K0=o,o=rt(r7,"onSelect"),0>=w,f-=w,U4=1<<32-z1(a)+f|l<G1?(a2=V1,V1=null):a2=V1.sibling;var l2=J(G,V1,Q[G1],o1);if(l2===null){V1===null&&(V1=a2);break}e&&V1&&l2.alternate===null&&a(G,V1),P=z(l2,P,G1),r2===null?j1=l2:r2.sibling=l2,r2=l2,V1=a2}if(G1===Q.length)return l(G,V1),n2&&t3(G,G1),j1;if(V1===null){for(;G1G1?(a2=V1,V1=null):a2=V1.sibling;var $3=J(G,V1,l2.value,o1);if($3===null){V1===null&&(V1=a2);break}e&&V1&&$3.alternate===null&&a(G,V1),P=z($3,P,G1),r2===null?j1=$3:r2.sibling=$3,r2=$3,V1=a2}if(l2.done)return l(G,V1),n2&&t3(G,G1),j1;if(V1===null){for(;!l2.done;G1++,l2=Q.next())l2=u1(G,l2.value,o1),l2!==null&&(P=z(l2,P,G1),r2===null?j1=l2:r2.sibling=l2,r2=l2);return n2&&t3(G,G1),j1}for(V1=o(V1);!l2.done;G1++,l2=Q.next())l2=c1(V1,G,G1,l2.value,o1),l2!==null&&(e&&l2.alternate!==null&&V1.delete(l2.key===null?G1:l2.key),P=z(l2,P,G1),r2===null?j1=l2:r2.sibling=l2,r2=l2);return e&&V1.forEach(function(Jm){return a(G,Jm)}),n2&&t3(G,G1),j1}function v2(G,P,Q,o1){if(typeof Q=="object"&&Q!==null&&Q.type===S&&Q.key===null&&(Q=Q.props.children),typeof Q=="object"&&Q!==null){switch(Q.$$typeof){case M:t:{for(var j1=Q.key;P!==null;){if(P.key===j1){if(j1=Q.type,j1===S){if(P.tag===7){l(G,P.sibling),o1=f(P,Q.props.children),o1.return=G,G=o1;break t}}else if(P.elementType===j1||typeof j1=="object"&&j1!==null&&j1.$$typeof===V&&s6(j1)===P.type){l(G,P.sibling),o1=f(P,Q.props),e8(o1,Q),o1.return=G,G=o1;break t}l(G,P);break}else a(G,P);P=P.sibling}Q.type===S?(o1=a6(Q.props.children,G.mode,o1,Q.key),o1.return=G,G=o1):(o1=M5(Q.type,Q.key,Q.props,null,G.mode,o1),e8(o1,Q),o1.return=G,G=o1)}return w(G);case L:t:{for(j1=Q.key;P!==null;){if(P.key===j1)if(P.tag===4&&P.stateNode.containerInfo===Q.containerInfo&&P.stateNode.implementation===Q.implementation){l(G,P.sibling),o1=f(P,Q.children||[]),o1.return=G,G=o1;break t}else{l(G,P);break}else a(G,P);P=P.sibling}o1=f7(Q,G.mode,o1),o1.return=G,G=o1}return w(G);case V:return Q=s6(Q),v2(G,P,Q,o1)}if(Y(Q))return H1(G,P,Q,o1);if(D(Q)){if(j1=D(Q),typeof j1!="function")throw Error(i(150));return Q=j1.call(Q),R1(G,P,Q,o1)}if(typeof Q.then=="function")return v2(G,P,V5(Q),o1);if(Q.$$typeof===H)return v2(G,P,S5(G,Q),o1);B5(G,Q)}return typeof Q=="string"&&Q!==""||typeof Q=="number"||typeof Q=="bigint"?(Q=""+Q,P!==null&&P.tag===6?(l(G,P.sibling),o1=f(P,Q),o1.return=G,G=o1):(l(G,P),o1=d7(Q,G.mode,o1),o1.return=G,G=o1),w(G)):l(G,P)}return function(G,P,Q,o1){try{t8=0;var j1=v2(G,P,Q,o1);return Y6=null,j1}catch(V1){if(V1===G6||V1===A5)throw V1;var r2=h4(29,V1,null,G.mode);return r2.lanes=o1,r2.return=G,r2}finally{}}}var u6=mr(!0),vr=mr(!1),L3=!1;function S7(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function H7(e,a){e=e.updateQueue,a.updateQueue===e&&(a.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function V3(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function B3(e,a,l){var o=e.updateQueue;if(o===null)return null;if(o=o.shared,(o2&2)!==0){var f=o.pending;return f===null?a.next=a:(a.next=f.next,f.next=a),o.pending=a,a=y5(e),Wn(e,null,l),a}return b5(e,o,a,l),y5(e)}function c8(e,a,l){if(a=a.updateQueue,a!==null&&(a=a.shared,(l&4194048)!==0)){var o=a.lanes;o&=e.pendingLanes,l|=o,a.lanes=l,rn(e,l)}}function A7(e,a){var l=e.updateQueue,o=e.alternate;if(o!==null&&(o=o.updateQueue,l===o)){var f=null,z=null;if(l=l.firstBaseUpdate,l!==null){do{var w={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};z===null?f=z=w:z=z.next=w,l=l.next}while(l!==null);z===null?f=z=a:z=z.next=a}else f=z=a;l={baseState:o.baseState,firstBaseUpdate:f,lastBaseUpdate:z,shared:o.shared,callbacks:o.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=a:e.next=a,l.lastBaseUpdate=a}var L7=!1;function a8(){if(L7){var e=Z6;if(e!==null)throw e}}function n8(e,a,l,o){L7=!1;var f=e.updateQueue;L3=!1;var z=f.firstBaseUpdate,w=f.lastBaseUpdate,j=f.shared.pending;if(j!==null){f.shared.pending=null;var U=j,X=U.next;U.next=null,w===null?z=X:w.next=X,w=U;var r1=e.alternate;r1!==null&&(r1=r1.updateQueue,j=r1.lastBaseUpdate,j!==w&&(j===null?r1.firstBaseUpdate=X:j.next=X,r1.lastBaseUpdate=U))}if(z!==null){var u1=f.baseState;w=0,r1=X=U=null,j=z;do{var J=j.lane&-536870913,c1=J!==j.lane;if(c1?(c2&J)===J:(o&J)===J){J!==0&&J===$6&&(L7=!0),r1!==null&&(r1=r1.next={lane:0,tag:j.tag,payload:j.payload,callback:null,next:null});t:{var H1=e,R1=j;J=a;var v2=l;switch(R1.tag){case 1:if(H1=R1.payload,typeof H1=="function"){u1=H1.call(v2,u1,J);break t}u1=H1;break t;case 3:H1.flags=H1.flags&-65537|128;case 0:if(H1=R1.payload,J=typeof H1=="function"?H1.call(v2,u1,J):H1,J==null)break t;u1=b({},u1,J);break t;case 2:L3=!0}}J=j.callback,J!==null&&(e.flags|=64,c1&&(e.flags|=8192),c1=f.callbacks,c1===null?f.callbacks=[J]:c1.push(J))}else c1={lane:J,tag:j.tag,payload:j.payload,callback:j.callback,next:null},r1===null?(X=r1=c1,U=u1):r1=r1.next=c1,w|=J;if(j=j.next,j===null){if(j=f.shared.pending,j===null)break;c1=j,j=c1.next,c1.next=null,f.lastBaseUpdate=c1,f.shared.pending=null}}while(!0);r1===null&&(U=u1),f.baseState=U,f.firstBaseUpdate=X,f.lastBaseUpdate=r1,z===null&&(f.shared.lanes=0),R3|=w,e.lanes=w,e.memoizedState=u1}}function pr(e,a){if(typeof e!="function")throw Error(i(191,e));e.call(a)}function gr(e,a){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ez?z:8;var w=E.T,j={};E.T=j,G7(e,!1,a,l);try{var U=f(),X=E.S;if(X!==null&&X(j,U),U!==null&&typeof U=="object"&&typeof U.then=="function"){var r1=Uf(U,o);i8(e,a,r1,p4(e))}else i8(e,a,o,p4(e))}catch(u1){i8(e,a,{then:function(){},status:"rejected",reason:u1},p4())}finally{I.p=z,w!==null&&j.types!==null&&(w.types=j.types),E.T=w}}function Yf(){}function $7(e,a,l,o){if(e.tag!==5)throw Error(i(476));var f=Kr(e).queue;Yr(e,f,a,Z,l===null?Yf:function(){return Qr(e),l(o)})}function Kr(e){var a=e.memoizedState;if(a!==null)return a;a={memoizedState:Z,baseState:Z,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:n3,lastRenderedState:Z},next:null};var l={};return a.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:n3,lastRenderedState:l},next:null},e.memoizedState=a,e=e.alternate,e!==null&&(e.memoizedState=a),a}function Qr(e){var a=Kr(e);a.next===null&&(a=e.alternate.memoizedState),i8(e,a.next.queue,{},p4())}function Z7(){return K2(C8)}function Xr(){return L2().memoizedState}function Wr(){return L2().memoizedState}function Kf(e){for(var a=e.return;a!==null;){switch(a.tag){case 24:case 3:var l=p4();e=V3(l);var o=B3(a,e,l);o!==null&&(s4(o,a,l),c8(o,a,l)),a={cache:y7()},e.payload=a;return}a=a.return}}function Qf(e,a,l){var o=p4();l={lane:o,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},D5(e)?tl(a,l):(l=u7(e,a,l,o),l!==null&&(s4(l,e,o),el(l,a,o)))}function Jr(e,a,l){var o=p4();i8(e,a,l,o)}function i8(e,a,l,o){var f={lane:o,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(D5(e))tl(a,f);else{var z=e.alternate;if(e.lanes===0&&(z===null||z.lanes===0)&&(z=a.lastRenderedReducer,z!==null))try{var w=a.lastRenderedState,j=z(w,l);if(f.hasEagerState=!0,f.eagerState=j,u4(j,w))return b5(e,a,f,0),p2===null&&z5(),!1}catch{}finally{}if(l=u7(e,a,f,o),l!==null)return s4(l,e,o),el(l,a,o),!0}return!1}function G7(e,a,l,o){if(o={lane:2,revertLane:S9(),gesture:null,action:o,hasEagerState:!1,eagerState:null,next:null},D5(e)){if(a)throw Error(i(479))}else a=u7(e,l,o,2),a!==null&&s4(a,e,2)}function D5(e){var a=e.alternate;return e===Z1||a!==null&&a===Z1}function tl(e,a){Q6=k5=!0;var l=e.pending;l===null?a.next=a:(a.next=l.next,l.next=a),e.pending=a}function el(e,a,l){if((l&4194048)!==0){var o=a.lanes;o&=e.pendingLanes,l|=o,a.lanes=l,rn(e,l)}}var s8={readContext:K2,use:E5,useCallback:C2,useContext:C2,useEffect:C2,useImperativeHandle:C2,useLayoutEffect:C2,useInsertionEffect:C2,useMemo:C2,useReducer:C2,useRef:C2,useState:C2,useDebugValue:C2,useDeferredValue:C2,useTransition:C2,useSyncExternalStore:C2,useId:C2,useHostTransitionStatus:C2,useFormState:C2,useActionState:C2,useOptimistic:C2,useMemoCache:C2,useCacheRefresh:C2};s8.useEffectEvent=C2;var cl={readContext:K2,use:E5,useCallback:function(e,a){return e4().memoizedState=[e,a===void 0?null:a],e},useContext:K2,useEffect:qr,useImperativeHandle:function(e,a,l){l=l!=null?l.concat([e]):null,_5(4194308,4,Ir.bind(null,a,e),l)},useLayoutEffect:function(e,a){return _5(4194308,4,e,a)},useInsertionEffect:function(e,a){_5(4,2,e,a)},useMemo:function(e,a){var l=e4();a=a===void 0?null:a;var o=e();if(h6){q1(!0);try{e()}finally{q1(!1)}}return l.memoizedState=[o,a],o},useReducer:function(e,a,l){var o=e4();if(l!==void 0){var f=l(a);if(h6){q1(!0);try{l(a)}finally{q1(!1)}}}else f=a;return o.memoizedState=o.baseState=f,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:f},o.queue=e,e=e.dispatch=Qf.bind(null,Z1,e),[o.memoizedState,e]},useRef:function(e){var a=e4();return e={current:e},a.memoizedState=e},useState:function(e){e=D7(e);var a=e.queue,l=Jr.bind(null,Z1,a);return a.dispatch=l,[e.memoizedState,l]},useDebugValue:I7,useDeferredValue:function(e,a){var l=e4();return P7(l,e,a)},useTransition:function(){var e=D7(!1);return e=Yr.bind(null,Z1,e.queue,!0,!1),e4().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,a,l){var o=Z1,f=e4();if(n2){if(l===void 0)throw Error(i(407));l=l()}else{if(l=a(),p2===null)throw Error(i(349));(c2&127)!==0||wr(o,a,l)}f.memoizedState=l;var z={value:l,getSnapshot:a};return f.queue=z,qr(Sr.bind(null,o,z,e),[e]),o.flags|=2048,W6(9,{destroy:void 0},Cr.bind(null,o,z,l,a),null),l},useId:function(){var e=e4(),a=p2.identifierPrefix;if(n2){var l=I4,o=U4;l=(o&~(1<<32-z1(o)-1)).toString(32)+l,a="_"+a+"R_"+l,l=F5++,0<\/script>",z=z.removeChild(z.firstChild);break;case"select":z=typeof o.is=="string"?w.createElement("select",{is:o.is}):w.createElement("select"),o.multiple?z.multiple=!0:o.size&&(z.size=o.size);break;default:z=typeof o.is=="string"?w.createElement(f,{is:o.is}):w.createElement(f)}}z[G2]=a,z[c4]=o;t:for(w=a.child;w!==null;){if(w.tag===5||w.tag===6)z.appendChild(w.stateNode);else if(w.tag!==4&&w.tag!==27&&w.child!==null){w.child.return=w,w=w.child;continue}if(w===a)break t;for(;w.sibling===null;){if(w.return===null||w.return===a)break t;w=w.return}w.sibling.return=w.return,w=w.sibling}a.stateNode=z;t:switch(X2(z,f,o),f){case"button":case"input":case"select":case"textarea":o=!!o.autoFocus;break t;case"img":o=!0;break t;default:o=!1}o&&l3(a)}}return x2(a),i9(a,a.type,e===null?null:e.memoizedProps,a.pendingProps,l),null;case 6:if(e&&a.stateNode!=null)e.memoizedProps!==o&&l3(a);else{if(typeof o!="string"&&a.stateNode===null)throw Error(i(166));if(e=v1.current,I6(a)){if(e=a.stateNode,l=a.memoizedProps,o=null,f=Y2,f!==null)switch(f.tag){case 27:case 5:o=f.memoizedProps}e[G2]=a,e=!!(e.nodeValue===l||o!==null&&o.suppressHydrationWarning===!0||yi(e.nodeValue,l)),e||H3(a,!0)}else e=lt(e).createTextNode(o),e[G2]=a,a.stateNode=e}return x2(a),null;case 31:if(l=a.memoizedState,e===null||e.memoizedState!==null){if(o=I6(a),l!==null){if(e===null){if(!o)throw Error(i(318));if(e=a.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(i(557));e[G2]=a}else n6(),(a.flags&128)===0&&(a.memoizedState=null),a.flags|=4;x2(a),e=!1}else l=g7(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return a.flags&256?(f4(a),a):(f4(a),null);if((a.flags&128)!==0)throw Error(i(558))}return x2(a),null;case 13:if(o=a.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(f=I6(a),o!==null&&o.dehydrated!==null){if(e===null){if(!f)throw Error(i(318));if(f=a.memoizedState,f=f!==null?f.dehydrated:null,!f)throw Error(i(317));f[G2]=a}else n6(),(a.flags&128)===0&&(a.memoizedState=null),a.flags|=4;x2(a),f=!1}else f=g7(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=f),f=!0;if(!f)return a.flags&256?(f4(a),a):(f4(a),null)}return f4(a),(a.flags&128)!==0?(a.lanes=l,a):(l=o!==null,e=e!==null&&e.memoizedState!==null,l&&(o=a.child,f=null,o.alternate!==null&&o.alternate.memoizedState!==null&&o.alternate.memoizedState.cachePool!==null&&(f=o.alternate.memoizedState.cachePool.pool),z=null,o.memoizedState!==null&&o.memoizedState.cachePool!==null&&(z=o.memoizedState.cachePool.pool),z!==f&&(o.flags|=2048)),l!==e&&l&&(a.child.flags|=8192),$5(a,a.updateQueue),x2(a),null);case 4:return F1(),e===null&&V9(a.stateNode.containerInfo),x2(a),null;case 10:return c3(a.type),x2(a),null;case 19:if(O(A2),o=a.memoizedState,o===null)return x2(a),null;if(f=(a.flags&128)!==0,z=o.rendering,z===null)if(f)u8(o,!1);else{if(S2!==0||e!==null&&(e.flags&128)!==0)for(e=a.child;e!==null;){if(z=j5(e),z!==null){for(a.flags|=128,u8(o,!1),e=z.updateQueue,a.updateQueue=e,$5(a,e),a.subtreeFlags=0,e=l,l=a.child;l!==null;)Jn(l,e),l=l.sibling;return K(A2,A2.current&1|2),n2&&t3(a,o.treeForkCount),a.child}e=e.sibling}o.tail!==null&&a1()>Q5&&(a.flags|=128,f=!0,u8(o,!1),a.lanes=4194304)}else{if(!f)if(e=j5(z),e!==null){if(a.flags|=128,f=!0,e=e.updateQueue,a.updateQueue=e,$5(a,e),u8(o,!0),o.tail===null&&o.tailMode==="hidden"&&!z.alternate&&!n2)return x2(a),null}else 2*a1()-o.renderingStartTime>Q5&&l!==536870912&&(a.flags|=128,f=!0,u8(o,!1),a.lanes=4194304);o.isBackwards?(z.sibling=a.child,a.child=z):(e=o.last,e!==null?e.sibling=z:a.child=z,o.last=z)}return o.tail!==null?(e=o.tail,o.rendering=e,o.tail=e.sibling,o.renderingStartTime=a1(),e.sibling=null,l=A2.current,K(A2,f?l&1|2:l&1),n2&&t3(a,o.treeForkCount),e):(x2(a),null);case 22:case 23:return f4(a),B7(),o=a.memoizedState!==null,e!==null?e.memoizedState!==null!==o&&(a.flags|=8192):o&&(a.flags|=8192),o?(l&536870912)!==0&&(a.flags&128)===0&&(x2(a),a.subtreeFlags&6&&(a.flags|=8192)):x2(a),l=a.updateQueue,l!==null&&$5(a,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),o=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(o=a.memoizedState.cachePool.pool),o!==l&&(a.flags|=2048),e!==null&&O(i6),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),a.memoizedState.cache!==l&&(a.flags|=2048),c3(j2),x2(a),null;case 25:return null;case 30:return null}throw Error(i(156,a.tag))}function em(e,a){switch(v7(a),a.tag){case 1:return e=a.flags,e&65536?(a.flags=e&-65537|128,a):null;case 3:return c3(j2),F1(),e=a.flags,(e&65536)!==0&&(e&128)===0?(a.flags=e&-65537|128,a):null;case 26:case 27:case 5:return Q1(a),null;case 31:if(a.memoizedState!==null){if(f4(a),a.alternate===null)throw Error(i(340));n6()}return e=a.flags,e&65536?(a.flags=e&-65537|128,a):null;case 13:if(f4(a),e=a.memoizedState,e!==null&&e.dehydrated!==null){if(a.alternate===null)throw Error(i(340));n6()}return e=a.flags,e&65536?(a.flags=e&-65537|128,a):null;case 19:return O(A2),null;case 4:return F1(),null;case 10:return c3(a.type),null;case 22:case 23:return f4(a),B7(),e!==null&&O(i6),e=a.flags,e&65536?(a.flags=e&-65537|128,a):null;case 24:return c3(j2),null;case 25:return null;default:return null}}function Hl(e,a){switch(v7(a),a.tag){case 3:c3(j2),F1();break;case 26:case 27:case 5:Q1(a);break;case 4:F1();break;case 31:a.memoizedState!==null&&f4(a);break;case 13:f4(a);break;case 19:O(A2);break;case 10:c3(a.type);break;case 22:case 23:f4(a),B7(),e!==null&&O(i6);break;case 24:c3(j2)}}function h8(e,a){try{var l=a.updateQueue,o=l!==null?l.lastEffect:null;if(o!==null){var f=o.next;l=f;do{if((l.tag&e)===e){o=void 0;var z=l.create,w=l.inst;o=z(),w.destroy=o}l=l.next}while(l!==f)}}catch(j){d2(a,a.return,j)}}function k3(e,a,l){try{var o=a.updateQueue,f=o!==null?o.lastEffect:null;if(f!==null){var z=f.next;o=z;do{if((o.tag&e)===e){var w=o.inst,j=w.destroy;if(j!==void 0){w.destroy=void 0,f=a;var U=l,X=j;try{X()}catch(r1){d2(f,U,r1)}}}o=o.next}while(o!==z)}}catch(r1){d2(a,a.return,r1)}}function Al(e){var a=e.updateQueue;if(a!==null){var l=e.stateNode;try{gr(a,l)}catch(o){d2(e,e.return,o)}}}function Ll(e,a,l){l.props=d6(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(o){d2(e,a,o)}}function d8(e,a){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var o=e.stateNode;break;case 30:o=e.stateNode;break;default:o=e.stateNode}typeof l=="function"?e.refCleanup=l(o):l.current=o}}catch(f){d2(e,a,f)}}function P4(e,a){var l=e.ref,o=e.refCleanup;if(l!==null)if(typeof o=="function")try{o()}catch(f){d2(e,a,f)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(f){d2(e,a,f)}else l.current=null}function Vl(e){var a=e.type,l=e.memoizedProps,o=e.stateNode;try{t:switch(a){case"button":case"input":case"select":case"textarea":l.autoFocus&&o.focus();break t;case"img":l.src?o.src=l.src:l.srcSet&&(o.srcset=l.srcSet)}}catch(f){d2(e,e.return,f)}}function s9(e,a,l){try{var o=e.stateNode;wm(o,e.type,l,a),o[c4]=a}catch(f){d2(e,e.return,f)}}function Bl(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&D3(e.type)||e.tag===4}function o9(e){t:for(;;){for(;e.sibling===null;){if(e.return===null||Bl(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&&D3(e.type)||e.flags&2||e.child===null||e.tag===4)continue t;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function u9(e,a,l){var o=e.tag;if(o===5||o===6)e=e.stateNode,a?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,a):(a=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,a.appendChild(e),l=l._reactRootContainer,l!=null||a.onclick!==null||(a.onclick=X4));else if(o!==4&&(o===27&&D3(e.type)&&(l=e.stateNode,a=null),e=e.child,e!==null))for(u9(e,a,l),e=e.sibling;e!==null;)u9(e,a,l),e=e.sibling}function Z5(e,a,l){var o=e.tag;if(o===5||o===6)e=e.stateNode,a?l.insertBefore(e,a):l.appendChild(e);else if(o!==4&&(o===27&&D3(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Z5(e,a,l),e=e.sibling;e!==null;)Z5(e,a,l),e=e.sibling}function Nl(e){var a=e.stateNode,l=e.memoizedProps;try{for(var o=e.type,f=a.attributes;f.length;)a.removeAttributeNode(f[0]);X2(a,o,l),a[G2]=e,a[c4]=l}catch(z){d2(e,e.return,z)}}var i3=!1,R2=!1,h9=!1,jl=typeof WeakSet=="function"?WeakSet:Set,$2=null;function cm(e,a){if(e=e.containerInfo,j9=ft,e=Pn(e),n7(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else t:{l=(l=e.ownerDocument)&&l.defaultView||window;var o=l.getSelection&&l.getSelection();if(o&&o.rangeCount!==0){l=o.anchorNode;var f=o.anchorOffset,z=o.focusNode;o=o.focusOffset;try{l.nodeType,z.nodeType}catch{l=null;break t}var w=0,j=-1,U=-1,X=0,r1=0,u1=e,J=null;e:for(;;){for(var c1;u1!==l||f!==0&&u1.nodeType!==3||(j=w+f),u1!==z||o!==0&&u1.nodeType!==3||(U=w+o),u1.nodeType===3&&(w+=u1.nodeValue.length),(c1=u1.firstChild)!==null;)J=u1,u1=c1;for(;;){if(u1===e)break e;if(J===l&&++X===f&&(j=w),J===z&&++r1===o&&(U=w),(c1=u1.nextSibling)!==null)break;u1=J,J=u1.parentNode}u1=c1}l=j===-1||U===-1?null:{start:j,end:U}}else l=null}l=l||{start:0,end:0}}else l=null;for(k9={focusedElem:e,selectionRange:l},ft=!1,$2=a;$2!==null;)if(a=$2,e=a.child,(a.subtreeFlags&1028)!==0&&e!==null)e.return=a,$2=e;else for(;$2!==null;){switch(a=$2,z=a.alternate,e=a.flags,a.tag){case 0:if((e&4)!==0&&(e=a.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),X2(z,o,l),z[G2]=e,P2(z),o=z;break t;case"link":var w=_i("link","href",f).get(o+(l.href||""));if(w){for(var j=0;jv2&&(w=v2,v2=R1,R1=w);var G=Un(j,R1),P=Un(j,v2);if(G&&P&&(c1.rangeCount!==1||c1.anchorNode!==G.node||c1.anchorOffset!==G.offset||c1.focusNode!==P.node||c1.focusOffset!==P.offset)){var Q=u1.createRange();Q.setStart(G.node,G.offset),c1.removeAllRanges(),R1>v2?(c1.addRange(Q),c1.extend(P.node,P.offset)):(Q.setEnd(P.node,P.offset),c1.addRange(Q))}}}}for(u1=[],c1=j;c1=c1.parentNode;)c1.nodeType===1&&u1.push({element:c1,left:c1.scrollLeft,top:c1.scrollTop});for(typeof j.focus=="function"&&j.focus(),j=0;jl?32:l,E.T=null,l=x9,x9=null;var z=T3,w=d3;if(T2=0,a0=T3=null,d3=0,(o2&6)!==0)throw Error(i(331));var j=o2;if(o2|=4,Il(z.current),Dl(z,z.current,w,l),o2=j,x8(0,!1),w1&&typeof w1.onPostCommitFiberRoot=="function")try{w1.onPostCommitFiberRoot(h1,z)}catch{}return!0}finally{I.p=f,E.T=o,ii(e,a)}}function oi(e,a,l){a=b4(l,a),a=X7(e.stateNode,a,2),e=B3(e,a,2),e!==null&&(_0(e,2),$4(e))}function d2(e,a,l){if(e.tag===3)oi(e,e,l);else for(;a!==null;){if(a.tag===3){oi(a,e,l);break}else if(a.tag===1){var o=a.stateNode;if(typeof a.type.getDerivedStateFromError=="function"||typeof o.componentDidCatch=="function"&&(E3===null||!E3.has(o))){e=b4(l,e),l=ul(2),o=B3(a,l,2),o!==null&&(hl(l,o,a,e),_0(o,2),$4(o));break}}a=a.return}}function M9(e,a,l){var o=e.pingCache;if(o===null){o=e.pingCache=new rm;var f=new Set;o.set(a,f)}else f=o.get(a),f===void 0&&(f=new Set,o.set(a,f));f.has(l)||(m9=!0,f.add(l),e=um.bind(null,e,a,l),a.then(e,e))}function um(e,a,l){var o=e.pingCache;o!==null&&o.delete(a),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,p2===e&&(c2&l)===l&&(S2===4||S2===3&&(c2&62914560)===c2&&300>a1()-K5?(o2&2)===0&&n0(e,0):v9|=l,c0===c2&&(c0=0)),$4(e)}function ui(e,a){a===0&&(a=an()),e=c6(e,a),e!==null&&(_0(e,a),$4(e))}function hm(e){var a=e.memoizedState,l=0;a!==null&&(l=a.retryLane),ui(e,l)}function dm(e,a){var l=0;switch(e.tag){case 31:case 13:var o=e.stateNode,f=e.memoizedState;f!==null&&(l=f.retryLane);break;case 19:o=e.stateNode;break;case 22:o=e.stateNode._retryCache;break;default:throw Error(i(314))}o!==null&&o.delete(a),ui(e,l)}function fm(e,a){return x1(e,a)}var ct=null,l0=null,w9=!1,at=!1,C9=!1,q3=0;function $4(e){e!==l0&&e.next===null&&(l0===null?ct=l0=e:l0=l0.next=e),at=!0,w9||(w9=!0,vm())}function x8(e,a){if(!C9&&at){C9=!0;do for(var l=!1,o=ct;o!==null;){if(e!==0){var f=o.pendingLanes;if(f===0)var z=0;else{var w=o.suspendedLanes,j=o.pingedLanes;z=(1<<31-z1(42|e)+1)-1,z&=f&~(w&~j),z=z&201326741?z&201326741|1:z?z|2:0}z!==0&&(l=!0,mi(o,z))}else z=c2,z=w2(o,o===p2?z:0,o.cancelPendingCommit!==null||o.timeoutHandle!==-1),(z&3)===0||F4(o,z)||(l=!0,mi(o,z));o=o.next}while(l);C9=!1}}function mm(){hi()}function hi(){at=w9=!1;var e=0;q3!==0&&Sm()&&(e=q3);for(var a=a1(),l=null,o=ct;o!==null;){var f=o.next,z=di(o,a);z===0?(o.next=null,l===null?ct=f:l.next=f,f===null&&(l0=l)):(l=o,(e!==0||(z&3)!==0)&&(at=!0)),o=f}T2!==0&&T2!==5||x8(e),q3!==0&&(q3=0)}function di(e,a){for(var l=e.suspendedLanes,o=e.pingedLanes,f=e.expirationTimes,z=e.pendingLanes&-62914561;0j)break;var r1=U.transferSize,u1=U.initiatorType;r1&&Mi(u1)&&(U=U.responseEnd,w+=r1*(U"u"?null:document;function Fi(e,a,l){var o=i0;if(o&&typeof a=="string"&&a){var f=x4(a);f='link[rel="'+e+'"][href="'+f+'"]',typeof l=="string"&&(f+='[crossorigin="'+l+'"]'),ki.has(f)||(ki.add(f),e={rel:e,crossOrigin:l,href:a},o.querySelector(f)===null&&(a=o.createElement("link"),X2(a,"link",e),P2(a),o.head.appendChild(a)))}}function Fm(e){f3.D(e),Fi("dns-prefetch",e,null)}function Rm(e,a){f3.C(e,a),Fi("preconnect",e,a)}function Em(e,a,l){f3.L(e,a,l);var o=i0;if(o&&e&&a){var f='link[rel="preload"][as="'+x4(a)+'"]';a==="image"&&l&&l.imageSrcSet?(f+='[imagesrcset="'+x4(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(f+='[imagesizes="'+x4(l.imageSizes)+'"]')):f+='[href="'+x4(e)+'"]';var z=f;switch(a){case"style":z=s0(e);break;case"script":z=o0(e)}H4.has(z)||(e=b({rel:"preload",href:a==="image"&&l&&l.imageSrcSet?void 0:e,as:a},l),H4.set(z,e),o.querySelector(f)!==null||a==="style"&&o.querySelector(M8(z))||a==="script"&&o.querySelector(w8(z))||(a=o.createElement("link"),X2(a,"link",e),P2(a),o.head.appendChild(a)))}}function Tm(e,a){f3.m(e,a);var l=i0;if(l&&e){var o=a&&typeof a.as=="string"?a.as:"script",f='link[rel="modulepreload"][as="'+x4(o)+'"][href="'+x4(e)+'"]',z=f;switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":z=o0(e)}if(!H4.has(z)&&(e=b({rel:"modulepreload",href:e},a),H4.set(z,e),l.querySelector(f)===null)){switch(o){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(w8(z)))return}o=l.createElement("link"),X2(o,"link",e),P2(o),l.head.appendChild(o)}}}function _m(e,a,l){f3.S(e,a,l);var o=i0;if(o&&e){var f=B6(o).hoistableStyles,z=s0(e);a=a||"default";var w=f.get(z);if(!w){var j={loading:0,preload:null};if(w=o.querySelector(M8(z)))j.loading=5;else{e=b({rel:"stylesheet",href:e,"data-precedence":a},l),(l=H4.get(z))&&D9(e,l);var U=w=o.createElement("link");P2(U),X2(U,"link",e),U._p=new Promise(function(X,r1){U.onload=X,U.onerror=r1}),U.addEventListener("load",function(){j.loading|=1}),U.addEventListener("error",function(){j.loading|=2}),j.loading|=4,st(w,a,o)}w={type:"stylesheet",instance:w,count:1,state:j},f.set(z,w)}}}function qm(e,a){f3.X(e,a);var l=i0;if(l&&e){var o=B6(l).hoistableScripts,f=o0(e),z=o.get(f);z||(z=l.querySelector(w8(f)),z||(e=b({src:e,async:!0},a),(a=H4.get(f))&&O9(e,a),z=l.createElement("script"),P2(z),X2(z,"link",e),l.head.appendChild(z)),z={type:"script",instance:z,count:1,state:null},o.set(f,z))}}function Dm(e,a){f3.M(e,a);var l=i0;if(l&&e){var o=B6(l).hoistableScripts,f=o0(e),z=o.get(f);z||(z=l.querySelector(w8(f)),z||(e=b({src:e,async:!0,type:"module"},a),(a=H4.get(f))&&O9(e,a),z=l.createElement("script"),P2(z),X2(z,"link",e),l.head.appendChild(z)),z={type:"script",instance:z,count:1,state:null},o.set(f,z))}}function Ri(e,a,l,o){var f=(f=v1.current)?it(f):null;if(!f)throw Error(i(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(a=s0(l.href),l=B6(f).hoistableStyles,o=l.get(a),o||(o={type:"style",instance:null,count:0,state:null},l.set(a,o)),o):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=s0(l.href);var z=B6(f).hoistableStyles,w=z.get(e);if(w||(f=f.ownerDocument||f,w={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},z.set(e,w),(z=f.querySelector(M8(e)))&&!z._p&&(w.instance=z,w.state.loading=5),H4.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},H4.set(e,l),z||Om(f,e,l,w.state))),a&&o===null)throw Error(i(528,""));return w}if(a&&o!==null)throw Error(i(529,""));return null;case"script":return a=l.async,l=l.src,typeof l=="string"&&a&&typeof a!="function"&&typeof a!="symbol"?(a=o0(l),l=B6(f).hoistableScripts,o=l.get(a),o||(o={type:"script",instance:null,count:0,state:null},l.set(a,o)),o):{type:"void",instance:null,count:0,state:null};default:throw Error(i(444,e))}}function s0(e){return'href="'+x4(e)+'"'}function M8(e){return'link[rel="stylesheet"]['+e+"]"}function Ei(e){return b({},e,{"data-precedence":e.precedence,precedence:null})}function Om(e,a,l,o){e.querySelector('link[rel="preload"][as="style"]['+a+"]")?o.loading=1:(a=e.createElement("link"),o.preload=a,a.addEventListener("load",function(){return o.loading|=1}),a.addEventListener("error",function(){return o.loading|=2}),X2(a,"link",l),P2(a),e.head.appendChild(a))}function o0(e){return'[src="'+x4(e)+'"]'}function w8(e){return"script[async]"+e}function Ti(e,a,l){if(a.count++,a.instance===null)switch(a.type){case"style":var o=e.querySelector('style[data-href~="'+x4(l.href)+'"]');if(o)return a.instance=o,P2(o),o;var f=b({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return o=(e.ownerDocument||e).createElement("style"),P2(o),X2(o,"style",f),st(o,l.precedence,e),a.instance=o;case"stylesheet":f=s0(l.href);var z=e.querySelector(M8(f));if(z)return a.state.loading|=4,a.instance=z,P2(z),z;o=Ei(l),(f=H4.get(f))&&D9(o,f),z=(e.ownerDocument||e).createElement("link"),P2(z);var w=z;return w._p=new Promise(function(j,U){w.onload=j,w.onerror=U}),X2(z,"link",o),a.state.loading|=4,st(z,l.precedence,e),a.instance=z;case"script":return z=o0(l.src),(f=e.querySelector(w8(z)))?(a.instance=f,P2(f),f):(o=l,(f=H4.get(z))&&(o=b({},l),O9(o,f)),e=e.ownerDocument||e,f=e.createElement("script"),P2(f),X2(f,"link",o),e.head.appendChild(f),a.instance=f);case"void":return null;default:throw Error(i(443,a.type))}else a.type==="stylesheet"&&(a.state.loading&4)===0&&(o=a.instance,a.state.loading|=4,st(o,l.precedence,e));return a.instance}function st(e,a,l){for(var o=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),f=o.length?o[o.length-1]:null,z=f,w=0;w title"):null)}function Um(e,a,l){if(l===1||a.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof a.precedence!="string"||typeof a.href!="string"||a.href==="")break;return!0;case"link":if(typeof a.rel!="string"||typeof a.href!="string"||a.href===""||a.onLoad||a.onError)break;switch(a.rel){case"stylesheet":return e=a.disabled,typeof a.precedence=="string"&&e==null;default:return!0}case"script":if(a.async&&typeof a.async!="function"&&typeof a.async!="symbol"&&!a.onLoad&&!a.onError&&a.src&&typeof a.src=="string")return!0}return!1}function Di(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function Im(e,a,l,o){if(l.type==="stylesheet"&&(typeof o.media!="string"||matchMedia(o.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var f=s0(o.href),z=a.querySelector(M8(f));if(z){a=z._p,a!==null&&typeof a=="object"&&typeof a.then=="function"&&(e.count++,e=ut.bind(e),a.then(e,e)),l.state.loading|=4,l.instance=z,P2(z);return}z=a.ownerDocument||a,o=Ei(o),(f=H4.get(f))&&D9(o,f),z=z.createElement("link"),P2(z);var w=z;w._p=new Promise(function(j,U){w.onload=j,w.onerror=U}),X2(z,"link",o),l.instance=z}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,a),(a=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=ut.bind(e),a.addEventListener("load",l),a.addEventListener("error",l))}}var U9=0;function Pm(e,a){return e.stylesheets&&e.count===0&&dt(e,e.stylesheets),0U9?50:800)+a);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(o),clearTimeout(f)}}:null}function ut(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)dt(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var ht=null;function dt(e,a){e.stylesheets=null,e.unsuspend!==null&&(e.count++,ht=new Map,a.forEach($m,e),ht=null,ut.call(e))}function $m(e,a){if(!(a.state.loading&4)){var l=ht.get(e);if(l)var o=l.get(null);else{l=new Map,ht.set(e,l);for(var f=e.querySelectorAll("link[data-precedence],style[data-precedence]"),z=0;z"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(c){console.error(c)}}return t(),X9.exports=iv(),X9.exports}var ov=sv();var us="popstate";function uv(t={}){function c(i,u){let{pathname:h,search:d,hash:v}=i.location;return _c("",{pathname:h,search:d,hash:v},u.state&&u.state.usr||null,u.state&&u.state.key||"default")}function n(i,u){return typeof u=="string"?u:$8(u)}return dv(c,n,null,t)}function y2(t,c){if(t===!1||t===null||typeof t>"u")throw new Error(c)}function O4(t,c){if(!t){typeof console<"u"&&console.warn(c);try{throw new Error(c)}catch{}}}function hv(){return Math.random().toString(36).substring(2,10)}function hs(t,c){return{usr:t.state,key:t.key,idx:c}}function _c(t,c,n=null,i){return{pathname:typeof t=="string"?t:t.pathname,search:"",hash:"",...typeof c=="string"?V0(c):c,state:n,key:c&&c.key||i||hv()}}function $8({pathname:t="/",search:c="",hash:n=""}){return c&&c!=="?"&&(t+=c.charAt(0)==="?"?c:"?"+c),n&&n!=="#"&&(t+=n.charAt(0)==="#"?n:"#"+n),t}function V0(t){let c={};if(t){let n=t.indexOf("#");n>=0&&(c.hash=t.substring(n),t=t.substring(0,n));let i=t.indexOf("?");i>=0&&(c.search=t.substring(i),t=t.substring(0,i)),t&&(c.pathname=t)}return c}function dv(t,c,n,i={}){let{window:u=document.defaultView,v5Compat:h=!1}=i,d=u.history,v="POP",p=null,x=g();x==null&&(x=0,d.replaceState({...d.state,idx:x},""));function g(){return(d.state||{idx:null}).idx}function b(){v="POP";let C=g(),B=C==null?null:C-x;x=C,p&&p({action:v,location:S.location,delta:B})}function y(C,B){v="PUSH";let A=_c(S.location,C,B);x=g()+1;let H=hs(A,x),N=S.createHref(A);try{d.pushState(H,"",N)}catch(_){if(_ instanceof DOMException&&_.name==="DataCloneError")throw _;u.location.assign(N)}h&&p&&p({action:v,location:S.location,delta:1})}function M(C,B){v="REPLACE";let A=_c(S.location,C,B);x=g();let H=hs(A,x),N=S.createHref(A);d.replaceState(H,"",N),h&&p&&p({action:v,location:S.location,delta:0})}function L(C){return fv(C)}let S={get action(){return v},get location(){return t(u,d)},listen(C){if(p)throw new Error("A history only accepts one active listener");return u.addEventListener(us,b),p=C,()=>{u.removeEventListener(us,b),p=null}},createHref(C){return c(u,C)},createURL:L,encodeLocation(C){let B=L(C);return{pathname:B.pathname,search:B.search,hash:B.hash}},push:y,replace:M,go(C){return d.go(C)}};return S}function fv(t,c=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),y2(n,"No window.location.(origin|href) available to create URL");let i=typeof t=="string"?t:$8(t);return i=i.replace(/ $/,"%20"),!c&&i.startsWith("//")&&(i=n+i),new URL(i,n)}function fu(t,c,n="/"){return mv(t,c,n,!1)}function mv(t,c,n,i){let u=typeof c=="string"?V0(c):c,h=z3(u.pathname||"/",n);if(h==null)return null;let d=mu(t);vv(d);let v=null;for(let p=0;v==null&&p{let g={relativePath:x===void 0?d.path||"":x,caseSensitive:d.caseSensitive===!0,childrenIndex:v,route:d};if(g.relativePath.startsWith("/")){if(!g.relativePath.startsWith(i)&&p)return;y2(g.relativePath.startsWith(i),`Absolute route path "${g.relativePath}" nested under path "${i}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),g.relativePath=g.relativePath.slice(i.length)}let b=g3([i,g.relativePath]),y=n.concat(g);d.children&&d.children.length>0&&(y2(d.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${b}".`),mu(d.children,c,y,b,p)),!(d.path==null&&!d.index)&&c.push({path:b,score:Mv(b,d.index),routesMeta:y})};return t.forEach((d,v)=>{if(d.path===""||!d.path?.includes("?"))h(d,v);else for(let p of vu(d.path))h(d,v,!0,p)}),c}function vu(t){let c=t.split("/");if(c.length===0)return[];let[n,...i]=c,u=n.endsWith("?"),h=n.replace(/\?$/,"");if(i.length===0)return u?[h,""]:[h];let d=vu(i.join("/")),v=[];return v.push(...d.map(p=>p===""?h:[h,p].join("/"))),u&&v.push(...d),v.map(p=>t.startsWith("/")&&p===""?"/":p)}function vv(t){t.sort((c,n)=>c.score!==n.score?n.score-c.score:wv(c.routesMeta.map(i=>i.childrenIndex),n.routesMeta.map(i=>i.childrenIndex)))}var pv=/^:[\w-]+$/,gv=3,xv=2,zv=1,bv=10,yv=-2,ds=t=>t==="*";function Mv(t,c){let n=t.split("/"),i=n.length;return n.some(ds)&&(i+=yv),c&&(i+=xv),n.filter(u=>!ds(u)).reduce((u,h)=>u+(pv.test(h)?gv:h===""?zv:bv),i)}function wv(t,c){return t.length===c.length&&t.slice(0,-1).every((i,u)=>i===c[u])?t[t.length-1]-c[c.length-1]:0}function Cv(t,c,n=!1){let{routesMeta:i}=t,u={},h="/",d=[];for(let v=0;v{if(g==="*"){let L=v[y]||"";d=h.slice(0,h.length-L.length).replace(/(.)\/+$/,"$1")}const M=v[y];return b&&!M?x[g]=void 0:x[g]=(M||"").replace(/%2F/g,"/"),x},{}),pathname:h,pathnameBase:d,pattern:t}}function Sv(t,c=!1,n=!0){O4(t==="*"||!t.endsWith("*")||t.endsWith("/*"),`Route path "${t}" will be treated as if it were "${t.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${t.replace(/\*$/,"/*")}".`);let i=[],u="^"+t.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(d,v,p)=>(i.push({paramName:v,isOptional:p!=null}),p?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return t.endsWith("*")?(i.push({paramName:"*"}),u+=t==="*"||t==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?u+="\\/*$":t!==""&&t!=="/"&&(u+="(?:(?=\\/|$))"),[new RegExp(u,c?void 0:"i"),i]}function Hv(t){try{return t.split("/").map(c=>decodeURIComponent(c).replace(/\//g,"%2F")).join("/")}catch(c){return O4(!1,`The URL path "${t}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${c}).`),t}}function z3(t,c){if(c==="/")return t;if(!t.toLowerCase().startsWith(c.toLowerCase()))return null;let n=c.endsWith("/")?c.length-1:c.length,i=t.charAt(n);return i&&i!=="/"?null:t.slice(n)||"/"}var Av=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Lv=t=>Av.test(t);function Vv(t,c="/"){let{pathname:n,search:i="",hash:u=""}=typeof t=="string"?V0(t):t,h;if(n)if(Lv(n))h=n;else{if(n.includes("//")){let d=n;n=n.replace(/\/\/+/g,"/"),O4(!1,`Pathnames cannot have embedded double slashes - normalizing ${d} -> ${n}`)}n.startsWith("/")?h=fs(n.substring(1),"/"):h=fs(n,c)}else h=c;return{pathname:h,search:jv(i),hash:kv(u)}}function fs(t,c){let n=c.replace(/\/+$/,"").split("/");return t.split("/").forEach(u=>{u===".."?n.length>1&&n.pop():u!=="."&&n.push(u)}),n.length>1?n.join("/"):"/"}function ec(t,c,n,i){return`Cannot include a '${t}' character in a manually specified \`to.${c}\` field [${JSON.stringify(i)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Bv(t){return t.filter((c,n)=>n===0||c.route.path&&c.route.path.length>0)}function pu(t){let c=Bv(t);return c.map((n,i)=>i===c.length-1?n.pathname:n.pathnameBase)}function gu(t,c,n,i=!1){let u;typeof t=="string"?u=V0(t):(u={...t},y2(!u.pathname||!u.pathname.includes("?"),ec("?","pathname","search",u)),y2(!u.pathname||!u.pathname.includes("#"),ec("#","pathname","hash",u)),y2(!u.search||!u.search.includes("#"),ec("#","search","hash",u)));let h=t===""||u.pathname==="",d=h?"/":u.pathname,v;if(d==null)v=n;else{let b=c.length-1;if(!i&&d.startsWith("..")){let y=d.split("/");for(;y[0]==="..";)y.shift(),b-=1;u.pathname=y.join("/")}v=b>=0?c[b]:"/"}let p=Vv(u,v),x=d&&d!=="/"&&d.endsWith("/"),g=(h||d===".")&&n.endsWith("/");return!p.pathname.endsWith("/")&&(x||g)&&(p.pathname+="/"),p}var g3=t=>t.join("/").replace(/\/\/+/g,"/"),Nv=t=>t.replace(/\/+$/,"").replace(/^\/*/,"/"),jv=t=>!t||t==="?"?"":t.startsWith("?")?t:"?"+t,kv=t=>!t||t==="#"?"":t.startsWith("#")?t:"#"+t;function Fv(t){return t!=null&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.internal=="boolean"&&"data"in t}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var xu=["POST","PUT","PATCH","DELETE"];new Set(xu);var Rv=["GET",...xu];new Set(Rv);var B0=m.createContext(null);B0.displayName="DataRouter";var be=m.createContext(null);be.displayName="DataRouterState";m.createContext(!1);var zu=m.createContext({isTransitioning:!1});zu.displayName="ViewTransition";var Ev=m.createContext(new Map);Ev.displayName="Fetchers";var Tv=m.createContext(null);Tv.displayName="Await";var Y4=m.createContext(null);Y4.displayName="Navigation";var t5=m.createContext(null);t5.displayName="Location";var b3=m.createContext({outlet:null,matches:[],isDataRoute:!1});b3.displayName="Route";var ga=m.createContext(null);ga.displayName="RouteError";function _v(t,{relative:c}={}){y2(e5(),"useHref() may be used only in the context of a component.");let{basename:n,navigator:i}=m.useContext(Y4),{hash:u,pathname:h,search:d}=c5(t,{relative:c}),v=h;return n!=="/"&&(v=h==="/"?n:g3([n,h])),i.createHref({pathname:v,search:d,hash:u})}function e5(){return m.useContext(t5)!=null}function K3(){return y2(e5(),"useLocation() may be used only in the context of a component."),m.useContext(t5).location}var bu="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function yu(t){m.useContext(Y4).static||m.useLayoutEffect(t)}function qv(){let{isDataRoute:t}=m.useContext(b3);return t?Wv():Dv()}function Dv(){y2(e5(),"useNavigate() may be used only in the context of a component.");let t=m.useContext(B0),{basename:c,navigator:n}=m.useContext(Y4),{matches:i}=m.useContext(b3),{pathname:u}=K3(),h=JSON.stringify(pu(i)),d=m.useRef(!1);return yu(()=>{d.current=!0}),m.useCallback((p,x={})=>{if(O4(d.current,bu),!d.current)return;if(typeof p=="number"){n.go(p);return}let g=gu(p,JSON.parse(h),u,x.relative==="path");t==null&&c!=="/"&&(g.pathname=g.pathname==="/"?c:g3([c,g.pathname])),(x.replace?n.replace:n.push)(g,x.state,x)},[c,n,h,u,t])}m.createContext(null);function c5(t,{relative:c}={}){let{matches:n}=m.useContext(b3),{pathname:i}=K3(),u=JSON.stringify(pu(n));return m.useMemo(()=>gu(t,JSON.parse(u),i,c==="path"),[t,u,i,c])}function Ov(t,c){return Mu(t,c)}function Mu(t,c,n,i,u){y2(e5(),"useRoutes() may be used only in the context of a component.");let{navigator:h}=m.useContext(Y4),{matches:d}=m.useContext(b3),v=d[d.length-1],p=v?v.params:{},x=v?v.pathname:"/",g=v?v.pathnameBase:"/",b=v&&v.route;{let A=b&&b.path||"";wu(x,!b||A.endsWith("*")||A.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 y=K3(),M;if(c){let A=typeof c=="string"?V0(c):c;y2(g==="/"||A.pathname?.startsWith(g),`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 "${g}" but pathname "${A.pathname}" was given in the \`location\` prop.`),M=A}else M=y;let L=M.pathname||"/",S=L;if(g!=="/"){let A=g.replace(/^\//,"").split("/");S="/"+L.replace(/^\//,"").split("/").slice(A.length).join("/")}let C=fu(t,{pathname:S});O4(b||C!=null,`No routes matched location "${M.pathname}${M.search}${M.hash}" `),O4(C==null||C[C.length-1].route.element!==void 0||C[C.length-1].route.Component!==void 0||C[C.length-1].route.lazy!==void 0,`Matched leaf route at location "${M.pathname}${M.search}${M.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 B=Zv(C&&C.map(A=>Object.assign({},A,{params:Object.assign({},p,A.params),pathname:g3([g,h.encodeLocation?h.encodeLocation(A.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:A.pathname]),pathnameBase:A.pathnameBase==="/"?g:g3([g,h.encodeLocation?h.encodeLocation(A.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:A.pathnameBase])})),d,n,i,u);return c&&B?m.createElement(t5.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...M},navigationType:"POP"}},B):B}function Uv(){let t=Xv(),c=Fv(t)?`${t.status} ${t.statusText}`:t instanceof Error?t.message:JSON.stringify(t),n=t instanceof Error?t.stack:null,i="rgba(200,200,200, 0.5)",u={padding:"0.5rem",backgroundColor:i},h={padding:"2px 4px",backgroundColor:i},d=null;return console.error("Error handled by React Router default ErrorBoundary:",t),d=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:h},"ErrorBoundary")," or"," ",m.createElement("code",{style:h},"errorElement")," prop on your route.")),m.createElement(m.Fragment,null,m.createElement("h2",null,"Unexpected Application Error!"),m.createElement("h3",{style:{fontStyle:"italic"}},c),n?m.createElement("pre",{style:u},n):null,d)}var Iv=m.createElement(Uv,null),Pv=class extends m.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,c){return c.location!==t.location||c.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:c.error,location:c.location,revalidation:t.revalidation||c.revalidation}}componentDidCatch(t,c){this.props.onError?this.props.onError(t,c):console.error("React Router caught the following error during render",t)}render(){return this.state.error!==void 0?m.createElement(b3.Provider,{value:this.props.routeContext},m.createElement(ga.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function $v({routeContext:t,match:c,children:n}){let i=m.useContext(B0);return i&&i.static&&i.staticContext&&(c.route.errorElement||c.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=c.route.id),m.createElement(b3.Provider,{value:t},n)}function Zv(t,c=[],n=null,i=null,u=null){if(t==null){if(!n)return null;if(n.errors)t=n.matches;else if(c.length===0&&!n.initialized&&n.matches.length>0)t=n.matches;else return null}let h=t,d=n?.errors;if(d!=null){let g=h.findIndex(b=>b.route.id&&d?.[b.route.id]!==void 0);y2(g>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(d).join(",")}`),h=h.slice(0,Math.min(h.length,g+1))}let v=!1,p=-1;if(n)for(let g=0;g=0?h=h.slice(0,p+1):h=[h[0]];break}}}let x=n&&i?(g,b)=>{i(g,{location:n.location,params:n.matches?.[0]?.params??{},errorInfo:b})}:void 0;return h.reduceRight((g,b,y)=>{let M,L=!1,S=null,C=null;n&&(M=d&&b.route.id?d[b.route.id]:void 0,S=b.route.errorElement||Iv,v&&(p<0&&y===0?(wu("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),L=!0,C=null):p===y&&(L=!0,C=b.route.hydrateFallbackElement||null)));let B=c.concat(h.slice(0,y+1)),A=()=>{let H;return M?H=S:L?H=C:b.route.Component?H=m.createElement(b.route.Component,null):b.route.element?H=b.route.element:H=g,m.createElement($v,{match:b,routeContext:{outlet:g,matches:B,isDataRoute:n!=null},children:H})};return n&&(b.route.ErrorBoundary||b.route.errorElement||y===0)?m.createElement(Pv,{location:n.location,revalidation:n.revalidation,component:S,error:M,children:A(),routeContext:{outlet:null,matches:B,isDataRoute:!0},onError:x}):A()},null)}function xa(t){return`${t} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Gv(t){let c=m.useContext(B0);return y2(c,xa(t)),c}function Yv(t){let c=m.useContext(be);return y2(c,xa(t)),c}function Kv(t){let c=m.useContext(b3);return y2(c,xa(t)),c}function za(t){let c=Kv(t),n=c.matches[c.matches.length-1];return y2(n.route.id,`${t} can only be used on routes that contain a unique "id"`),n.route.id}function Qv(){return za("useRouteId")}function Xv(){let t=m.useContext(ga),c=Yv("useRouteError"),n=za("useRouteError");return t!==void 0?t:c.errors?.[n]}function Wv(){let{router:t}=Gv("useNavigate"),c=za("useNavigate"),n=m.useRef(!1);return yu(()=>{n.current=!0}),m.useCallback(async(u,h={})=>{O4(n.current,bu),n.current&&(typeof u=="number"?t.navigate(u):await t.navigate(u,{fromRouteId:c,...h}))},[t,c])}var ms={};function wu(t,c,n){!c&&!ms[t]&&(ms[t]=!0,O4(!1,n))}m.memo(Jv);function Jv({routes:t,future:c,state:n,unstable_onError:i}){return Mu(t,void 0,n,i,c)}function z6(t){y2(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function tp({basename:t="/",children:c=null,location:n,navigationType:i="POP",navigator:u,static:h=!1}){y2(!e5(),"You cannot render a inside another . You should never have more than one in your app.");let d=t.replace(/^\/*/,"/"),v=m.useMemo(()=>({basename:d,navigator:u,static:h,future:{}}),[d,u,h]);typeof n=="string"&&(n=V0(n));let{pathname:p="/",search:x="",hash:g="",state:b=null,key:y="default"}=n,M=m.useMemo(()=>{let L=z3(p,d);return L==null?null:{location:{pathname:L,search:x,hash:g,state:b,key:y},navigationType:i}},[d,p,x,g,b,y,i]);return O4(M!=null,` is not able to match the URL "${p}${x}${g}" because it does not start with the basename, so the won't render anything.`),M==null?null:m.createElement(Y4.Provider,{value:v},m.createElement(t5.Provider,{children:c,value:M}))}function ep({children:t,location:c}){return Ov(qc(t),c)}function qc(t,c=[]){let n=[];return m.Children.forEach(t,(i,u)=>{if(!m.isValidElement(i))return;let h=[...c,u];if(i.type===m.Fragment){n.push.apply(n,qc(i.props.children,h));return}y2(i.type===z6,`[${typeof i.type=="string"?i.type:i.type.name}] is not a component. All component children of must be a or `),y2(!i.props.index||!i.props.children,"An index route cannot have child routes.");let d={id:i.props.id||h.join("-"),caseSensitive:i.props.caseSensitive,element:i.props.element,Component:i.props.Component,index:i.props.index,path:i.props.path,middleware:i.props.middleware,loader:i.props.loader,action:i.props.action,hydrateFallbackElement:i.props.hydrateFallbackElement,HydrateFallback:i.props.HydrateFallback,errorElement:i.props.errorElement,ErrorBoundary:i.props.ErrorBoundary,hasErrorBoundary:i.props.hasErrorBoundary===!0||i.props.ErrorBoundary!=null||i.props.errorElement!=null,shouldRevalidate:i.props.shouldRevalidate,handle:i.props.handle,lazy:i.props.lazy};i.props.children&&(d.children=qc(i.props.children,h)),n.push(d)}),n}var Ot="get",Ut="application/x-www-form-urlencoded";function ye(t){return t!=null&&typeof t.tagName=="string"}function cp(t){return ye(t)&&t.tagName.toLowerCase()==="button"}function ap(t){return ye(t)&&t.tagName.toLowerCase()==="form"}function np(t){return ye(t)&&t.tagName.toLowerCase()==="input"}function rp(t){return!!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)}function lp(t,c){return t.button===0&&(!c||c==="_self")&&!rp(t)}var bt=null;function ip(){if(bt===null)try{new FormData(document.createElement("form"),0),bt=!1}catch{bt=!0}return bt}var sp=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function cc(t){return t!=null&&!sp.has(t)?(O4(!1,`"${t}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Ut}"`),null):t}function op(t,c){let n,i,u,h,d;if(ap(t)){let v=t.getAttribute("action");i=v?z3(v,c):null,n=t.getAttribute("method")||Ot,u=cc(t.getAttribute("enctype"))||Ut,h=new FormData(t)}else if(cp(t)||np(t)&&(t.type==="submit"||t.type==="image")){let v=t.form;if(v==null)throw new Error('Cannot submit a + + {features.length ? ( +
+ {features.map((feature) => ( + + {feature} + + ))} +
+ ) : ( +

No advertised feature flags.

+ )} + + + + ); +} + +function RemoteMediaStrip({ remote }) { + const roverSnapshots = (remote.roster || []) + .map((rover) => ({ + id: rover.id, + name: rover.name || rover.id, + url: rover.snapshots?.latestUrl, + updatedAt: rover.snapshots?.updatedAt, + })) + .filter((entry) => entry.url); + const roomCameras = Array.isArray(remote.roomCameras) ? remote.roomCameras.filter((camera) => camera.snapshotUrl) : []; + const items = [ + ...roverSnapshots.map((entry) => ({ ...entry, kind: 'Rover' })), + ...roomCameras.map((entry) => ({ ...entry, name: entry.name || entry.id, url: entry.snapshotUrl, kind: 'Room' })), + ]; + if (!items.length) return null; + return ( +
+ {items.map((item) => ( +
+ {item.name} +
+ {item.name} + {item.kind} +
+
+ ))} +
+ ); +} + +export function ExternalInstancesCompact() { + const [expanded, setExpanded] = useState(false); + const [popupOpen, setPopupOpen] = useState(false); + const enabled = useInterInstanceEnabled(); + const instances = useRemoteInstances(); + const visible = useMemo(() => instances.filter((remote) => remote?.online || remote?.url), [instances]); + if (!enabled) return null; + if (!visible.length) return null; + return ( +
+
+ + +
+ {expanded ? ( +
+ {visible.map((remote) => + remote.online ? ( + + ) : ( + + ), + )} +
+ ) : null} + {popupOpen ? setPopupOpen(false)} /> : null} +
+ ); +} + +export function InterInstancePopup({ onClose }) { + const enabled = useInterInstanceEnabled(); + if (!enabled) return null; + return ( +
+ + Close + + } + className="w-full max-w-6xl" + bodyClassName="max-h-[82vh] overflow-y-auto p-0.5" + clipOverflow={false} + > + + +
+ ); +} + +export default function InterInstancePanel({ compact = false, centered = false }) { + const enabled = useInterInstanceEnabled(); + const instances = useRemoteInstances(); + if (!enabled) return null; + if (compact) return ; + if (!instances.length) { + return ( + +

No external instances discovered.

+
+ ); + } + return ( +
+ {instances.map((remote) => ( +
+ + {remote.online ? ( + <> + + + + ) : ( + + )} +
+ ))} +
+ ); +} diff --git a/webui/src/components/ModeGateOverlay/index.jsx b/webui/src/components/ModeGateOverlay/index.jsx index 9dbf0714..e903cc3c 100644 --- a/webui/src/components/ModeGateOverlay/index.jsx +++ b/webui/src/components/ModeGateOverlay/index.jsx @@ -7,6 +7,8 @@ import { useSessionSelector } from '../../context/SessionContext.jsx'; import { useSharedClock } from '../../hooks/useSharedClock.js'; import SocialButton from '../SocialButton/index.jsx'; import ChatPanel from '../ChatPanel/index.jsx'; +import InterInstancePanel from '../InterInstancePanel/index.jsx'; +import { isFeatureEnabled } from '../../lib/features.js'; const PRIVILEGED_ROLES = new Set(['admin', 'lockdown']); const LOCKDOWN_ROLES = new Set(['lockdown']); @@ -32,6 +34,7 @@ export default function ModeGateOverlay() { const role = useSessionSelector((state) => state.session?.role || null); const reason = useSessionSelector((state) => state.session?.adminReason?.text || ''); const timezone = useSessionSelector((state) => state.session?.timezone || 'UTC'); + const interInstanceEnabled = useSessionSelector((state) => isFeatureEnabled(state, 'interInstance')); const restricted = RESTRICTED_MODES.has(mode); const privileged = mode === 'lockdown' ? LOCKDOWN_ROLES.has(role) : PRIVILEGED_ROLES.has(role); /* @@ -62,33 +65,40 @@ export default function ModeGateOverlay() { const details = getModeDetails(mode); return ( -
-
-
-

{details.title}

-

{details.description}

+
+
+
+
+

{details.title}

+

{details.description}

+
+
+

Reason for locking:

+

+ {reason ? reason : 'No reason set.'} +

+

Server time: {serverTime}

+
+
+ +
+ + You can still use the chat while the server is locked: + {/* set max height of this box */} +
+ +
+ + {/*

+ Your controls are paused until access is granted. You will automatically regain the interface once the mode + changes or after a successful login. +

*/}
-
-

Reason for locking:

-

- {reason ? reason : 'No reason set.'} -

-

Server time: {serverTime}

-
-
- -
- - You can still use the chat while the server is locked: - {/* set max height of this box */} -
- -
- - {/*

- Your controls are paused until access is granted. You will automatically regain the interface once the mode - changes or after a successful login. -

*/} + {interInstanceEnabled ? ( +
+ +
+ ) : null}
); diff --git a/webui/src/components/RightPaneTabs/index.jsx b/webui/src/components/RightPaneTabs/index.jsx index 3d23ea66..0e61cae8 100644 --- a/webui/src/components/RightPaneTabs/index.jsx +++ b/webui/src/components/RightPaneTabs/index.jsx @@ -195,7 +195,7 @@ function QueueReplayLinksRow() { */ return (
-
+
diff --git a/webui/src/components/RoverQueuesPanel/index.jsx b/webui/src/components/RoverQueuesPanel/index.jsx index 22e079b6..61659bc8 100644 --- a/webui/src/components/RoverQueuesPanel/index.jsx +++ b/webui/src/components/RoverQueuesPanel/index.jsx @@ -7,6 +7,9 @@ import { useSharedClock } from '../../hooks/useSharedClock.js'; import CardFrame from '../CardFrame/index.jsx'; import RoverLabel from '../RoverLabel/index.jsx'; import { trackAnalyticsEvent } from '../../analytics/index.js'; +import { openExternalRoverWithPrompt } from '../../lib/interInstanceTransfer.js'; +import { ExternalInstancesCompact } from '../InterInstancePanel/index.jsx'; +import { isFeatureEnabled } from '../../lib/features.js'; function classNames(...values) { return values.filter(Boolean).join(' '); @@ -46,11 +49,18 @@ function formatLabel(user, selfId) { return base; } -export default function RoverQueuesPanel({ title = 'Rovers' }) { +export default function RoverQueuesPanel({ + title = 'Rovers', + roster: rosterOverride = null, + turnQueues: turnQueuesOverride = null, + users: usersOverride = null, + externalInstance = null, +}) { const role = useSessionSelector((state) => state.session?.role || null); - const roster = useSessionSelector((state) => state.session?.roster ?? []); - const turnQueues = useSessionSelector((state) => state.session?.turnQueues ?? {}); - const users = useSessionSelector((state) => state.session?.users ?? []); + const localRoster = useSessionSelector((state) => state.session?.roster ?? []); + const localTurnQueues = useSessionSelector((state) => state.session?.turnQueues ?? {}); + const localUsers = useSessionSelector((state) => state.session?.users ?? []); + const interInstanceEnabled = useSessionSelector((state) => isFeatureEnabled(state, 'interInstance')); const selfId = useSessionSelector((state) => state.session?.socketId || null); const assignedRoverId = useSessionSelector((state) => String(state.session?.assignment?.roverId || '').trim()); const assignedRoverName = useSessionSelector((state) => { @@ -62,8 +72,12 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) { const { requestControl, rebootOwnRover } = useSessionActions(); const [pending, setPending] = useState({}); const [rebootPending, setRebootPending] = useState(false); + const externalMode = Boolean(externalInstance); + const roster = Array.isArray(rosterOverride) ? rosterOverride : localRoster; + const turnQueues = turnQueuesOverride && typeof turnQueuesOverride === 'object' ? turnQueuesOverride : localTurnQueues; + const users = Array.isArray(usersOverride) ? usersOverride : localUsers; - const canRequest = useMemo(() => role && role !== 'spectator', [role]); + const canRequest = useMemo(() => externalMode || (role && role !== 'spectator'), [externalMode, role]); const adminCapable = useMemo( () => role === 'admin' || role === 'lockdown', [role], @@ -89,6 +103,15 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) { async function handleRequest(targetRoverId) { if (!targetRoverId) return; + if (externalMode) { + /* + External queue cards deliberately reuse the local row layout, but their + action cannot go through this Socket.IO server. The row opens the remote + instance, optionally carrying settings after the source-page prompt. + */ + openExternalRoverWithPrompt(externalInstance, targetRoverId); + return; + } setPending((prev) => ({ ...prev, [targetRoverId]: true })); trackAnalyticsEvent('rover_queue_join', { roverId: targetRoverId, @@ -139,7 +162,7 @@ export default function RoverQueuesPanel({ title = 'Rovers' }) { } const headerActions = - role !== 'spectator' && assignedRoverId ? ( + !externalMode && role !== 'spectator' && assignedRoverId ? (
); } diff --git a/webui/src/hooks/useIncomingInterInstanceTransfer.js b/webui/src/hooks/useIncomingInterInstanceTransfer.js new file mode 100644 index 00000000..3026ca94 --- /dev/null +++ b/webui/src/hooks/useIncomingInterInstanceTransfer.js @@ -0,0 +1,60 @@ +// Hook: useIncomingInterInstanceTransfer +// Purpose: Applies settings transferred through an inter-instance URL before the normal identity heartbeat runs. +// Scope: Owns only inbound URL parameters; requesting the target rover is handled after socket/session state is ready. +import { useEffect, useRef } from 'react'; +import { useSettings } from '../settings/index.js'; +import { base64UrlDecodeJson } from '../lib/interInstanceTransfer.js'; +import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx'; + +export default function useIncomingInterInstanceTransfer() { + const settings = useSettings(); + const { requestControl } = useSessionActions(); + const connected = useSessionSelector((state) => state.connected); + const appliedRef = useRef(false); + const requestedRef = useRef(false); + const roverIdRef = useRef(''); + + useEffect(() => { + if (appliedRef.current || typeof window === 'undefined') return; + const url = new URL(window.location.href); + const transfer = url.searchParams.get('settingsTransfer'); + roverIdRef.current = String(url.searchParams.get('rover') || '').trim(); + if (!transfer) { + appliedRef.current = true; + return; + } + try { + /* + The source page already asked before adding settingsTransfer. A present + transfer param is therefore an explicit instruction to replace the local + settings cookie without asking again on the destination server. + */ + const nextSettings = base64UrlDecodeJson(transfer); + settings.saveAll(nextSettings && typeof nextSettings === 'object' ? nextSettings : {}); + url.searchParams.delete('settingsTransfer'); + window.history.replaceState({}, '', `${url.pathname}${url.search}${url.hash}`); + } catch (error) { + // A bad transfer payload should not block the page or the rover request. + console.warn('Failed to apply transferred inter-instance settings', error); + } finally { + appliedRef.current = true; + } + }, [settings]); + + useEffect(() => { + if (!appliedRef.current || requestedRef.current || !connected) return; + const roverId = roverIdRef.current; + if (!roverId) return; + requestedRef.current = true; + /* + The identity heartbeat reacts to the settings overwrite through the shared + settings context. Waiting for a connected socket here keeps this hook from + racing the initial Socket.IO connection while still using the existing + request-control path. + */ + requestControl(roverId).catch((error) => { + requestedRef.current = false; + console.warn('Failed to request transferred inter-instance rover', error); + }); + }, [connected, requestControl]); +} diff --git a/webui/src/lib/interInstanceTransfer.js b/webui/src/lib/interInstanceTransfer.js new file mode 100644 index 00000000..f95ad0a7 --- /dev/null +++ b/webui/src/lib/interInstanceTransfer.js @@ -0,0 +1,48 @@ +// Inter-Instance Transfer Helpers +// Purpose: Builds cross-server links and moves the local settings cookie only when the user opts in before leaving. +// Scope: Keeps URL encoding and settings-transfer behavior out of the rover queue rendering code. +import { loadSettings } from '../settings/persistence.js'; + +function base64UrlEncodeJson(value) { + const json = JSON.stringify(value ?? {}); + const bytes = new TextEncoder().encode(json); + let binary = ''; + bytes.forEach((byte) => { + binary += String.fromCharCode(byte); + }); + return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); +} + +export function base64UrlDecodeJson(value) { + const raw = String(value || '').replace(/-/g, '+').replace(/_/g, '/'); + const padded = raw.padEnd(Math.ceil(raw.length / 4) * 4, '='); + const binary = atob(padded); + const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); + return JSON.parse(new TextDecoder().decode(bytes)); +} + +export function buildExternalRoverUrl(instance, roverId, { includeSettings = false } = {}) { + const publicUrl = String(instance?.instance?.publicUrl || instance?.publicUrl || instance?.url || '').trim(); + if (!publicUrl) return ''; + const url = new URL(publicUrl); + if (roverId) url.searchParams.set('rover', String(roverId)); + /* + The destination always applies settingsTransfer if present, so this helper + only adds it after the current page has already asked for consent. + */ + if (includeSettings) { + url.searchParams.set('settingsTransfer', base64UrlEncodeJson(loadSettings())); + } + return url.toString(); +} + +export function openExternalRoverWithPrompt(instance, roverId) { + const withoutTransfer = buildExternalRoverUrl(instance, roverId); + if (!withoutTransfer) return; + const instanceName = String(instance?.instance?.name || instance?.url || 'that server'); + const includeSettings = window.confirm( + `Transfer your identity and settings to ${instanceName}? Press Cancel to open without transferring them.`, + ); + const targetUrl = buildExternalRoverUrl(instance, roverId, { includeSettings }); + window.location.href = targetUrl || withoutTransfer; +}