commit c1c56a1f3ec93ae6edd2e0d023327f650ac955be Author: dvirlabs Date: Mon Apr 13 00:16:56 2026 +0300 Build and push app diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..26d11ee --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +# Build outputs +dist/ +node_modules/ + +# Version control +.git/ +.gitignore + +# Helm chart (not needed inside the image) +helm/ + +# Editor / OS +.vscode/ +.idea/ +*.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* + +# Env files (never bake secrets into the image) +.env +.env.* diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dee90dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +cloudflare-my-apps-tunnel.yaml \ No newline at end of file diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..001a361 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,65 @@ +steps: + build: + name: Build & Push + image: woodpeckerci/plugin-kaniko + when: + branch: [ master, develop ] + event: [ push, pull_request, tag ] + settings: + registry: harbor.dvirlabs.com + repo: my-apps/${CI_REPO_NAME} + dockerfile: Dockerfile + context: . + tags: + - latest + - ${CI_COMMIT_TAG:-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:7}} + username: + from_secret: DOCKER_USERNAME + password: + from_secret: DOCKER_PASSWORD + + update-values: + name: Update image tag in values.yaml + image: alpine:3.19 + when: + branch: [ master, develop ] + event: [ push ] + environment: + GIT_USERNAME: + from_secret: GIT_USERNAME + GIT_TOKEN: + from_secret: GIT_TOKEN + commands: + - apk add --no-cache git yq + - git config --global user.name "woodpecker-bot" + - git config --global user.email "ci@dvirlabs.com" + - git clone "https://$${GIT_USERNAME}:$${GIT_TOKEN}@git.dvirlabs.com/dvirlabs/my-apps.git" + - cd my-apps + - | + TAG="${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:7}" + echo "๐Ÿ’ก Setting image tag to: $TAG" + yq -i ".image.tag = \"$TAG\"" manifests/${CI_REPO_NAME}/values.yaml + git add manifests/${CI_REPO_NAME}/values.yaml + git commit -m "ci(${CI_REPO_NAME}): update image tag to $TAG" || echo "No changes" + git push origin HEAD + + trigger-gitops-via-push: + name: Trigger apps-gitops via Git push + image: alpine/git + when: + branch: [ master, develop ] + event: [ push ] + environment: + GIT_USERNAME: + from_secret: GIT_USERNAME + GIT_TOKEN: + from_secret: GIT_TOKEN + commands: + - git config --global user.name "woodpecker-bot" + - git config --global user.email "ci@dvirlabs.com" + - git clone "https://$${GIT_USERNAME}:$${GIT_TOKEN}@git.dvirlabs.com/dvirlabs/apps-gitops.git" + - cd apps-gitops + - echo "# trigger at $(date) by ${CI_REPO_NAME}" >> .trigger + - git add .trigger + - git commit -m "ci: trigger apps-gitops build" || echo "no changes" + - git push origin HEAD \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bb56d6b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +# โ”€โ”€ Stage 1: build โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +FROM node:20-alpine AS builder + +WORKDIR /app + +# Install deps first (layer-cached unless package files change) +COPY package.json package-lock.json ./ +RUN npm ci --frozen-lockfile + +# Copy source and build +COPY . . +RUN npm run build + +# โ”€โ”€ Stage 2: serve โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +FROM nginx:1.27-alpine AS runner + +# Remove default nginx config +RUN rm /etc/nginx/conf.d/default.conf + +# Drop our custom config +COPY nginx.conf /etc/nginx/conf.d/errorlab.conf + +# Copy built static files from the builder stage +COPY --from=builder /app/dist /usr/share/nginx/html + +# Run as non-root (nginx:alpine UID 101) +RUN chown -R nginx:nginx /usr/share/nginx/html && \ + chown -R nginx:nginx /var/cache/nginx && \ + touch /var/run/nginx.pid && \ + chown nginx:nginx /var/run/nginx.pid + +USER nginx + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD wget -qO- http://localhost/index.html || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/dist/assets/index-BmIETw7i.js b/dist/assets/index-BmIETw7i.js new file mode 100644 index 0000000..31e0545 --- /dev/null +++ b/dist/assets/index-BmIETw7i.js @@ -0,0 +1,67 @@ +function Qc(e,t){for(var n=0;nr[l]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const o of l)if(o.type==="childList")for(const i of o.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&r(i)}).observe(document,{childList:!0,subtree:!0});function n(l){const o={};return l.integrity&&(o.integrity=l.integrity),l.referrerPolicy&&(o.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?o.credentials="include":l.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(l){if(l.ep)return;l.ep=!0;const o=n(l);fetch(l.href,o)}})();function Yc(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ws={exports:{}},fl={},Ss={exports:{}},j={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var nr=Symbol.for("react.element"),Kc=Symbol.for("react.portal"),Gc=Symbol.for("react.fragment"),Xc=Symbol.for("react.strict_mode"),Zc=Symbol.for("react.profiler"),Jc=Symbol.for("react.provider"),qc=Symbol.for("react.context"),bc=Symbol.for("react.forward_ref"),ef=Symbol.for("react.suspense"),tf=Symbol.for("react.memo"),nf=Symbol.for("react.lazy"),eu=Symbol.iterator;function rf(e){return e===null||typeof e!="object"?null:(e=eu&&e[eu]||e["@@iterator"],typeof e=="function"?e:null)}var ks={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Es=Object.assign,Cs={};function dn(e,t,n){this.props=e,this.context=t,this.refs=Cs,this.updater=n||ks}dn.prototype.isReactComponent={};dn.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};dn.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function xs(){}xs.prototype=dn.prototype;function li(e,t,n){this.props=e,this.context=t,this.refs=Cs,this.updater=n||ks}var oi=li.prototype=new xs;oi.constructor=li;Es(oi,dn.prototype);oi.isPureReactComponent=!0;var tu=Array.isArray,_s=Object.prototype.hasOwnProperty,ii={current:null},Ns={key:!0,ref:!0,__self:!0,__source:!0};function Ps(e,t,n){var r,l={},o=null,i=null;if(t!=null)for(r in t.ref!==void 0&&(i=t.ref),t.key!==void 0&&(o=""+t.key),t)_s.call(t,r)&&!Ns.hasOwnProperty(r)&&(l[r]=t[r]);var u=arguments.length-2;if(u===1)l.children=n;else if(1>>1,Z=_[Q];if(0>>1;Ql(Ol,z))Etl(sr,Ol)?(_[Q]=sr,_[Et]=z,Q=Et):(_[Q]=Ol,_[kt]=z,Q=kt);else if(Etl(sr,z))_[Q]=sr,_[Et]=z,Q=Et;else break e}}return L}function l(_,L){var z=_.sortIndex-L.sortIndex;return z!==0?z:_.id-L.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var i=Date,u=i.now();e.unstable_now=function(){return i.now()-u}}var s=[],a=[],h=1,d=null,m=3,y=!1,w=!1,S=!1,x=typeof setTimeout=="function"?setTimeout:null,f=typeof clearTimeout=="function"?clearTimeout:null,c=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function p(_){for(var L=n(a);L!==null;){if(L.callback===null)r(a);else if(L.startTime<=_)r(a),L.sortIndex=L.expirationTime,t(s,L);else break;L=n(a)}}function v(_){if(S=!1,p(_),!w)if(n(s)!==null)w=!0,zl(E);else{var L=n(a);L!==null&&jl(v,L.startTime-_)}}function E(_,L){w=!1,S&&(S=!1,f(R),R=-1),y=!0;var z=m;try{for(p(L),d=n(s);d!==null&&(!(d.expirationTime>L)||_&&!Re());){var Q=d.callback;if(typeof Q=="function"){d.callback=null,m=d.priorityLevel;var Z=Q(d.expirationTime<=L);L=e.unstable_now(),typeof Z=="function"?d.callback=Z:d===n(s)&&r(s),p(L)}else r(s);d=n(s)}if(d!==null)var ur=!0;else{var kt=n(a);kt!==null&&jl(v,kt.startTime-L),ur=!1}return ur}finally{d=null,m=z,y=!1}}var P=!1,T=null,R=-1,H=5,O=-1;function Re(){return!(e.unstable_now()-O_||125<_?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):H=0<_?Math.floor(1e3/_):5},e.unstable_getCurrentPriorityLevel=function(){return m},e.unstable_getFirstCallbackNode=function(){return n(s)},e.unstable_next=function(_){switch(m){case 1:case 2:case 3:var L=3;break;default:L=m}var z=m;m=L;try{return _()}finally{m=z}},e.unstable_pauseExecution=function(){},e.unstable_requestPaint=function(){},e.unstable_runWithPriority=function(_,L){switch(_){case 1:case 2:case 3:case 4:case 5:break;default:_=3}var z=m;m=_;try{return L()}finally{m=z}},e.unstable_scheduleCallback=function(_,L,z){var Q=e.unstable_now();switch(typeof z=="object"&&z!==null?(z=z.delay,z=typeof z=="number"&&0Q?(_.sortIndex=z,t(a,_),n(s)===null&&_===n(a)&&(S?(f(R),R=-1):S=!0,jl(v,z-Q))):(_.sortIndex=Z,t(s,_),w||y||(w=!0,zl(E))),_},e.unstable_shouldYield=Re,e.unstable_wrapCallback=function(_){var L=m;return function(){var z=m;m=L;try{return _.apply(this,arguments)}finally{m=z}}}})(Os);js.exports=Os;var vf=js.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var gf=N,Se=vf;function g(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),ao=Object.prototype.hasOwnProperty,yf=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ru={},lu={};function wf(e){return ao.call(lu,e)?!0:ao.call(ru,e)?!1:yf.test(e)?lu[e]=!0:(ru[e]=!0,!1)}function Sf(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function kf(e,t,n,r){if(t===null||typeof t>"u"||Sf(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function ce(e,t,n,r,l,o,i){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=i}var ne={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){ne[e]=new ce(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];ne[t]=new ce(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){ne[e]=new ce(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){ne[e]=new ce(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){ne[e]=new ce(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){ne[e]=new ce(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){ne[e]=new ce(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){ne[e]=new ce(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){ne[e]=new ce(e,5,!1,e.toLowerCase(),null,!1,!1)});var si=/[\-:]([a-z])/g;function ai(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(si,ai);ne[t]=new ce(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(si,ai);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(si,ai);ne[t]=new ce(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!1,!1)});ne.xlinkHref=new ce("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){ne[e]=new ce(e,1,!1,e.toLowerCase(),null,!0,!0)});function ci(e,t,n,r){var l=ne.hasOwnProperty(t)?ne[t]:null;(l!==null?l.type!==0:r||!(2u||l[i]!==o[u]){var s=` +`+l[i].replace(" at new "," at ");return e.displayName&&s.includes("")&&(s=s.replace("",e.displayName)),s}while(1<=i&&0<=u);break}}}finally{Dl=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?_n(e):""}function Ef(e){switch(e.tag){case 5:return _n(e.type);case 16:return _n("Lazy");case 13:return _n("Suspense");case 19:return _n("SuspenseList");case 0:case 2:case 15:return e=Fl(e.type,!1),e;case 11:return e=Fl(e.type.render,!1),e;case 1:return e=Fl(e.type,!0),e;default:return""}}function ho(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Bt:return"Fragment";case At:return"Portal";case co:return"Profiler";case fi:return"StrictMode";case fo:return"Suspense";case po:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case Ds:return(e.displayName||"Context")+".Consumer";case Ms:return(e._context.displayName||"Context")+".Provider";case di:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case pi:return t=e.displayName||null,t!==null?t:ho(e.type)||"Memo";case tt:t=e._payload,e=e._init;try{return ho(e(t))}catch{}}return null}function Cf(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ho(t);case 8:return t===fi?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function vt(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Us(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function xf(e){var t=Us(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(i){r=""+i,o.call(this,i)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(i){r=""+i},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function fr(e){e._valueTracker||(e._valueTracker=xf(e))}function As(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=Us(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Ar(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function mo(e,t){var n=t.checked;return V({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function iu(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=vt(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Bs(e,t){t=t.checked,t!=null&&ci(e,"checked",t,!1)}function vo(e,t){Bs(e,t);var n=vt(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?go(e,t.type,n):t.hasOwnProperty("defaultValue")&&go(e,t.type,vt(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function uu(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function go(e,t,n){(t!=="number"||Ar(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Nn=Array.isArray;function Jt(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=dr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Bn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ln={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},_f=["Webkit","ms","Moz","O"];Object.keys(Ln).forEach(function(e){_f.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ln[t]=Ln[e]})});function Hs(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ln.hasOwnProperty(e)&&Ln[e]?(""+t).trim():t+"px"}function Qs(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Hs(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Nf=V({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function So(e,t){if(t){if(Nf[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(g(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(g(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(g(61))}if(t.style!=null&&typeof t.style!="object")throw Error(g(62))}}function ko(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Eo=null;function hi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Co=null,qt=null,bt=null;function cu(e){if(e=or(e)){if(typeof Co!="function")throw Error(g(280));var t=e.stateNode;t&&(t=vl(t),Co(e.stateNode,e.type,t))}}function Ys(e){qt?bt?bt.push(e):bt=[e]:qt=e}function Ks(){if(qt){var e=qt,t=bt;if(bt=qt=null,cu(e),t)for(e=0;e>>=0,e===0?32:31-(Ff(e)/Uf|0)|0}var pr=64,hr=4194304;function Pn(e){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: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 e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Wr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,o=e.pingedLanes,i=n&268435455;if(i!==0){var u=i&~l;u!==0?r=Pn(u):(o&=i,o!==0&&(r=Pn(o)))}else i=n&~l,i!==0?r=Pn(i):o!==0&&(r=Pn(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&l)&&(l=r&-r,o=t&-t,l>=o||l===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function rr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Ie(t),e[t]=n}function Vf(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=jn),wu=" ",Su=!1;function pa(e,t){switch(e){case"keyup":return vd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ha(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var $t=!1;function yd(e,t){switch(e){case"compositionend":return ha(t);case"keypress":return t.which!==32?null:(Su=!0,wu);case"textInput":return e=t.data,e===wu&&Su?null:e;default:return null}}function wd(e,t){if($t)return e==="compositionend"||!Ei&&pa(e,t)?(e=fa(),Rr=wi=ot=null,$t=!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:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=xu(n)}}function ya(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?ya(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function wa(){for(var e=window,t=Ar();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Ar(e.document)}return t}function Ci(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")}function Td(e){var t=wa(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&ya(n.ownerDocument.documentElement,n)){if(r!==null&&Ci(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,o=Math.min(r.start,l);r=r.end===void 0?o:Math.min(r.end,l),!e.extend&&o>r&&(l=r,r=o,o=l),l=_u(n,o);var i=_u(n,r);l&&i&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==i.node||e.focusOffset!==i.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(i.node,i.offset)):(t.setEnd(i.node,i.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Vt=null,Ro=null,In=null,Lo=!1;function Nu(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Lo||Vt==null||Vt!==Ar(r)||(r=Vt,"selectionStart"in r&&Ci(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),In&&Yn(In,r)||(In=r,r=Yr(Ro,"onSelect"),0Qt||(e.current=Do[Qt],Do[Qt]=null,Qt--)}function D(e,t){Qt++,Do[Qt]=e.current,e.current=t}var gt={},ie=wt(gt),pe=wt(!1),Lt=gt;function on(e,t){var n=e.type.contextTypes;if(!n)return gt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},o;for(o in n)l[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function he(e){return e=e.childContextTypes,e!=null}function Gr(){U(pe),U(ie)}function Ou(e,t,n){if(ie.current!==gt)throw Error(g(168));D(ie,t),D(pe,n)}function Ta(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(g(108,Cf(e)||"Unknown",l));return V({},n,r)}function Xr(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||gt,Lt=ie.current,D(ie,e),D(pe,pe.current),!0}function Iu(e,t,n){var r=e.stateNode;if(!r)throw Error(g(169));n?(e=Ta(e,t,Lt),r.__reactInternalMemoizedMergedChildContext=e,U(pe),U(ie),D(ie,e)):U(pe),D(pe,n)}var Qe=null,gl=!1,Jl=!1;function Ra(e){Qe===null?Qe=[e]:Qe.push(e)}function Bd(e){gl=!0,Ra(e)}function St(){if(!Jl&&Qe!==null){Jl=!0;var e=0,t=M;try{var n=Qe;for(M=1;e>=i,l-=i,Ye=1<<32-Ie(t)+l|n<R?(H=T,T=null):H=T.sibling;var O=m(f,T,p[R],v);if(O===null){T===null&&(T=H);break}e&&T&&O.alternate===null&&t(f,T),c=o(O,c,R),P===null?E=O:P.sibling=O,P=O,T=H}if(R===p.length)return n(f,T),A&&Ct(f,R),E;if(T===null){for(;RR?(H=T,T=null):H=T.sibling;var Re=m(f,T,O.value,v);if(Re===null){T===null&&(T=H);break}e&&T&&Re.alternate===null&&t(f,T),c=o(Re,c,R),P===null?E=Re:P.sibling=Re,P=Re,T=H}if(O.done)return n(f,T),A&&Ct(f,R),E;if(T===null){for(;!O.done;R++,O=p.next())O=d(f,O.value,v),O!==null&&(c=o(O,c,R),P===null?E=O:P.sibling=O,P=O);return A&&Ct(f,R),E}for(T=r(f,T);!O.done;R++,O=p.next())O=y(T,f,R,O.value,v),O!==null&&(e&&O.alternate!==null&&T.delete(O.key===null?R:O.key),c=o(O,c,R),P===null?E=O:P.sibling=O,P=O);return e&&T.forEach(function(mn){return t(f,mn)}),A&&Ct(f,R),E}function x(f,c,p,v){if(typeof p=="object"&&p!==null&&p.type===Bt&&p.key===null&&(p=p.props.children),typeof p=="object"&&p!==null){switch(p.$$typeof){case cr:e:{for(var E=p.key,P=c;P!==null;){if(P.key===E){if(E=p.type,E===Bt){if(P.tag===7){n(f,P.sibling),c=l(P,p.props.children),c.return=f,f=c;break e}}else if(P.elementType===E||typeof E=="object"&&E!==null&&E.$$typeof===tt&&Fu(E)===P.type){n(f,P.sibling),c=l(P,p.props),c.ref=En(f,P,p),c.return=f,f=c;break e}n(f,P);break}else t(f,P);P=P.sibling}p.type===Bt?(c=Rt(p.props.children,f.mode,v,p.key),c.return=f,f=c):(v=Fr(p.type,p.key,p.props,null,f.mode,v),v.ref=En(f,c,p),v.return=f,f=v)}return i(f);case At:e:{for(P=p.key;c!==null;){if(c.key===P)if(c.tag===4&&c.stateNode.containerInfo===p.containerInfo&&c.stateNode.implementation===p.implementation){n(f,c.sibling),c=l(c,p.children||[]),c.return=f,f=c;break e}else{n(f,c);break}else t(f,c);c=c.sibling}c=oo(p,f.mode,v),c.return=f,f=c}return i(f);case tt:return P=p._init,x(f,c,P(p._payload),v)}if(Nn(p))return w(f,c,p,v);if(gn(p))return S(f,c,p,v);kr(f,p)}return typeof p=="string"&&p!==""||typeof p=="number"?(p=""+p,c!==null&&c.tag===6?(n(f,c.sibling),c=l(c,p),c.return=f,f=c):(n(f,c),c=lo(p,f.mode,v),c.return=f,f=c),i(f)):n(f,c)}return x}var sn=Oa(!0),Ia=Oa(!1),qr=wt(null),br=null,Gt=null,Pi=null;function Ti(){Pi=Gt=br=null}function Ri(e){var t=qr.current;U(qr),e._currentValue=t}function Ao(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function tn(e,t){br=e,Pi=Gt=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(de=!0),e.firstContext=null)}function Pe(e){var t=e._currentValue;if(Pi!==e)if(e={context:e,memoizedValue:t,next:null},Gt===null){if(br===null)throw Error(g(308));Gt=e,br.dependencies={lanes:0,firstContext:e}}else Gt=Gt.next=e;return t}var Nt=null;function Li(e){Nt===null?Nt=[e]:Nt.push(e)}function Ma(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,Li(t)):(n.next=l.next,l.next=n),t.interleaved=n,Je(e,r)}function Je(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var nt=!1;function zi(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Da(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ge(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function dt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,I&2){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Je(e,n)}return l=r.interleaved,l===null?(t.next=t,Li(r)):(t.next=l.next,l.next=t),r.interleaved=t,Je(e,n)}function zr(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,vi(e,n)}}function Uu(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,o=null;if(n=n.firstBaseUpdate,n!==null){do{var i={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};o===null?l=o=i:o=o.next=i,n=n.next}while(n!==null);o===null?l=o=t:o=o.next=t}else l=o=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:o,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function el(e,t,n,r){var l=e.updateQueue;nt=!1;var o=l.firstBaseUpdate,i=l.lastBaseUpdate,u=l.shared.pending;if(u!==null){l.shared.pending=null;var s=u,a=s.next;s.next=null,i===null?o=a:i.next=a,i=s;var h=e.alternate;h!==null&&(h=h.updateQueue,u=h.lastBaseUpdate,u!==i&&(u===null?h.firstBaseUpdate=a:u.next=a,h.lastBaseUpdate=s))}if(o!==null){var d=l.baseState;i=0,h=a=s=null,u=o;do{var m=u.lane,y=u.eventTime;if((r&m)===m){h!==null&&(h=h.next={eventTime:y,lane:0,tag:u.tag,payload:u.payload,callback:u.callback,next:null});e:{var w=e,S=u;switch(m=t,y=n,S.tag){case 1:if(w=S.payload,typeof w=="function"){d=w.call(y,d,m);break e}d=w;break e;case 3:w.flags=w.flags&-65537|128;case 0:if(w=S.payload,m=typeof w=="function"?w.call(y,d,m):w,m==null)break e;d=V({},d,m);break e;case 2:nt=!0}}u.callback!==null&&u.lane!==0&&(e.flags|=64,m=l.effects,m===null?l.effects=[u]:m.push(u))}else y={eventTime:y,lane:m,tag:u.tag,payload:u.payload,callback:u.callback,next:null},h===null?(a=h=y,s=d):h=h.next=y,i|=m;if(u=u.next,u===null){if(u=l.shared.pending,u===null)break;m=u,u=m.next,m.next=null,l.lastBaseUpdate=m,l.shared.pending=null}}while(!0);if(h===null&&(s=d),l.baseState=s,l.firstBaseUpdate=a,l.lastBaseUpdate=h,t=l.shared.interleaved,t!==null){l=t;do i|=l.lane,l=l.next;while(l!==t)}else o===null&&(l.shared.lanes=0);Ot|=i,e.lanes=i,e.memoizedState=d}}function Au(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=bl.transition;bl.transition={};try{e(!1),t()}finally{M=n,bl.transition=r}}function ba(){return Te().memoizedState}function Hd(e,t,n){var r=ht(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},ec(e))tc(t,n);else if(n=Ma(e,t,n,r),n!==null){var l=se();Me(n,e,r,l),nc(n,t,r)}}function Qd(e,t,n){var r=ht(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(ec(e))tc(t,l);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var i=t.lastRenderedState,u=o(i,n);if(l.hasEagerState=!0,l.eagerState=u,De(u,i)){var s=t.interleaved;s===null?(l.next=l,Li(t)):(l.next=s.next,s.next=l),t.interleaved=l;return}}catch{}finally{}n=Ma(e,t,l,r),n!==null&&(l=se(),Me(n,e,r,l),nc(n,t,r))}}function ec(e){var t=e.alternate;return e===$||t!==null&&t===$}function tc(e,t){Mn=nl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function nc(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,vi(e,n)}}var rl={readContext:Pe,useCallback:re,useContext:re,useEffect:re,useImperativeHandle:re,useInsertionEffect:re,useLayoutEffect:re,useMemo:re,useReducer:re,useRef:re,useState:re,useDebugValue:re,useDeferredValue:re,useTransition:re,useMutableSource:re,useSyncExternalStore:re,useId:re,unstable_isNewReconciler:!1},Yd={readContext:Pe,useCallback:function(e,t){return Ue().memoizedState=[e,t===void 0?null:t],e},useContext:Pe,useEffect:$u,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Or(4194308,4,Ga.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Or(4194308,4,e,t)},useInsertionEffect:function(e,t){return Or(4,2,e,t)},useMemo:function(e,t){var n=Ue();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ue();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Hd.bind(null,$,e),[r.memoizedState,e]},useRef:function(e){var t=Ue();return e={current:e},t.memoizedState=e},useState:Bu,useDebugValue:Ai,useDeferredValue:function(e){return Ue().memoizedState=e},useTransition:function(){var e=Bu(!1),t=e[0];return e=Wd.bind(null,e[1]),Ue().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=$,l=Ue();if(A){if(n===void 0)throw Error(g(407));n=n()}else{if(n=t(),b===null)throw Error(g(349));jt&30||Ba(r,t,n)}l.memoizedState=n;var o={value:n,getSnapshot:t};return l.queue=o,$u(Va.bind(null,r,o,e),[e]),r.flags|=2048,er(9,$a.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Ue(),t=b.identifierPrefix;if(A){var n=Ke,r=Ye;n=(r&~(1<<32-Ie(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=qn++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=i.createElement(n,{is:r.is}):(e=i.createElement(n),n==="select"&&(i=e,r.multiple?i.multiple=!0:r.size&&(i.size=r.size))):e=i.createElementNS(e,n),e[Ae]=t,e[Xn]=r,dc(e,t,!1,!1),t.stateNode=e;e:{switch(i=ko(n,r),n){case"dialog":F("cancel",e),F("close",e),l=r;break;case"iframe":case"object":case"embed":F("load",e),l=r;break;case"video":case"audio":for(l=0;lfn&&(t.flags|=128,r=!0,Cn(o,!1),t.lanes=4194304)}else{if(!r)if(e=tl(i),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Cn(o,!0),o.tail===null&&o.tailMode==="hidden"&&!i.alternate&&!A)return le(t),null}else 2*Y()-o.renderingStartTime>fn&&n!==1073741824&&(t.flags|=128,r=!0,Cn(o,!1),t.lanes=4194304);o.isBackwards?(i.sibling=t.child,t.child=i):(n=o.last,n!==null?n.sibling=i:t.child=i,o.last=i)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=Y(),t.sibling=null,n=B.current,D(B,r?n&1|2:n&1),t):(le(t),null);case 22:case 23:return Qi(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ge&1073741824&&(le(t),t.subtreeFlags&6&&(t.flags|=8192)):le(t),null;case 24:return null;case 25:return null}throw Error(g(156,t.tag))}function ep(e,t){switch(_i(t),t.tag){case 1:return he(t.type)&&Gr(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return an(),U(pe),U(ie),Ii(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Oi(t),null;case 13:if(U(B),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(g(340));un()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return U(B),null;case 4:return an(),null;case 10:return Ri(t.type._context),null;case 22:case 23:return Qi(),null;case 24:return null;default:return null}}var Cr=!1,oe=!1,tp=typeof WeakSet=="function"?WeakSet:Set,C=null;function Xt(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){W(e,t,r)}else n.current=null}function Go(e,t,n){try{n()}catch(r){W(e,t,r)}}var qu=!1;function np(e,t){if(zo=Hr,e=wa(),Ci(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var i=0,u=-1,s=-1,a=0,h=0,d=e,m=null;t:for(;;){for(var y;d!==n||l!==0&&d.nodeType!==3||(u=i+l),d!==o||r!==0&&d.nodeType!==3||(s=i+r),d.nodeType===3&&(i+=d.nodeValue.length),(y=d.firstChild)!==null;)m=d,d=y;for(;;){if(d===e)break t;if(m===n&&++a===l&&(u=i),m===o&&++h===r&&(s=i),(y=d.nextSibling)!==null)break;d=m,m=d.parentNode}d=y}n=u===-1||s===-1?null:{start:u,end:s}}else n=null}n=n||{start:0,end:0}}else n=null;for(jo={focusedElem:e,selectionRange:n},Hr=!1,C=t;C!==null;)if(t=C,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,C=e;else for(;C!==null;){t=C;try{var w=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(w!==null){var S=w.memoizedProps,x=w.memoizedState,f=t.stateNode,c=f.getSnapshotBeforeUpdate(t.elementType===t.type?S:ze(t.type,S),x);f.__reactInternalSnapshotBeforeUpdate=c}break;case 3:var p=t.stateNode.containerInfo;p.nodeType===1?p.textContent="":p.nodeType===9&&p.documentElement&&p.removeChild(p.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(g(163))}}catch(v){W(t,t.return,v)}if(e=t.sibling,e!==null){e.return=t.return,C=e;break}C=t.return}return w=qu,qu=!1,w}function Dn(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var o=l.destroy;l.destroy=void 0,o!==void 0&&Go(t,n,o)}l=l.next}while(l!==r)}}function Sl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Xo(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function mc(e){var t=e.alternate;t!==null&&(e.alternate=null,mc(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ae],delete t[Xn],delete t[Mo],delete t[Ud],delete t[Ad])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function vc(e){return e.tag===5||e.tag===3||e.tag===4}function bu(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||vc(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.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 Zo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Kr));else if(r!==4&&(e=e.child,e!==null))for(Zo(e,t,n),e=e.sibling;e!==null;)Zo(e,t,n),e=e.sibling}function Jo(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Jo(e,t,n),e=e.sibling;e!==null;)Jo(e,t,n),e=e.sibling}var ee=null,je=!1;function et(e,t,n){for(n=n.child;n!==null;)gc(e,t,n),n=n.sibling}function gc(e,t,n){if(Ve&&typeof Ve.onCommitFiberUnmount=="function")try{Ve.onCommitFiberUnmount(dl,n)}catch{}switch(n.tag){case 5:oe||Xt(n,t);case 6:var r=ee,l=je;ee=null,et(e,t,n),ee=r,je=l,ee!==null&&(je?(e=ee,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):ee.removeChild(n.stateNode));break;case 18:ee!==null&&(je?(e=ee,n=n.stateNode,e.nodeType===8?Zl(e.parentNode,n):e.nodeType===1&&Zl(e,n),Hn(e)):Zl(ee,n.stateNode));break;case 4:r=ee,l=je,ee=n.stateNode.containerInfo,je=!0,et(e,t,n),ee=r,je=l;break;case 0:case 11:case 14:case 15:if(!oe&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var o=l,i=o.destroy;o=o.tag,i!==void 0&&(o&2||o&4)&&Go(n,t,i),l=l.next}while(l!==r)}et(e,t,n);break;case 1:if(!oe&&(Xt(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(u){W(n,t,u)}et(e,t,n);break;case 21:et(e,t,n);break;case 22:n.mode&1?(oe=(r=oe)||n.memoizedState!==null,et(e,t,n),oe=r):et(e,t,n);break;default:et(e,t,n)}}function es(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new tp),t.forEach(function(r){var l=fp.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function Le(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=i),r&=~o}if(r=l,r=Y()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*lp(r/1960))-r,10e?16:e,it===null)var r=!1;else{if(e=it,it=null,il=0,I&6)throw Error(g(331));var l=I;for(I|=4,C=e.current;C!==null;){var o=C,i=o.child;if(C.flags&16){var u=o.deletions;if(u!==null){for(var s=0;sY()-Wi?Tt(e,0):Vi|=n),me(e,t)}function _c(e,t){t===0&&(e.mode&1?(t=hr,hr<<=1,!(hr&130023424)&&(hr=4194304)):t=1);var n=se();e=Je(e,t),e!==null&&(rr(e,t,n),me(e,n))}function cp(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),_c(e,n)}function fp(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(g(314))}r!==null&&r.delete(t),_c(e,n)}var Nc;Nc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||pe.current)de=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return de=!1,qd(e,t,n);de=!!(e.flags&131072)}else de=!1,A&&t.flags&1048576&&La(t,Jr,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ir(e,t),e=t.pendingProps;var l=on(t,ie.current);tn(t,n),l=Di(null,t,r,e,l,n);var o=Fi();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,he(r)?(o=!0,Xr(t)):o=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,zi(t),l.updater=wl,t.stateNode=l,l._reactInternals=t,$o(t,r,e,n),t=Ho(null,t,r,!0,o,n)):(t.tag=0,A&&o&&xi(t),ue(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ir(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=pp(r),e=ze(r,e),l){case 0:t=Wo(null,t,r,e,n);break e;case 1:t=Xu(null,t,r,e,n);break e;case 11:t=Ku(null,t,r,e,n);break e;case 14:t=Gu(null,t,r,ze(r.type,e),n);break e}throw Error(g(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Wo(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Xu(e,t,r,l,n);case 3:e:{if(ac(t),e===null)throw Error(g(387));r=t.pendingProps,o=t.memoizedState,l=o.element,Da(e,t),el(t,r,null,n);var i=t.memoizedState;if(r=i.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:i.cache,pendingSuspenseBoundaries:i.pendingSuspenseBoundaries,transitions:i.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){l=cn(Error(g(423)),t),t=Zu(e,t,r,n,l);break e}else if(r!==l){l=cn(Error(g(424)),t),t=Zu(e,t,r,n,l);break e}else for(ye=ft(t.stateNode.containerInfo.firstChild),we=t,A=!0,Oe=null,n=Ia(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(un(),r===l){t=qe(e,t,n);break e}ue(e,t,r,n)}t=t.child}return t;case 5:return Fa(t),e===null&&Uo(t),r=t.type,l=t.pendingProps,o=e!==null?e.memoizedProps:null,i=l.children,Oo(r,l)?i=null:o!==null&&Oo(r,o)&&(t.flags|=32),sc(e,t),ue(e,t,i,n),t.child;case 6:return e===null&&Uo(t),null;case 13:return cc(e,t,n);case 4:return ji(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=sn(t,null,r,n):ue(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Ku(e,t,r,l,n);case 7:return ue(e,t,t.pendingProps,n),t.child;case 8:return ue(e,t,t.pendingProps.children,n),t.child;case 12:return ue(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,o=t.memoizedProps,i=l.value,D(qr,r._currentValue),r._currentValue=i,o!==null)if(De(o.value,i)){if(o.children===l.children&&!pe.current){t=qe(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var u=o.dependencies;if(u!==null){i=o.child;for(var s=u.firstContext;s!==null;){if(s.context===r){if(o.tag===1){s=Ge(-1,n&-n),s.tag=2;var a=o.updateQueue;if(a!==null){a=a.shared;var h=a.pending;h===null?s.next=s:(s.next=h.next,h.next=s),a.pending=s}}o.lanes|=n,s=o.alternate,s!==null&&(s.lanes|=n),Ao(o.return,n,t),u.lanes|=n;break}s=s.next}}else if(o.tag===10)i=o.type===t.type?null:o.child;else if(o.tag===18){if(i=o.return,i===null)throw Error(g(341));i.lanes|=n,u=i.alternate,u!==null&&(u.lanes|=n),Ao(i,n,t),i=o.sibling}else i=o.child;if(i!==null)i.return=o;else for(i=o;i!==null;){if(i===t){i=null;break}if(o=i.sibling,o!==null){o.return=i.return,i=o;break}i=i.return}o=i}ue(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,tn(t,n),l=Pe(l),r=r(l),t.flags|=1,ue(e,t,r,n),t.child;case 14:return r=t.type,l=ze(r,t.pendingProps),l=ze(r.type,l),Gu(e,t,r,l,n);case 15:return ic(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:ze(r,l),Ir(e,t),t.tag=1,he(r)?(e=!0,Xr(t)):e=!1,tn(t,n),rc(t,r,l),$o(t,r,l,n),Ho(null,t,r,!0,e,n);case 19:return fc(e,t,n);case 22:return uc(e,t,n)}throw Error(g(156,t.tag))};function Pc(e,t){return ea(e,t)}function dp(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function _e(e,t,n,r){return new dp(e,t,n,r)}function Ki(e){return e=e.prototype,!(!e||!e.isReactComponent)}function pp(e){if(typeof e=="function")return Ki(e)?1:0;if(e!=null){if(e=e.$$typeof,e===di)return 11;if(e===pi)return 14}return 2}function mt(e,t){var n=e.alternate;return n===null?(n=_e(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Fr(e,t,n,r,l,o){var i=2;if(r=e,typeof e=="function")Ki(e)&&(i=1);else if(typeof e=="string")i=5;else e:switch(e){case Bt:return Rt(n.children,l,o,t);case fi:i=8,l|=8;break;case co:return e=_e(12,n,t,l|2),e.elementType=co,e.lanes=o,e;case fo:return e=_e(13,n,t,l),e.elementType=fo,e.lanes=o,e;case po:return e=_e(19,n,t,l),e.elementType=po,e.lanes=o,e;case Fs:return El(n,l,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ms:i=10;break e;case Ds:i=9;break e;case di:i=11;break e;case pi:i=14;break e;case tt:i=16,r=null;break e}throw Error(g(130,e==null?e:typeof e,""))}return t=_e(i,n,t,l),t.elementType=e,t.type=r,t.lanes=o,t}function Rt(e,t,n,r){return e=_e(7,e,r,t),e.lanes=n,e}function El(e,t,n,r){return e=_e(22,e,r,t),e.elementType=Fs,e.lanes=n,e.stateNode={isHidden:!1},e}function lo(e,t,n){return e=_e(6,e,null,t),e.lanes=n,e}function oo(e,t,n){return t=_e(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function hp(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Al(0),this.expirationTimes=Al(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Al(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Gi(e,t,n,r,l,o,i,u,s){return e=new hp(e,t,n,u,s),t===1?(t=1,o===!0&&(t|=8)):t=0,o=_e(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},zi(o),e}function mp(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(zc)}catch(e){console.error(e)}}zc(),zs.exports=ke;var Sp=zs.exports,ss=Sp;so.createRoot=ss.createRoot,so.hydrateRoot=ss.hydrateRoot;/** + * @remix-run/router v1.23.2 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function al(){return al=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function jc(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Ep(){return Math.random().toString(36).substr(2,8)}function cs(e,t){return{usr:e.state,key:e.key,idx:t}}function ni(e,t,n,r){return n===void 0&&(n=null),al({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Pl(t):t,{state:n,key:t&&t.key||r||Ep()})}function Oc(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Pl(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Cp(e,t,n,r){r===void 0&&(r={});let{window:l=document.defaultView,v5Compat:o=!1}=r,i=l.history,u=ut.Pop,s=null,a=h();a==null&&(a=0,i.replaceState(al({},i.state,{idx:a}),""));function h(){return(i.state||{idx:null}).idx}function d(){u=ut.Pop;let x=h(),f=x==null?null:x-a;a=x,s&&s({action:u,location:S.location,delta:f})}function m(x,f){u=ut.Push;let c=ni(S.location,x,f);a=h()+1;let p=cs(c,a),v=S.createHref(c);try{i.pushState(p,"",v)}catch(E){if(E instanceof DOMException&&E.name==="DataCloneError")throw E;l.location.assign(v)}o&&s&&s({action:u,location:S.location,delta:1})}function y(x,f){u=ut.Replace;let c=ni(S.location,x,f);a=h();let p=cs(c,a),v=S.createHref(c);i.replaceState(p,"",v),o&&s&&s({action:u,location:S.location,delta:0})}function w(x){let f=l.location.origin!=="null"?l.location.origin:l.location.href,c=typeof x=="string"?x:Oc(x);return c=c.replace(/ $/,"%20"),ve(f,"No window.location.(origin|href) available to create URL for href: "+c),new URL(c,f)}let S={get action(){return u},get location(){return e(l,i)},listen(x){if(s)throw new Error("A history only accepts one active listener");return l.addEventListener(as,d),s=x,()=>{l.removeEventListener(as,d),s=null}},createHref(x){return t(l,x)},createURL:w,encodeLocation(x){let f=w(x);return{pathname:f.pathname,search:f.search,hash:f.hash}},push:m,replace:y,go(x){return i.go(x)}};return S}var fs;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(fs||(fs={}));function xp(e,t,n){return n===void 0&&(n="/"),_p(e,t,n)}function _p(e,t,n,r){let l=typeof t=="string"?Pl(t):t,o=Dc(l.pathname||"/",n);if(o==null)return null;let i=Ic(e);Np(i);let u=null;for(let s=0;u==null&&s{let s={relativePath:u===void 0?o.path||"":u,caseSensitive:o.caseSensitive===!0,childrenIndex:i,route:o};s.relativePath.startsWith("/")&&(ve(s.relativePath.startsWith(r),'Absolute route path "'+s.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),s.relativePath=s.relativePath.slice(r.length));let a=rn([r,s.relativePath]),h=n.concat(s);o.children&&o.children.length>0&&(ve(o.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+a+'".')),Ic(o.children,t,h,a)),!(o.path==null&&!o.index)&&t.push({path:a,score:Op(a,o.index),routesMeta:h})};return e.forEach((o,i)=>{var u;if(o.path===""||!((u=o.path)!=null&&u.includes("?")))l(o,i);else for(let s of Mc(o.path))l(o,i,s)}),t}function Mc(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,l=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return l?[o,""]:[o];let i=Mc(r.join("/")),u=[];return u.push(...i.map(s=>s===""?o:[o,s].join("/"))),l&&u.push(...i),u.map(s=>e.startsWith("/")&&s===""?"/":s)}function Np(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:Ip(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Pp=/^:[\w-]+$/,Tp=3,Rp=2,Lp=1,zp=10,jp=-2,ds=e=>e==="*";function Op(e,t){let n=e.split("/"),r=n.length;return n.some(ds)&&(r+=jp),t&&(r+=Rp),n.filter(l=>!ds(l)).reduce((l,o)=>l+(Pp.test(o)?Tp:o===""?Lp:zp),r)}function Ip(e,t){return e.length===t.length&&e.slice(0,-1).every((r,l)=>r===t[l])?e[e.length-1]-t[t.length-1]:0}function Mp(e,t,n){let{routesMeta:r}=e,l={},o="/",i=[];for(let u=0;u{let{paramName:m,isOptional:y}=h;if(m==="*"){let S=u[d]||"";i=o.slice(0,o.length-S.length).replace(/(.)\/+$/,"$1")}const w=u[d];return y&&!w?a[m]=void 0:a[m]=(w||"").replace(/%2F/g,"/"),a},{}),pathname:o,pathnameBase:i,pattern:e}}function Fp(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),jc(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],l="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(i,u,s)=>(r.push({paramName:u,isOptional:s!=null}),s?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(r.push({paramName:"*"}),l+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?l+="\\/*$":e!==""&&e!=="/"&&(l+="(?:(?=\\/|$))"),[new RegExp(l,t?void 0:"i"),r]}function Up(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return jc(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function Dc(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}const rn=e=>e.join("/").replace(/\/\/+/g,"/"),Ap=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/");function Bp(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const Fc=["post","put","patch","delete"];new Set(Fc);const $p=["get",...Fc];new Set($p);/** + * React Router v6.30.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function cl(){return cl=Object.assign?Object.assign.bind():function(e){for(var t=1;tObject.assign({},x,{params:Object.assign({},u,x.params),pathname:rn([s,l.encodeLocation?l.encodeLocation(x.pathname).pathname:x.pathname]),pathnameBase:x.pathnameBase==="/"?s:rn([s,l.encodeLocation?l.encodeLocation(x.pathnameBase).pathname:x.pathnameBase])})),o,n,r);return t&&S?N.createElement(Tl.Provider,{value:{location:cl({pathname:"/",search:"",hash:"",state:null,key:"default"},h),navigationType:ut.Pop}},S):S}function Kp(){let e=th(),t=Bp(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,l={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"};return N.createElement(N.Fragment,null,N.createElement("h2",null,"Unexpected Application Error!"),N.createElement("h3",{style:{fontStyle:"italic"}},t),n?N.createElement("pre",{style:l},n):null,null)}const Gp=N.createElement(Kp,null);class Xp extends N.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,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error!==void 0?t.error:n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error!==void 0?N.createElement(Rl.Provider,{value:this.props.routeContext},N.createElement(Ac.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function Zp(e){let{routeContext:t,match:n,children:r}=e,l=N.useContext(Vp);return l&&l.static&&l.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(l.staticContext._deepestRenderedBoundaryId=n.route.id),N.createElement(Rl.Provider,{value:t},r)}function Jp(e,t,n,r){var l;if(t===void 0&&(t=[]),n===void 0&&(n=null),r===void 0&&(r=null),e==null){var o;if(!n)return null;if(n.errors)e=n.matches;else if((o=r)!=null&&o.v7_partialHydration&&t.length===0&&!n.initialized&&n.matches.length>0)e=n.matches;else return null}let i=e,u=(l=n)==null?void 0:l.errors;if(u!=null){let h=i.findIndex(d=>d.route.id&&(u==null?void 0:u[d.route.id])!==void 0);h>=0||ve(!1),i=i.slice(0,Math.min(i.length,h+1))}let s=!1,a=-1;if(n&&r&&r.v7_partialHydration)for(let h=0;h=0?i=i.slice(0,a+1):i=[i[0]];break}}}return i.reduceRight((h,d,m)=>{let y,w=!1,S=null,x=null;n&&(y=u&&d.route.id?u[d.route.id]:void 0,S=d.route.errorElement||Gp,s&&(a<0&&m===0?(nh("route-fallback"),w=!0,x=null):a===m&&(w=!0,x=d.route.hydrateFallbackElement||null)));let f=t.concat(i.slice(0,m+1)),c=()=>{let p;return y?p=S:w?p=x:d.route.Component?p=N.createElement(d.route.Component,null):d.route.element?p=d.route.element:p=h,N.createElement(Zp,{match:d,routeContext:{outlet:h,matches:f,isDataRoute:n!=null},children:p})};return n&&(d.route.ErrorBoundary||d.route.errorElement||m===0)?N.createElement(Xp,{location:n.location,revalidation:n.revalidation,component:S,error:y,children:c(),routeContext:{outlet:null,matches:f,isDataRoute:!0}}):c()},null)}var Bc=function(e){return e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId",e}(Bc||{});function qp(e){let t=N.useContext(Wp);return t||ve(!1),t}function bp(e){let t=N.useContext(Rl);return t||ve(!1),t}function eh(e){let t=bp(),n=t.matches[t.matches.length-1];return n.route.id||ve(!1),n.route.id}function th(){var e;let t=N.useContext(Ac),n=qp(Bc.UseRouteError),r=eh();return t!==void 0?t:(e=n.errors)==null?void 0:e[r]}const ps={};function nh(e,t,n){ps[e]||(ps[e]=!0)}function rh(e,t){e==null||e.v7_startTransition,e==null||e.v7_relativeSplatPath}function Rn(e){ve(!1)}function lh(e){let{basename:t="/",children:n=null,location:r,navigationType:l=ut.Pop,navigator:o,static:i=!1,future:u}=e;qi()&&ve(!1);let s=t.replace(/^\/*/,"/"),a=N.useMemo(()=>({basename:s,navigator:o,static:i,future:cl({v7_relativeSplatPath:!1},u)}),[s,u,o,i]);typeof r=="string"&&(r=Pl(r));let{pathname:h="/",search:d="",hash:m="",state:y=null,key:w="default"}=r,S=N.useMemo(()=>{let x=Dc(h,s);return x==null?null:{location:{pathname:x,search:d,hash:m,state:y,key:w},navigationType:l}},[s,h,d,m,y,w,l]);return S==null?null:N.createElement(Uc.Provider,{value:a},N.createElement(Tl.Provider,{children:n,value:S}))}function oh(e){let{children:t,location:n}=e;return Qp(ri(t),n)}new Promise(()=>{});function ri(e,t){t===void 0&&(t=[]);let n=[];return N.Children.forEach(e,(r,l)=>{if(!N.isValidElement(r))return;let o=[...t,l];if(r.type===N.Fragment){n.push.apply(n,ri(r.props.children,o));return}r.type!==Rn&&ve(!1),!r.props.index||!r.props.children||ve(!1);let i={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(i.children=ri(r.props.children,o)),n.push(i)}),n}/** + * React Router DOM v6.30.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */const ih="6";try{window.__reactRouterVersion=ih}catch{}const uh="startTransition",hs=af[uh];function sh(e){let{basename:t,children:n,future:r,window:l}=e,o=N.useRef();o.current==null&&(o.current=kp({window:l,v5Compat:!0}));let i=o.current,[u,s]=N.useState({action:i.action,location:i.location}),{v7_startTransition:a}=r||{},h=N.useCallback(d=>{a&&hs?hs(()=>s(d)):s(d)},[s,a]);return N.useLayoutEffect(()=>i.listen(h),[i,h]),N.useEffect(()=>rh(r),[r]),N.createElement(lh,{basename:t,children:n,location:u.location,navigationType:u.action,navigator:i,future:r})}var ms;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmit="useSubmit",e.UseSubmitFetcher="useSubmitFetcher",e.UseFetcher="useFetcher",e.useViewTransitionState="useViewTransitionState"})(ms||(ms={}));var vs;(function(e){e.UseFetcher="useFetcher",e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(vs||(vs={}));function Ll({children:e,className:t=""}){return k.jsx("div",{className:`page-layout ${t}`.trim(),children:e})}const q=800,Be=220,$e=168,ah=.58,ch=-13.5,io=5.2,fh=55e-5,dh=16,$c=82,Vc=36,Ut=36,gs=340,ph=680,ys=[{lines:["Crash","Loop"],color:"#ff4455",w:46,h:52},{lines:["OOM","Killed"],color:"#ff2288",w:38,h:60},{lines:["Image","PullErr"],color:"#ff8820",w:44,h:46},{lines:["404"],color:"#00ffff",w:58,h:36},{lines:["Expired","Cert"],color:"#ffee22",w:48,h:48},{lines:["Evicted"],color:"#aaaaaa",w:40,h:34},{lines:["Pod","Failed"],color:"#ff3366",w:40,h:44},{lines:["Bad","Ingress"],color:"#ff6600",w:50,h:40},{lines:["DNS","Fail"],color:"#cc44ff",w:42,h:50},{lines:["Pending","..."],color:"#4499ff",w:52,h:32},{lines:["Timeout"],color:"#ff9944",w:48,h:42},{lines:["502"],color:"#ff4444",w:52,h:36},{lines:["No","Route"],color:"#55ff88",w:46,h:44},{lines:["Tunnel","Down"],color:"#ff55cc",w:50,h:46}];function hh(){const e=ys[Math.floor(Math.random()*ys.length)];return{...e,x:q+60,y:$e-e.h}}function mh(e,t,n,r,l,o,i,u,s=7){return e+sl+s&&t+so+s}function Ur(e,t,n,r,l,o=5){e.beginPath(),e.moveTo(t+o,n),e.lineTo(t+r-o,n),e.quadraticCurveTo(t+r,n,t+r,n+o),e.lineTo(t+r,n+l-o),e.quadraticCurveTo(t+r,n+l,t+r-o,n+l),e.lineTo(t+o,n+l),e.quadraticCurveTo(t,n+l,t,n+l-o),e.lineTo(t,n+o),e.quadraticCurveTo(t,n,t+o,n),e.closePath()}function vh(e,t){e.fillStyle="#080c14",e.fillRect(0,0,q,Be),e.fillStyle="rgba(0, 180, 255, 0.055)";const n=28,r=-t%n-n;for(let l=r;l0&&t>=n&&(e.fillStyle="#ffee22",e.font="bold 11px monospace",e.fillText("โœฆ new high score โœฆ",q/2,Be/2+16)),e.fillStyle="#00ffcc",e.font="12px monospace",e.fillText("[ SPACE / โ†‘ / TAP to respawn ]",q/2,Be/2+38),e.restore()}function Ch(){const e=N.useRef(null),t=N.useRef({status:"idle",playerY:$e-Ut,velocityY:0,isJumping:!1,obstacles:[],bgOffset:0,frameCount:0,distTravelled:0,nextSpawnAt:400,speed:io,score:0,highScore:parseInt(localStorage.getItem("lab404_hi")??"0",10)}),[n,r]=N.useState("idle"),l=N.useCallback(()=>{const o=t.current;o.status==="idle"?(o.status="playing",r("playing")):o.status==="playing"&&!o.isJumping?(o.velocityY=ch,o.isJumping=!0):o.status==="gameover"&&(o.status="playing",o.playerY=$e-Ut,o.velocityY=0,o.isJumping=!1,o.obstacles=[],o.bgOffset=0,o.frameCount=0,o.distTravelled=0,o.nextSpawnAt=400,o.speed=io,o.score=0,r("playing"))},[]);return N.useEffect(()=>{const o=i=>{(i.code==="Space"||i.code==="ArrowUp")&&(i.preventDefault(),l())};return window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[l]),N.useEffect(()=>{const i=e.current.getContext("2d");let u;const s=()=>{const a=t.current;if(a.status==="playing"){if(a.frameCount++,a.score=Math.floor(a.frameCount/7),a.speed=Math.min(io+a.score*fh,dh),a.bgOffset=(a.bgOffset+a.speed*.35)%q,a.distTravelled+=a.speed,a.velocityY+=ah,a.playerY+=a.velocityY,a.playerY>=$e-Ut&&(a.playerY=$e-Ut,a.velocityY=0,a.isJumping=!1),a.distTravelled>=a.nextSpawnAt){a.obstacles.push(hh());const d=gs+Math.random()*(ph-gs);a.nextSpawnAt=a.distTravelled+d}for(const d of a.obstacles)d.x-=a.speed;a.obstacles=a.obstacles.filter(d=>d.x+d.w>-10),a.obstacles.some(d=>mh($c,a.playerY,Vc,Ut,d.x,d.y,d.w,d.h))&&(a.score>a.highScore&&(a.highScore=a.score,localStorage.setItem("lab404_hi",String(a.score))),a.status="gameover",r("gameover"))}else a.bgOffset=(a.bgOffset+.4)%q;vh(i,a.bgOffset),gh(i,a.bgOffset),a.obstacles.forEach(h=>wh(i,h)),yh(i,a.playerY,a.frameCount,a.isJumping),Sh(i,a.score,a.highScore),a.status==="idle"&&kh(i),a.status==="gameover"&&Eh(i,a.score,a.highScore),u=requestAnimationFrame(s)};return u=requestAnimationFrame(s),()=>cancelAnimationFrame(u)},[]),k.jsx("div",{className:"dino-game-wrapper",onClick:l,onTouchStart:o=>{o.preventDefault(),l()},children:k.jsx("canvas",{ref:e,width:q,height:Be,className:"dino-game-canvas",role:"img","aria-label":"Kubernetes pod runner mini-game. Press Space or tap to jump."})})}const uo=[{type:"cmd",text:"$ kubectl get page /this-path --namespace default"},{type:"err",text:'Error from server (NotFound): pages "this-path" not found'},{type:"cmd",text:"$ kubectl describe ingress/main-ingress"},{type:"warn",text:'[WARN] ingress-nginx: no matching rule for path "/this-path"'},{type:"cmd",text:"$ nslookup this-page.cluster.local"},{type:"err",text:"NXDOMAIN: Name does not exist."},{type:"cmd",text:"$ kubectl get pod -l app=this-page"},{type:"err",text:"No resources found in default namespace."},{type:"warn",text:"[WARN] cloudflared: tunnel endpoint unreachable"},{type:"info",text:"[INFO] attempting to reschedule pod..."},{type:"err",text:'[ERROR] ImagePullBackOff: image "this-page:latest" not found'},{type:"cmd",text:"$ kubectl rollout status deploy/this-page"},{type:"err",text:'error: deployment "this-page" not found'},{type:"warn",text:"[WARN] certificate for this-page.lab expired 42 days ago"},{type:"info",text:"[INFO] DNS TTL 30s โ€” retrying..."},{type:"err",text:'[ERROR] upstream "this-page:8080": connection refused'},{type:"cmd",text:"$ kubectl logs -l app=this-page --tail=5"},{type:"err",text:'Error from server (NotFound): pods "this-page" not found'},{type:"warn",text:"[WARN] 3 consecutive CrashLoopBackOff events detected"},{type:"info",text:"[INFO] route table: /this-path โ†’ ??? (unresolved)"}],xh=1700;function Wc({title:e="cluster-diagnostics"}){const[t,n]=N.useState([uo[0]]),r=N.useRef(1),l=N.useRef(null);return N.useEffect(()=>{const o=setInterval(()=>{const i=uo[r.current%uo.length];r.current+=1,n(u=>[...u,i].slice(-6))},xh);return()=>clearInterval(o)},[]),N.useEffect(()=>{l.current&&(l.current.scrollTop=l.current.scrollHeight)},[t]),k.jsxs("div",{className:"terminal-log",children:[k.jsxs("div",{className:"terminal-header",children:[k.jsx("span",{className:"terminal-dot red"}),k.jsx("span",{className:"terminal-dot yellow"}),k.jsx("span",{className:"terminal-dot green"}),k.jsx("span",{className:"terminal-title",children:e})]}),k.jsxs("div",{className:"terminal-body",ref:l,children:[t.map((o,i)=>k.jsx("div",{className:`terminal-line terminal-line--${o.type}${i===t.length-1?" terminal-line--new":""}`,children:o.text},i)),k.jsx("span",{className:"terminal-cursor",children:"โ–‹"})]})]})}function _h(){return k.jsx(Ll,{children:k.jsxs("div",{className:"nfp-container",children:[k.jsxs("header",{className:"nfp-header",children:[k.jsx("h1",{className:"nfp-code","aria-label":"Error 404",children:"404"}),k.jsx("h2",{className:"nfp-title",children:"Page Not Found"}),k.jsx("p",{className:"nfp-subtitle",children:"This route was lost somewhere in the cluster."}),k.jsxs("code",{className:"nfp-kubectl",children:["$ kubectl get page: ",k.jsx("span",{className:"err",children:"NotFound"})]})]}),k.jsxs("section",{className:"nfp-game-section","aria-label":"Mini-game",children:[k.jsx(Ch,{}),k.jsx("p",{className:"nfp-game-hint",children:"space / โ†‘ / tap โ€” jump ย ยทย  dodge the cluster faults"})]}),k.jsx("section",{className:"nfp-log-section","aria-label":"Cluster diagnostics log",children:k.jsx(Wc,{title:"cluster-diagnostics โ€” 404"})}),k.jsxs("footer",{className:"nfp-footer",children:[k.jsx("a",{href:"/",className:"nfp-btn nfp-btn--primary",children:"โ† Go Home"}),k.jsx("a",{href:"https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/",className:"nfp-btn nfp-btn--secondary",target:"_blank",rel:"noopener noreferrer",children:"kubectl describe pod"})]})]})})}function Nh(){return k.jsx(Ll,{children:k.jsxs("div",{className:"bgp-container",children:[k.jsxs("header",{className:"bgp-header",children:[k.jsx("h1",{className:"bgp-code","aria-label":"Error 502",children:"502"}),k.jsx("h2",{className:"bgp-title",children:"Bad Gateway"}),k.jsx("p",{className:"bgp-subtitle",children:"The upstream service isn't responding. The proxy is confused."}),k.jsxs("code",{className:"bgp-kubectl",children:["$ curl -I https://app.lab โ€” ",k.jsx("span",{className:"err",children:"502 Bad Gateway"})]})]}),k.jsx("section",{className:"bgp-log-section","aria-label":"Gateway diagnostics",children:k.jsx(Wc,{title:"gateway-diagnostics โ€” 502"})}),k.jsxs("footer",{className:"bgp-footer",children:[k.jsx("a",{href:"/",className:"bgp-btn bgp-btn--primary",children:"โ† Go Home"}),k.jsx("a",{href:"/maintenance",className:"bgp-btn bgp-btn--secondary",children:"Check maintenance status"})]})]})})}function Ph(){const[e,t]=N.useState(".");return N.useEffect(()=>{const n=setInterval(()=>{t(r=>r.length>=3?".":r+".")},600);return()=>clearInterval(n)},[]),k.jsx(Ll,{children:k.jsxs("div",{className:"maint-container",children:[k.jsxs("header",{className:"maint-header",children:[k.jsx("div",{className:"maint-icon","aria-hidden":"true",children:"๐Ÿ”ง"}),k.jsx("h1",{className:"maint-title",children:"Under Maintenance"}),k.jsxs("p",{className:"maint-subtitle",children:["The lab is being upgraded",e]}),k.jsxs("code",{className:"maint-status",children:["$ kubectl rollout status deploy/lab โ€” ",k.jsx("span",{className:"info",children:"Waiting"})]})]}),k.jsx("div",{className:"maint-progress",role:"progressbar","aria-label":"Maintenance in progress",children:k.jsx("div",{className:"maint-progress-bar"})}),k.jsx("p",{className:"maint-note",children:"Services will be back shortly. Check back in a few minutes."}),k.jsx("footer",{className:"maint-footer",children:k.jsx("button",{className:"maint-btn",onClick:()=>window.location.reload(),children:"โ†บ Refresh"})})]})})}function Th(){return k.jsx(Ll,{children:k.jsxs("div",{className:"home-container",children:[k.jsxs("header",{className:"home-header",children:[k.jsx("div",{className:"home-logo","aria-hidden":"true",children:"โŽˆ"}),k.jsx("h1",{className:"home-title",children:"ErrorLab"}),k.jsx("p",{className:"home-subtitle",children:"Home Lab / DevOps Dashboard"}),k.jsx("code",{className:"home-status",children:"cluster: online ย ยทย  nodes: healthy"})]}),k.jsx("section",{className:"home-services","aria-label":"Services",children:[{name:"App",status:"running",path:"#"},{name:"Monitoring",status:"running",path:"#"},{name:"CI/CD",status:"running",path:"#"},{name:"Storage",status:"pending",path:"#"}].map(e=>k.jsxs("a",{href:e.path,className:`home-card home-card--${e.status}`,children:[k.jsx("span",{className:"home-card-name",children:e.name}),k.jsx("span",{className:`home-card-badge home-card-badge--${e.status}`,children:e.status})]},e.name))}),k.jsx("footer",{className:"home-footer",children:k.jsxs("span",{className:"home-footer-text",children:["Something broken? ย ",k.jsx("a",{href:"/404",className:"home-link",children:"See the 404 game"})]})})]})})}function Rh(){return k.jsx(sh,{children:k.jsxs(oh,{children:[k.jsx(Rn,{path:"/",element:k.jsx(Th,{})}),k.jsx(Rn,{path:"/502",element:k.jsx(Nh,{})}),k.jsx(Rn,{path:"/maintenance",element:k.jsx(Ph,{})}),k.jsx(Rn,{path:"*",element:k.jsx(_h,{})})]})})}so.createRoot(document.getElementById("root")).render(k.jsx(Rs.StrictMode,{children:k.jsx(Rh,{})})); diff --git a/dist/assets/index-DmKezAnz.css b/dist/assets/index-DmKezAnz.css new file mode 100644 index 0000000..e0bf5f9 --- /dev/null +++ b/dist/assets/index-DmKezAnz.css @@ -0,0 +1 @@ +.page-layout{min-height:100vh;background:var(--bg);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 1rem;position:relative;overflow:hidden}.page-layout:before{content:"";position:fixed;top:0;right:0;bottom:0;left:0;background-image:radial-gradient(1px 1px at 15% 20%,rgba(0,200,255,.18) 0%,transparent 100%),radial-gradient(1px 1px at 42% 8%,rgba(0,255,204,.12) 0%,transparent 100%),radial-gradient(1px 1px at 70% 35%,rgba(0,200,255,.15) 0%,transparent 100%),radial-gradient(1px 1px at 88% 60%,rgba(0,255,204,.1) 0%,transparent 100%),radial-gradient(1px 1px at 25% 75%,rgba(0,200,255,.12) 0%,transparent 100%),radial-gradient(1px 1px at 55% 90%,rgba(0,255,204,.08) 0%,transparent 100%);pointer-events:none;z-index:0}.page-layout>*{position:relative;z-index:1}.dino-game-wrapper{width:100%;max-width:820px;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.dino-game-canvas{width:100%;height:auto;display:block;border:1px solid rgba(0,200,255,.18);border-radius:8px;box-shadow:0 0 0 1px #00000080,0 0 32px #00c8ff14}.terminal-log{width:100%;max-width:620px;background:var(--surface);border:1px solid var(--border);border-radius:8px;overflow:hidden;box-shadow:0 0 24px #00b4ff12}.terminal-header{display:flex;align-items:center;gap:6px;padding:8px 14px;background:var(--surface-alt);border-bottom:1px solid var(--border);-webkit-user-select:none;user-select:none}.terminal-dot{width:10px;height:10px;border-radius:50%}.terminal-dot.red{background:#ff5f57}.terminal-dot.yellow{background:#febc2e}.terminal-dot.green{background:#28c840}.terminal-title{margin-left:6px;font-size:.72rem;color:var(--text-muted);letter-spacing:.04em}.terminal-body{padding:12px 16px 10px;min-height:110px;display:flex;flex-direction:column;gap:3px;overflow:hidden}.terminal-line{font-size:.78rem;line-height:1.5;white-space:pre-wrap;word-break:break-all}.terminal-line--cmd{color:var(--accent)}.terminal-line--info{color:var(--cyan-dim)}.terminal-line--warn{color:var(--warning)}.terminal-line--err{color:var(--danger)}@keyframes slideInLine{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.terminal-line--new{animation:slideInLine .3s ease-out both}.terminal-cursor{display:inline-block;color:var(--accent);font-size:.85rem;animation:blink 1.1s step-end infinite}@keyframes blink{0%,to{opacity:1}50%{opacity:0}}.nfp-container{width:100%;max-width:860px;padding:2.5rem 1.5rem 3rem;display:flex;flex-direction:column;align-items:center;gap:2rem;text-align:center}.nfp-header{display:flex;flex-direction:column;align-items:center;gap:.6rem}@keyframes glitch{0%,88%,to{transform:none;text-shadow:-2px 0 #00ffcc,2px 0 #ff4455}90%{transform:translate(-2px);color:#f45}92%{transform:translate(2px);color:#0fc}94%{transform:translate(-1px)}96%{transform:none}}@keyframes fadeInScale{0%{opacity:0;transform:scale(.85)}to{opacity:1;transform:scale(1)}}.nfp-code{font-size:clamp(5.5rem,18vw,10.5rem);font-weight:900;letter-spacing:.06em;line-height:1;color:transparent;-webkit-text-stroke:2px var(--accent);text-shadow:-2px 0 #00ffcc,2px 0 #ff4455,0 0 60px rgba(0,255,204,.25);animation:fadeInScale .7s ease-out both,glitch 5s ease-in-out 1.5s infinite}.nfp-title{font-size:clamp(1.1rem,3vw,1.6rem);font-weight:700;color:var(--text);animation:fadeInScale .7s .15s ease-out both}.nfp-subtitle{font-size:clamp(.82rem,2vw,1rem);color:var(--cyan-dim);animation:fadeInScale .7s .3s ease-out both}.nfp-kubectl{display:inline-block;font-size:.8rem;color:var(--text-muted);background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:.35rem .8rem;animation:fadeInScale .7s .45s ease-out both}.nfp-kubectl .err{color:var(--danger)}.nfp-game-section{width:100%;animation:fadeInScale .7s .55s ease-out both}.nfp-game-hint{margin-top:.55rem;font-size:.72rem;color:var(--text-muted);letter-spacing:.03em}.nfp-log-section{width:100%;display:flex;justify-content:center;animation:fadeInScale .7s .65s ease-out both}.nfp-footer{display:flex;gap:.9rem;flex-wrap:wrap;justify-content:center;animation:fadeInScale .7s .75s ease-out both}.nfp-btn{display:inline-flex;align-items:center;gap:.45rem;padding:.55rem 1.4rem;border-radius:6px;font-family:var(--font-mono);font-size:.85rem;font-weight:600;text-decoration:none;cursor:pointer;transition:background .18s,border-color .18s,box-shadow .18s;border:1px solid transparent}.nfp-btn--primary{background:#00ffcc1a;border-color:#0fc6;color:var(--accent)}.nfp-btn--primary:hover{background:#00ffcc2e;border-color:var(--accent);box-shadow:0 0 18px var(--accent-glow)}.nfp-btn--secondary{background:#ffffff0d;border-color:#ffffff24;color:var(--text-muted)}.nfp-btn--secondary:hover{background:#ffffff17;border-color:#ffffff47;color:var(--text)}@media (max-width: 480px){.nfp-container{gap:1.5rem;padding:1.5rem 1rem 2rem}.nfp-footer{gap:.65rem}.nfp-btn{font-size:.8rem;padding:.5rem 1.1rem}}.bgp-container{width:100%;max-width:700px;padding:2.5rem 1.5rem 3rem;display:flex;flex-direction:column;align-items:center;gap:2rem;text-align:center}.bgp-header{display:flex;flex-direction:column;align-items:center;gap:.6rem}.bgp-code{font-size:clamp(5rem,18vw,9rem);font-weight:900;letter-spacing:.06em;line-height:1;color:transparent;-webkit-text-stroke:2px var(--warning);text-shadow:-2px 0 var(--warning),2px 0 var(--danger),0 0 60px rgba(255,170,0,.25)}.bgp-title{font-size:clamp(1.1rem,3vw,1.5rem);font-weight:700;color:var(--text)}.bgp-subtitle{font-size:.9rem;color:var(--cyan-dim)}.bgp-kubectl{font-size:.8rem;color:var(--text-muted);background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:.35rem .8rem}.bgp-kubectl .err{color:var(--danger)}.bgp-log-section{width:100%;display:flex;justify-content:center}.bgp-footer{display:flex;gap:.9rem;flex-wrap:wrap;justify-content:center}.bgp-btn{display:inline-flex;align-items:center;gap:.45rem;padding:.55rem 1.4rem;border-radius:6px;font-family:var(--font-mono);font-size:.85rem;font-weight:600;text-decoration:none;cursor:pointer;transition:background .18s,border-color .18s,box-shadow .18s;border:1px solid transparent}.bgp-btn--primary{background:#ffaa001a;border-color:#fa06;color:var(--warning)}.bgp-btn--primary:hover{background:#ffaa002e;border-color:var(--warning);box-shadow:0 0 18px #ffaa004d}.bgp-btn--secondary{background:#ffffff0d;border-color:#ffffff24;color:var(--text-muted)}.bgp-btn--secondary:hover{background:#ffffff17;border-color:#ffffff47;color:var(--text)}.maint-container{width:100%;max-width:600px;padding:3rem 1.5rem;display:flex;flex-direction:column;align-items:center;gap:1.8rem;text-align:center}.maint-header{display:flex;flex-direction:column;align-items:center;gap:.7rem}.maint-icon{font-size:3.5rem}.maint-title{font-size:clamp(1.5rem,4vw,2.2rem);font-weight:800;color:var(--warning);text-shadow:0 0 30px rgba(255,170,0,.3)}.maint-subtitle{font-size:1rem;color:var(--text);min-height:1.6em}.maint-status{font-size:.78rem;color:var(--text-muted);background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:.35rem .8rem}.maint-status .info{color:var(--cyan)}.maint-progress{width:100%;max-width:400px;height:4px;background:var(--surface);border-radius:2px;overflow:hidden}@keyframes indeterminate{0%{transform:translate(-100%)}to{transform:translate(400%)}}.maint-progress-bar{height:100%;width:25%;background:linear-gradient(90deg,transparent,var(--warning),transparent);animation:indeterminate 1.8s linear infinite}.maint-note{font-size:.82rem;color:var(--text-muted)}.maint-footer{display:flex;justify-content:center}.maint-btn{padding:.55rem 1.6rem;border-radius:6px;border:1px solid rgba(255,170,0,.35);background:#ffaa001a;color:var(--warning);font-family:var(--font-mono);font-size:.85rem;font-weight:600;cursor:pointer;transition:background .18s,border-color .18s,box-shadow .18s}.maint-btn:hover{background:#ffaa002e;border-color:var(--warning);box-shadow:0 0 16px #ffaa0047}.home-container{width:100%;max-width:680px;padding:3rem 1.5rem;display:flex;flex-direction:column;align-items:center;gap:2.2rem;text-align:center}.home-header{display:flex;flex-direction:column;align-items:center;gap:.6rem}.home-logo{font-size:4rem;text-shadow:0 0 30px var(--accent-glow)}.home-title{font-size:clamp(2rem,6vw,3rem);font-weight:900;color:transparent;-webkit-text-stroke:2px var(--accent);text-shadow:0 0 40px var(--accent-glow)}.home-subtitle{font-size:1rem;color:var(--cyan-dim)}.home-status{font-size:.78rem;color:var(--text-muted);background:var(--surface);border:1px solid var(--border);border-radius:4px;padding:.3rem .8rem}.home-services{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.8rem;width:100%}.home-card{display:flex;flex-direction:column;align-items:center;gap:.5rem;padding:1rem .8rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;text-decoration:none;color:var(--text);transition:border-color .18s,box-shadow .18s}.home-card:hover{border-color:var(--accent);box-shadow:0 0 16px var(--accent-dim)}.home-card-name{font-size:.9rem;font-weight:600}.home-card-badge{font-size:.7rem;padding:.2rem .5rem;border-radius:3px;letter-spacing:.04em}.home-card-badge--running{background:#00ff881f;color:#0f8}.home-card-badge--pending{background:#4499ff1f;color:#49f}.home-card-badge--error{background:#ff44551f;color:var(--danger)}.home-footer{margin-top:.5rem}.home-footer-text{font-size:.82rem;color:var(--text-muted)}.home-link{color:var(--accent);text-decoration:underline;text-underline-offset:3px}.home-link:hover{color:var(--cyan)}:root{--bg: #080c14;--surface: #0d1520;--surface-alt: #111827;--border: rgba(0, 200, 255, .14);--accent: #00ffcc;--accent-dim: rgba(0, 255, 204, .25);--accent-glow: rgba(0, 255, 204, .35);--cyan: #00ccff;--cyan-dim: rgba(0, 200, 255, .5);--text: #e0e8f0;--text-muted: rgba(200, 220, 240, .45);--danger: #ff4455;--warning: #ffaa00;--font-mono: "Consolas", "Monaco", "Courier New", monospace}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--font-mono);-webkit-font-smoothing:antialiased;overflow-x:hidden}#root{min-height:100%;display:flex;flex-direction:column}a{color:inherit;text-decoration:none}::-webkit-scrollbar{width:5px;height:5px}::-webkit-scrollbar-track{background:var(--bg)}::-webkit-scrollbar-thumb{background:#00c8ff40;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#00c8ff66} diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..fd09f5d --- /dev/null +++ b/dist/index.html @@ -0,0 +1,14 @@ + + + + + + + ErrorLab + + + + +
+ + diff --git a/helm/errorlab/Chart.yaml b/helm/errorlab/Chart.yaml new file mode 100644 index 0000000..d7a3cdd --- /dev/null +++ b/helm/errorlab/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: errorlab +description: Custom public error and landing pages for the home lab +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - errorlab + - '404' + - homelab + - devops +home: "" +sources: [] +maintainers: + - name: dvirl diff --git a/helm/errorlab/templates/_helpers.tpl b/helm/errorlab/templates/_helpers.tpl new file mode 100644 index 0000000..618be17 --- /dev/null +++ b/helm/errorlab/templates/_helpers.tpl @@ -0,0 +1,49 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "errorlab.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "errorlab.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart label (name + version). +*/}} +{{- define "errorlab.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels applied to every resource. +*/}} +{{- define "errorlab.labels" -}} +helm.sh/chart: {{ include "errorlab.chart" . }} +{{ include "errorlab.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels (used in spec.selector.matchLabels and pod template labels). +*/}} +{{- define "errorlab.selectorLabels" -}} +app.kubernetes.io/name: {{ include "errorlab.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/helm/errorlab/templates/deployment.yaml b/helm/errorlab/templates/deployment.yaml new file mode 100644 index 0000000..8b14f0f --- /dev/null +++ b/helm/errorlab/templates/deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "errorlab.fullname" . }} + labels: + {{- include "errorlab.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "errorlab.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "errorlab.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/errorlab/templates/hpa.yaml b/helm/errorlab/templates/hpa.yaml new file mode 100644 index 0000000..995f2af --- /dev/null +++ b/helm/errorlab/templates/hpa.yaml @@ -0,0 +1,22 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "errorlab.fullname" . }} + labels: + {{- include "errorlab.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "errorlab.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} +{{- end }} diff --git a/helm/errorlab/templates/ingress.yaml b/helm/errorlab/templates/ingress.yaml new file mode 100644 index 0000000..6a91791 --- /dev/null +++ b/helm/errorlab/templates/ingress.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "errorlab.fullname" . }} + labels: + {{- include "errorlab.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- toYaml .Values.ingress.tls | nindent 4 }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "errorlab.fullname" $ }} + port: + name: http + {{- end }} + {{- end }} +{{- end }} diff --git a/helm/errorlab/templates/service.yaml b/helm/errorlab/templates/service.yaml new file mode 100644 index 0000000..d89930d --- /dev/null +++ b/helm/errorlab/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "errorlab.fullname" . }} + labels: + {{- include "errorlab.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "errorlab.selectorLabels" . | nindent 4 }} diff --git a/helm/errorlab/values.yaml b/helm/errorlab/values.yaml new file mode 100644 index 0000000..066d180 --- /dev/null +++ b/helm/errorlab/values.yaml @@ -0,0 +1,87 @@ +# โ”€โ”€ Replica count โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +replicaCount: 1 + +# โ”€โ”€ Image โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +image: + repository: errorlab # override with your registry, e.g. ghcr.io/dvirl/errorlab + tag: latest + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +# โ”€โ”€ Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +service: + type: ClusterIP + port: 80 + +# โ”€โ”€ Ingress โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +ingress: + enabled: true + className: nginx # set to your ingress class (e.g. "nginx", "traefik") + annotations: {} + # cert-manager.io/cluster-issuer: letsencrypt-prod + # nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + hosts: + - host: errorlab.lab # replace with your actual hostname + paths: + - path: / + pathType: Prefix + tls: [] + # - secretName: errorlab-tls + # hosts: + # - errorlab.lab + +# โ”€โ”€ Resources โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + +# โ”€โ”€ Pod settings โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +podAnnotations: {} +podLabels: {} + +podSecurityContext: + runAsNonRoot: true + runAsUser: 101 # nginx user in nginx:alpine + fsGroup: 101 + +securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false # nginx needs to write to /var/cache/nginx + capabilities: + drop: + - ALL + +# โ”€โ”€ Probes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 5 + periodSeconds: 20 + +readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 3 + periodSeconds: 10 + +# โ”€โ”€ Autoscaling (disabled by default for a static site) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + +# โ”€โ”€ Misc โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +nameOverride: "" +fullnameOverride: "" + +nodeSelector: {} +tolerations: [] +affinity: {} diff --git a/index.html b/index.html new file mode 100644 index 0000000..0d85066 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + ErrorLab + + +
+ + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..23061d6 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,35 @@ +# nginx configuration for a React SPA (React Router support) +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Gzip + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css application/javascript application/json + image/svg+xml image/x-icon font/woff2; + + # Long-lived cache for hashed assets (Vite outputs hash in filename) + location ~* \.(?:js|css|woff2?|ttf|ico|png|jpg|jpeg|gif|svg|webp)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # index.html โ€” no cache; browser always re-validates + location = /index.html { + add_header Cache-Control "no-store"; + } + + # SPA fallback โ€” any unknown path serves index.html so React Router works + location / { + try_files $uri $uri/ /index.html; + } + + # Hide nginx version in error responses + server_tokens off; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0e5464b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1719 @@ +{ + "name": "errorlab", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "errorlab", + "version": "1.0.0", + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.27.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.3", + "vite": "^5.4.11" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", + "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001787", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", + "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.335", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.335.tgz", + "integrity": "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..16ce030 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "errorlab", + "version": "1.0.0", + "description": "Custom public error and landing pages for the home lab", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.27.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.3", + "vite": "^5.4.11" + } +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..4fdc082 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,19 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom' +import NotFoundPage from './pages/404/NotFoundPage.jsx' +import BadGatewayPage from './pages/502/BadGatewayPage.jsx' +import MaintenancePage from './pages/maintenance/MaintenancePage.jsx' +import HomePage from './pages/home/HomePage.jsx' + +export default function App() { + return ( + + + } /> + } /> + } /> + {/* Catch-all โ€” must be last */} + } /> + + + ) +} diff --git a/src/assets/README.md b/src/assets/README.md new file mode 100644 index 0000000..97c6655 --- /dev/null +++ b/src/assets/README.md @@ -0,0 +1,8 @@ +# assets/ + +Place static files here: icons, images, SVGs, fonts, etc. + +Examples: +- favicon.svg +- logo.svg +- og-image.png diff --git a/src/components/DinoGame/DinoGame.css b/src/components/DinoGame/DinoGame.css new file mode 100644 index 0000000..94a2f77 --- /dev/null +++ b/src/components/DinoGame/DinoGame.css @@ -0,0 +1,20 @@ +/* DinoGame canvas wrapper */ + +.dino-game-wrapper { + width: 100%; + max-width: 820px; + cursor: pointer; + user-select: none; + -webkit-tap-highlight-color: transparent; +} + +.dino-game-canvas { + width: 100%; + height: auto; + display: block; + border: 1px solid rgba(0, 200, 255, 0.18); + border-radius: 8px; + box-shadow: + 0 0 0 1px rgba(0,0,0,0.5), + 0 0 32px rgba(0, 200, 255, 0.08); +} diff --git a/src/components/DinoGame/DinoGame.jsx b/src/components/DinoGame/DinoGame.jsx new file mode 100644 index 0000000..2e5d8d0 --- /dev/null +++ b/src/components/DinoGame/DinoGame.jsx @@ -0,0 +1,416 @@ +import { useRef, useEffect, useCallback, useState } from 'react' +import './DinoGame.css' +import { + CANVAS_W, CANVAS_H, GROUND_Y, + GRAVITY, JUMP_VEL, INITIAL_SPEED, SPEED_GROWTH, MAX_SPEED, + PLAYER_X, PLAYER_W, PLAYER_H, + MIN_GAP, MAX_GAP, + OBSTACLE_TEMPLATES, +} from './gameConstants.js' + +// โ”€โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +function randObstacle() { + const t = OBSTACLE_TEMPLATES[Math.floor(Math.random() * OBSTACLE_TEMPLATES.length)] + return { ...t, x: CANVAS_W + 60, y: GROUND_Y - t.h } +} + +/** AABB collision with per-axis inward padding for forgiveness */ +function rectsOverlap(ax, ay, aw, ah, bx, by, bw, bh, pad = 7) { + return ( + ax + pad < bx + bw - pad && + ax + aw - pad > bx + pad && + ay + pad < by + bh - pad && + ay + ah - pad > by + pad + ) +} + +// โ”€โ”€โ”€ Canvas drawing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +function roundRect(ctx, x, y, w, h, r = 5) { + ctx.beginPath() + ctx.moveTo(x + r, y) + ctx.lineTo(x + w - r, y) + ctx.quadraticCurveTo(x + w, y, x + w, y + r) + ctx.lineTo(x + w, y + h - r) + ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h) + ctx.lineTo(x + r, y + h) + ctx.quadraticCurveTo(x, y + h, x, y + h - r) + ctx.lineTo(x, y + r) + ctx.quadraticCurveTo(x, y, x + r, y) + ctx.closePath() +} + +function drawBackground(ctx, bgOffset) { + // Fill + ctx.fillStyle = '#080c14' + ctx.fillRect(0, 0, CANVAS_W, CANVAS_H) + + // Scrolling dot grid + ctx.fillStyle = 'rgba(0, 180, 255, 0.055)' + const spacing = 28 + const startX = (-bgOffset % spacing) - spacing + for (let x = startX; x < CANVAS_W + spacing; x += spacing) { + for (let y = 10; y < GROUND_Y - 4; y += spacing) { + ctx.fillRect(x, y, 1.5, 1.5) + } + } +} + +function drawGround(ctx, bgOffset) { + // Glowing ground line + ctx.save() + ctx.shadowBlur = 8 + ctx.shadowColor = '#00ccff' + ctx.strokeStyle = '#00ccff' + ctx.lineWidth = 1.5 + ctx.beginPath() + ctx.moveTo(0, GROUND_Y) + ctx.lineTo(CANVAS_W, GROUND_Y) + ctx.stroke() + ctx.restore() + + // Scrolling dashes below ground line + ctx.save() + ctx.strokeStyle = 'rgba(0, 140, 200, 0.35)' + ctx.lineWidth = 1 + ctx.setLineDash([18, 14]) + ctx.lineDashOffset = -bgOffset + ctx.beginPath() + ctx.moveTo(0, GROUND_Y + 7) + ctx.lineTo(CANVAS_W, GROUND_Y + 7) + ctx.stroke() + ctx.setLineDash([]) + ctx.restore() +} + +/** Pod player โ€” kubernetes steering-wheel symbol inside a neon green box */ +function drawPlayer(ctx, playerY, frameCount, isJumping) { + const x = PLAYER_X + const y = playerY + const w = PLAYER_W + const h = PLAYER_H + const cx = x + w / 2 + const cy = y + h / 2 - 1 + + ctx.save() + + // Outer glow + ctx.shadowBlur = 14 + ctx.shadowColor = '#00ff88' + + // Body fill + roundRect(ctx, x, y, w, h, 5) + ctx.fillStyle = '#081410' + ctx.fill() + + // Body border + ctx.strokeStyle = '#00ff88' + ctx.lineWidth = 2 + ctx.stroke() + + ctx.shadowBlur = 0 + + // Kubernetes helm wheel (6 spokes + outer ring) + ctx.strokeStyle = '#00ff88' + ctx.lineWidth = 1 + for (let i = 0; i < 6; i++) { + const a = (Math.PI / 3) * i - Math.PI / 6 + ctx.beginPath() + ctx.moveTo(cx + Math.cos(a) * 3, cy + Math.sin(a) * 3) + ctx.lineTo(cx + Math.cos(a) * 9, cy + Math.sin(a) * 9) + ctx.stroke() + } + ctx.beginPath() + ctx.arc(cx, cy, 10, 0, Math.PI * 2) + ctx.stroke() + + // Centre dot + ctx.beginPath() + ctx.arc(cx, cy, 2.5, 0, Math.PI * 2) + ctx.fillStyle = '#00ff88' + ctx.fill() + + // Running legs (2-frame animation, hidden while airborne) + if (!isJumping) { + const phase = Math.floor(frameCount / 6) % 2 + ctx.fillStyle = '#00ff88' + // Left leg + const lh = phase === 0 ? 8 : 4 + ctx.fillRect(x + 7, y + h, 6, lh) + ctx.fillRect(x + 3, y + h + lh, 10, 3) + // Right leg (opposite phase) + const rh = phase === 0 ? 4 : 8 + ctx.fillRect(x + w - 13, y + h, 6, rh) + ctx.fillRect(x + w - 17, y + h + rh, 10, 3) + } + + ctx.restore() +} + +function drawObstacle(ctx, obs) { + const { x, y, w, h, lines, color } = obs + const cx = x + w / 2 + const cy = y + h / 2 + + ctx.save() + + // Diagonal warning stripes clipped to obstacle rect + roundRect(ctx, x, y, w, h, 4) + ctx.fillStyle = 'rgba(0,0,0,0.82)' + ctx.fill() + + ctx.save() + roundRect(ctx, x, y, w, h, 4) + ctx.clip() + // Translucent diagonal stripes using 8-digit hex alpha + ctx.strokeStyle = `${color}22` + ctx.lineWidth = 5 + for (let s = -h; s < w + h; s += 10) { + ctx.beginPath() + ctx.moveTo(x + s, y) + ctx.lineTo(x + s - h, y + h) + ctx.stroke() + } + ctx.restore() + + // Glowing border + ctx.shadowBlur = 14 + ctx.shadowColor = color + roundRect(ctx, x, y, w, h, 4) + ctx.strokeStyle = color + ctx.lineWidth = 2 + ctx.stroke() + ctx.shadowBlur = 0 + + // Label text + ctx.fillStyle = color + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + if (lines.length === 1) { + ctx.font = 'bold 10px monospace' + ctx.fillText(lines[0], cx, cy) + } else { + ctx.font = 'bold 9px monospace' + ctx.fillText(lines[0], cx, cy - 6) + ctx.fillText(lines[1], cx, cy + 6) + } + + ctx.restore() +} + +function drawHUD(ctx, score, highScore) { + // Score + ctx.save() + ctx.fillStyle = '#00ccff' + ctx.font = 'bold 13px monospace' + ctx.textAlign = 'right' + ctx.fillText(`SCORE ${String(score).padStart(5, '0')}`, CANVAS_W - 14, 20) + + // High score + ctx.fillStyle = 'rgba(0,200,255,0.42)' + ctx.font = '11px monospace' + ctx.fillText(`HI ${String(highScore).padStart(5, '0')}`, CANVAS_W - 14, 36) + ctx.restore() +} + +function drawIdleOverlay(ctx) { + ctx.save() + ctx.fillStyle = 'rgba(8, 12, 20, 0.72)' + ctx.fillRect(0, 0, CANVAS_W, CANVAS_H) + ctx.fillStyle = '#00ffcc' + ctx.font = 'bold 15px monospace' + ctx.textAlign = 'center' + ctx.fillText('[ SPACE / โ†‘ / TAP TO LAUNCH ]', CANVAS_W / 2, CANVAS_H / 2 - 8) + ctx.fillStyle = 'rgba(0,220,255,0.5)' + ctx.font = '11px monospace' + ctx.fillText('dodge the cluster faults', CANVAS_W / 2, CANVAS_H / 2 + 12) + ctx.restore() +} + +function drawGameOverOverlay(ctx, score, highScore) { + ctx.save() + ctx.fillStyle = 'rgba(8, 12, 20, 0.72)' + ctx.fillRect(0, 0, CANVAS_W, CANVAS_H) + + ctx.fillStyle = '#ff4455' + ctx.font = 'bold 17px monospace' + ctx.textAlign = 'center' + ctx.fillText('โ”€โ”€ POD TERMINATED โ”€โ”€', CANVAS_W / 2, CANVAS_H / 2 - 26) + + ctx.fillStyle = '#e0e8f0' + ctx.font = '13px monospace' + ctx.fillText(`score: ${score}`, CANVAS_W / 2, CANVAS_H / 2 - 2) + + if (score > 0 && score >= highScore) { + ctx.fillStyle = '#ffee22' + ctx.font = 'bold 11px monospace' + ctx.fillText('โœฆ new high score โœฆ', CANVAS_W / 2, CANVAS_H / 2 + 16) + } + + ctx.fillStyle = '#00ffcc' + ctx.font = '12px monospace' + ctx.fillText('[ SPACE / โ†‘ / TAP to respawn ]', CANVAS_W / 2, CANVAS_H / 2 + 38) + ctx.restore() +} + +// โ”€โ”€โ”€ Component โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * DinoGame โ€” canvas-based infinite runner with a Kubernetes pod player + * and DevOps-themed obstacles. + * + * Controls: Space / ArrowUp / click / tap to jump (or start / restart). + */ +export default function DinoGame() { + const canvasRef = useRef(null) + + // All mutable game state lives in a ref so the rAF loop never re-renders + const s = useRef({ + status: 'idle', // 'idle' | 'playing' | 'gameover' + playerY: GROUND_Y - PLAYER_H, + velocityY: 0, + isJumping: false, + obstacles: [], + bgOffset: 0, + frameCount: 0, // frames while playing (used for animation & score) + distTravelled: 0, // pixels travelled (used for obstacle spawning) + nextSpawnAt: 400, // distTravelled value at which to spawn next obstacle + speed: INITIAL_SPEED, + score: 0, + highScore: parseInt(localStorage.getItem('lab404_hi') ?? '0', 10), + }) + + // Minimal React state โ€” only for values shown in JSX buttons outside canvas + const [gameStatus, setGameStatus] = useState('idle') + + // โ”€โ”€ Input handler โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + const handleAction = useCallback(() => { + const g = s.current + if (g.status === 'idle') { + g.status = 'playing' + setGameStatus('playing') + } else if (g.status === 'playing' && !g.isJumping) { + g.velocityY = JUMP_VEL + g.isJumping = true + } else if (g.status === 'gameover') { + // Full reset + g.status = 'playing' + g.playerY = GROUND_Y - PLAYER_H + g.velocityY = 0 + g.isJumping = false + g.obstacles = [] + g.bgOffset = 0 + g.frameCount = 0 + g.distTravelled = 0 + g.nextSpawnAt = 400 + g.speed = INITIAL_SPEED + g.score = 0 + setGameStatus('playing') + } + }, []) + + // โ”€โ”€ Keyboard listeners โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + useEffect(() => { + const onKey = (e) => { + if (e.code === 'Space' || e.code === 'ArrowUp') { + e.preventDefault() + handleAction() + } + } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [handleAction]) + + // โ”€โ”€ Game loop โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + useEffect(() => { + const canvas = canvasRef.current + const ctx = canvas.getContext('2d') + let rafId + + const loop = () => { + const g = s.current + + // โ”€โ”€ Update โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + if (g.status === 'playing') { + g.frameCount++ + + // Speed ramp + g.score = Math.floor(g.frameCount / 7) + g.speed = Math.min(INITIAL_SPEED + g.score * SPEED_GROWTH, MAX_SPEED) + + // Background scroll + g.bgOffset = (g.bgOffset + g.speed * 0.35) % CANVAS_W + g.distTravelled += g.speed + + // Player physics + g.velocityY += GRAVITY + g.playerY += g.velocityY + if (g.playerY >= GROUND_Y - PLAYER_H) { + g.playerY = GROUND_Y - PLAYER_H + g.velocityY = 0 + g.isJumping = false + } + + // Spawn obstacle when distance threshold is reached + if (g.distTravelled >= g.nextSpawnAt) { + g.obstacles.push(randObstacle()) + const gap = MIN_GAP + Math.random() * (MAX_GAP - MIN_GAP) + g.nextSpawnAt = g.distTravelled + gap + } + + // Move & cull obstacles + for (const o of g.obstacles) o.x -= g.speed + g.obstacles = g.obstacles.filter(o => o.x + o.w > -10) + + // Collision detection + const hit = g.obstacles.some(o => + rectsOverlap(PLAYER_X, g.playerY, PLAYER_W, PLAYER_H, o.x, o.y, o.w, o.h) + ) + if (hit) { + if (g.score > g.highScore) { + g.highScore = g.score + localStorage.setItem('lab404_hi', String(g.score)) + } + g.status = 'gameover' + setGameStatus('gameover') + } + } else { + // Even while idle/gameover keep background subtly drifting + g.bgOffset = (g.bgOffset + 0.4) % CANVAS_W + } + + // โ”€โ”€ Draw โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + drawBackground(ctx, g.bgOffset) + drawGround(ctx, g.bgOffset) + g.obstacles.forEach(o => drawObstacle(ctx, o)) + drawPlayer(ctx, g.playerY, g.frameCount, g.isJumping) + drawHUD(ctx, g.score, g.highScore) + + if (g.status === 'idle') drawIdleOverlay(ctx) + if (g.status === 'gameover') drawGameOverOverlay(ctx, g.score, g.highScore) + + rafId = requestAnimationFrame(loop) + } + + rafId = requestAnimationFrame(loop) + return () => cancelAnimationFrame(rafId) + }, []) // runs once โ€” game loop owns its own state via ref + + return ( +
{ e.preventDefault(); handleAction() }} + > + +
+ ) +} diff --git a/src/components/DinoGame/gameConstants.js b/src/components/DinoGame/gameConstants.js new file mode 100644 index 0000000..089f276 --- /dev/null +++ b/src/components/DinoGame/gameConstants.js @@ -0,0 +1,39 @@ +// โ”€โ”€โ”€ Canvas & World โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +export const CANVAS_W = 800 +export const CANVAS_H = 220 +export const GROUND_Y = 168 // y of the ground surface + +// โ”€โ”€โ”€ Physics โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +export const GRAVITY = 0.58 +export const JUMP_VEL = -13.5 +export const INITIAL_SPEED = 5.2 +export const SPEED_GROWTH = 0.00055 // added per score point (score = frames / 7) +export const MAX_SPEED = 16 + +// โ”€โ”€โ”€ Player โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +export const PLAYER_X = 82 +export const PLAYER_W = 36 +export const PLAYER_H = 36 + +// โ”€โ”€โ”€ Obstacle spawning โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +export const MIN_GAP = 340 // minimum pixel gap between spawns (at game speed 1) +export const MAX_GAP = 680 + +// โ”€โ”€โ”€ Obstacle visual templates โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// Each obstacle has two display lines, a neon colour, and hit-box dimensions. +export const OBSTACLE_TEMPLATES = [ + { lines: ['Crash', 'Loop'], color: '#ff4455', w: 46, h: 52 }, + { lines: ['OOM', 'Killed'], color: '#ff2288', w: 38, h: 60 }, + { lines: ['Image', 'PullErr'], color: '#ff8820', w: 44, h: 46 }, + { lines: ['404'], color: '#00ffff', w: 58, h: 36 }, + { lines: ['Expired', 'Cert'], color: '#ffee22', w: 48, h: 48 }, + { lines: ['Evicted'], color: '#aaaaaa', w: 40, h: 34 }, + { lines: ['Pod', 'Failed'], color: '#ff3366', w: 40, h: 44 }, + { lines: ['Bad', 'Ingress'], color: '#ff6600', w: 50, h: 40 }, + { lines: ['DNS', 'Fail'], color: '#cc44ff', w: 42, h: 50 }, + { lines: ['Pending', '...'], color: '#4499ff', w: 52, h: 32 }, + { lines: ['Timeout'], color: '#ff9944', w: 48, h: 42 }, + { lines: ['502'], color: '#ff4444', w: 52, h: 36 }, + { lines: ['No', 'Route'], color: '#55ff88', w: 46, h: 44 }, + { lines: ['Tunnel', 'Down'], color: '#ff55cc', w: 50, h: 46 }, +] diff --git a/src/components/PageLayout/PageLayout.css b/src/components/PageLayout/PageLayout.css new file mode 100644 index 0000000..4f95c0a --- /dev/null +++ b/src/components/PageLayout/PageLayout.css @@ -0,0 +1,34 @@ +/* PageLayout โ€” full-viewport dark shell used by all pages */ + +.page-layout { + min-height: 100vh; + background: var(--bg); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem 1rem; + position: relative; + overflow: hidden; +} + +/* Subtle animated star field in the background */ +.page-layout::before { + content: ''; + position: fixed; + inset: 0; + background-image: + radial-gradient(1px 1px at 15% 20%, rgba(0,200,255,0.18) 0%, transparent 100%), + radial-gradient(1px 1px at 42% 8%, rgba(0,255,204,0.12) 0%, transparent 100%), + radial-gradient(1px 1px at 70% 35%, rgba(0,200,255,0.15) 0%, transparent 100%), + radial-gradient(1px 1px at 88% 60%, rgba(0,255,204,0.10) 0%, transparent 100%), + radial-gradient(1px 1px at 25% 75%, rgba(0,200,255,0.12) 0%, transparent 100%), + radial-gradient(1px 1px at 55% 90%, rgba(0,255,204,0.08) 0%, transparent 100%); + pointer-events: none; + z-index: 0; +} + +.page-layout > * { + position: relative; + z-index: 1; +} diff --git a/src/components/PageLayout/PageLayout.jsx b/src/components/PageLayout/PageLayout.jsx new file mode 100644 index 0000000..45d1966 --- /dev/null +++ b/src/components/PageLayout/PageLayout.jsx @@ -0,0 +1,17 @@ +import './PageLayout.css' + +/** + * PageLayout โ€” shared full-viewport wrapper for all public pages. + * Applies the dark background and centres content vertically. + * + * Props: + * children โ€” page content + * className โ€” optional extra class on the root element + */ +export default function PageLayout({ children, className = '' }) { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/TerminalLog/TerminalLog.css b/src/components/TerminalLog/TerminalLog.css new file mode 100644 index 0000000..e17678f --- /dev/null +++ b/src/components/TerminalLog/TerminalLog.css @@ -0,0 +1,82 @@ +/* TerminalLog โ€” fake cluster-diagnostics terminal window */ + +.terminal-log { + width: 100%; + max-width: 620px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + overflow: hidden; + box-shadow: 0 0 24px rgba(0, 180, 255, 0.07); +} + +/* Title bar */ +.terminal-header { + display: flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + background: var(--surface-alt); + border-bottom: 1px solid var(--border); + user-select: none; +} + +.terminal-dot { + width: 10px; + height: 10px; + border-radius: 50%; +} +.terminal-dot.red { background: #ff5f57; } +.terminal-dot.yellow { background: #febc2e; } +.terminal-dot.green { background: #28c840; } + +.terminal-title { + margin-left: 6px; + font-size: 0.72rem; + color: var(--text-muted); + letter-spacing: 0.04em; +} + +/* Log body */ +.terminal-body { + padding: 12px 16px 10px; + min-height: 110px; + display: flex; + flex-direction: column; + gap: 3px; + overflow: hidden; +} + +.terminal-line { + font-size: 0.78rem; + line-height: 1.5; + white-space: pre-wrap; + word-break: break-all; +} + +/* Type colour coding */ +.terminal-line--cmd { color: var(--accent); } +.terminal-line--info { color: var(--cyan-dim); } +.terminal-line--warn { color: var(--warning); } +.terminal-line--err { color: var(--danger); } + +/* Slide-in animation for the newest line */ +@keyframes slideInLine { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +} +.terminal-line--new { + animation: slideInLine 0.3s ease-out both; +} + +/* Blinking cursor */ +.terminal-cursor { + display: inline-block; + color: var(--accent); + font-size: 0.85rem; + animation: blink 1.1s step-end infinite; +} +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} diff --git a/src/components/TerminalLog/TerminalLog.jsx b/src/components/TerminalLog/TerminalLog.jsx new file mode 100644 index 0000000..b285719 --- /dev/null +++ b/src/components/TerminalLog/TerminalLog.jsx @@ -0,0 +1,77 @@ +import { useState, useEffect, useRef } from 'react' +import './TerminalLog.css' + +/** Rotating pool of cluster-flavoured diagnostic messages */ +const MESSAGE_POOL = [ + { type: 'cmd', text: '$ kubectl get page /this-path --namespace default' }, + { type: 'err', text: 'Error from server (NotFound): pages "this-path" not found' }, + { type: 'cmd', text: '$ kubectl describe ingress/main-ingress' }, + { type: 'warn', text: '[WARN] ingress-nginx: no matching rule for path "/this-path"' }, + { type: 'cmd', text: '$ nslookup this-page.cluster.local' }, + { type: 'err', text: 'NXDOMAIN: Name does not exist.' }, + { type: 'cmd', text: '$ kubectl get pod -l app=this-page' }, + { type: 'err', text: 'No resources found in default namespace.' }, + { type: 'warn', text: '[WARN] cloudflared: tunnel endpoint unreachable' }, + { type: 'info', text: '[INFO] attempting to reschedule pod...' }, + { type: 'err', text: '[ERROR] ImagePullBackOff: image "this-page:latest" not found' }, + { type: 'cmd', text: '$ kubectl rollout status deploy/this-page' }, + { type: 'err', text: 'error: deployment "this-page" not found' }, + { type: 'warn', text: '[WARN] certificate for this-page.lab expired 42 days ago' }, + { type: 'info', text: '[INFO] DNS TTL 30s โ€” retrying...' }, + { type: 'err', text: '[ERROR] upstream "this-page:8080": connection refused' }, + { type: 'cmd', text: '$ kubectl logs -l app=this-page --tail=5' }, + { type: 'err', text: 'Error from server (NotFound): pods "this-page" not found' }, + { type: 'warn', text: '[WARN] 3 consecutive CrashLoopBackOff events detected' }, + { type: 'info', text: '[INFO] route table: /this-path โ†’ ??? (unresolved)' }, +] + +const MAX_VISIBLE_LINES = 6 +const TICK_MS = 1700 + +/** + * TerminalLog โ€” auto-scrolling fake terminal showing rotating cluster errors. + * title โ€” override the window title bar text (default: "cluster-diagnostics") + */ +export default function TerminalLog({ title = 'cluster-diagnostics' }) { + const [lines, setLines] = useState([MESSAGE_POOL[0]]) + const indexRef = useRef(1) + const containerRef = useRef(null) + + useEffect(() => { + const id = setInterval(() => { + const next = MESSAGE_POOL[indexRef.current % MESSAGE_POOL.length] + indexRef.current += 1 + setLines(prev => [...prev, next].slice(-MAX_VISIBLE_LINES)) + }, TICK_MS) + return () => clearInterval(id) + }, []) + + // Auto-scroll to bottom whenever lines update + useEffect(() => { + if (containerRef.current) { + containerRef.current.scrollTop = containerRef.current.scrollHeight + } + }, [lines]) + + return ( +
+
+ + + + {title} +
+
+ {lines.map((line, i) => ( +
+ {line.text} +
+ ))} + โ–‹ +
+
+ ) +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..ad25c91 --- /dev/null +++ b/src/index.css @@ -0,0 +1,50 @@ +/* โ”€โ”€ Global reset & dark theme base โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +:root { + --bg: #080c14; + --surface: #0d1520; + --surface-alt: #111827; + --border: rgba(0, 200, 255, 0.14); + --accent: #00ffcc; + --accent-dim: rgba(0, 255, 204, 0.25); + --accent-glow: rgba(0, 255, 204, 0.35); + --cyan: #00ccff; + --cyan-dim: rgba(0, 200, 255, 0.5); + --text: #e0e8f0; + --text-muted: rgba(200, 220, 240, 0.45); + --danger: #ff4455; + --warning: #ffaa00; + --font-mono: 'Consolas', 'Monaco', 'Courier New', monospace; +} + +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + height: 100%; + background: var(--bg); + color: var(--text); + font-family: var(--font-mono); + -webkit-font-smoothing: antialiased; + overflow-x: hidden; +} + +#root { + min-height: 100%; + display: flex; + flex-direction: column; +} + +a { + color: inherit; + text-decoration: none; +} + +/* Custom scrollbars */ +::-webkit-scrollbar { width: 5px; height: 5px; } +::-webkit-scrollbar-track { background: var(--bg); } +::-webkit-scrollbar-thumb { background: rgba(0, 200, 255, 0.25); border-radius: 3px; } +::-webkit-scrollbar-thumb:hover { background: rgba(0, 200, 255, 0.4); } diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..5e8d112 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +) diff --git a/src/pages/404/NotFoundPage.css b/src/pages/404/NotFoundPage.css new file mode 100644 index 0000000..1c0bc72 --- /dev/null +++ b/src/pages/404/NotFoundPage.css @@ -0,0 +1,152 @@ +/* โ”€โ”€ 404 Not Found Page โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +.nfp-container { + width: 100%; + max-width: 860px; + padding: 2.5rem 1.5rem 3rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + text-align: center; +} + +/* โ”€โ”€ Header โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +.nfp-header { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.6rem; +} + +/* Glitch animation on the big 404 numeral */ +@keyframes glitch { + 0%, 88%, 100% { transform: none; text-shadow: -2px 0 #00ffcc, 2px 0 #ff4455; } + 90% { transform: translate(-2px, 0); color: #ff4455; } + 92% { transform: translate(2px, 0); color: #00ffcc; } + 94% { transform: translate(-1px, 0); } + 96% { transform: none; } +} + +@keyframes fadeInScale { + from { opacity: 0; transform: scale(0.85); } + to { opacity: 1; transform: scale(1); } +} + +.nfp-code { + font-size: clamp(5.5rem, 18vw, 10.5rem); + font-weight: 900; + letter-spacing: 0.06em; + line-height: 1; + color: transparent; + -webkit-text-stroke: 2px var(--accent); + text-shadow: -2px 0 #00ffcc, 2px 0 #ff4455, 0 0 60px rgba(0,255,204,0.25); + animation: + fadeInScale 0.7s ease-out both, + glitch 5s ease-in-out 1.5s infinite; +} + +.nfp-title { + font-size: clamp(1.1rem, 3vw, 1.6rem); + font-weight: 700; + color: var(--text); + animation: fadeInScale 0.7s 0.15s ease-out both; +} + +.nfp-subtitle { + font-size: clamp(0.82rem, 2vw, 1rem); + color: var(--cyan-dim); + animation: fadeInScale 0.7s 0.3s ease-out both; +} + +.nfp-kubectl { + display: inline-block; + font-size: 0.8rem; + color: var(--text-muted); + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.35rem 0.8rem; + animation: fadeInScale 0.7s 0.45s ease-out both; +} + +.nfp-kubectl .err { color: var(--danger); } + +/* โ”€โ”€ Game section โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +.nfp-game-section { + width: 100%; + animation: fadeInScale 0.7s 0.55s ease-out both; +} + +.nfp-game-hint { + margin-top: 0.55rem; + font-size: 0.72rem; + color: var(--text-muted); + letter-spacing: 0.03em; +} + +/* โ”€โ”€ Terminal log โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +.nfp-log-section { + width: 100%; + display: flex; + justify-content: center; + animation: fadeInScale 0.7s 0.65s ease-out both; +} + +/* โ”€โ”€ Footer buttons โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +.nfp-footer { + display: flex; + gap: 0.9rem; + flex-wrap: wrap; + justify-content: center; + animation: fadeInScale 0.7s 0.75s ease-out both; +} + +.nfp-btn { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.55rem 1.4rem; + border-radius: 6px; + font-family: var(--font-mono); + font-size: 0.85rem; + font-weight: 600; + text-decoration: none; + cursor: pointer; + transition: background 0.18s, border-color 0.18s, box-shadow 0.18s; + border: 1px solid transparent; +} + +.nfp-btn--primary { + background: rgba(0, 255, 204, 0.1); + border-color: rgba(0, 255, 204, 0.4); + color: var(--accent); +} +.nfp-btn--primary:hover { + background: rgba(0, 255, 204, 0.18); + border-color: var(--accent); + box-shadow: 0 0 18px var(--accent-glow); +} + +.nfp-btn--secondary { + background: rgba(255,255,255,0.05); + border-color: rgba(255,255,255,0.14); + color: var(--text-muted); +} +.nfp-btn--secondary:hover { + background: rgba(255,255,255,0.09); + border-color: rgba(255,255,255,0.28); + color: var(--text); +} + +/* โ”€โ”€ Responsive โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */ + +@media (max-width: 480px) { + .nfp-container { gap: 1.5rem; padding: 1.5rem 1rem 2rem; } + .nfp-footer { gap: 0.65rem; } + .nfp-btn { font-size: 0.8rem; padding: 0.5rem 1.1rem; } +} diff --git a/src/pages/404/NotFoundPage.jsx b/src/pages/404/NotFoundPage.jsx new file mode 100644 index 0000000..29d8c29 --- /dev/null +++ b/src/pages/404/NotFoundPage.jsx @@ -0,0 +1,58 @@ +import PageLayout from '../../components/PageLayout/PageLayout.jsx' +import DinoGame from '../../components/DinoGame/DinoGame.jsx' +import TerminalLog from '../../components/TerminalLog/TerminalLog.jsx' +import './NotFoundPage.css' + +/** + * 404 Not Found โ€” the main interactive error page. + * Features the Kubernetes pod runner game and a live cluster-diagnostics terminal. + */ +export default function NotFoundPage() { + return ( + +
+ + {/* โ”€โ”€ Header โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} +
+

404

+

Page Not Found

+

+ This route was lost somewhere in the cluster. +

+ + $ kubectl get page: NotFound + +
+ + {/* โ”€โ”€ Mini-game โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} +
+ +

+ space / โ†‘ / tap โ€” jump  ยท  dodge the cluster faults +

+
+ + {/* โ”€โ”€ Live "diagnostics" terminal โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} +
+ +
+ + {/* โ”€โ”€ Navigation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */} + + +
+
+ ) +} diff --git a/src/pages/502/BadGatewayPage.css b/src/pages/502/BadGatewayPage.css new file mode 100644 index 0000000..d47b646 --- /dev/null +++ b/src/pages/502/BadGatewayPage.css @@ -0,0 +1,56 @@ +/* 502 Bad Gateway โ€” shares the same visual language as the 404 page */ + +.bgp-container { + width: 100%; + max-width: 700px; + padding: 2.5rem 1.5rem 3rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + text-align: center; +} + +.bgp-header { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; } + +.bgp-code { + font-size: clamp(5rem, 18vw, 9rem); + font-weight: 900; + letter-spacing: 0.06em; + line-height: 1; + color: transparent; + -webkit-text-stroke: 2px var(--warning); + text-shadow: -2px 0 var(--warning), 2px 0 var(--danger), 0 0 60px rgba(255,170,0,0.25); +} + +.bgp-title { font-size: clamp(1.1rem, 3vw, 1.5rem); font-weight: 700; color: var(--text); } +.bgp-subtitle { font-size: 0.9rem; color: var(--cyan-dim); } +.bgp-kubectl { + font-size: 0.8rem; color: var(--text-muted); + background: var(--surface); border: 1px solid var(--border); + border-radius: 4px; padding: 0.35rem 0.8rem; +} +.bgp-kubectl .err { color: var(--danger); } + +.bgp-log-section { width: 100%; display: flex; justify-content: center; } + +.bgp-footer { display: flex; gap: 0.9rem; flex-wrap: wrap; justify-content: center; } + +.bgp-btn { + display: inline-flex; align-items: center; gap: 0.45rem; + padding: 0.55rem 1.4rem; border-radius: 6px; + font-family: var(--font-mono); font-size: 0.85rem; font-weight: 600; + text-decoration: none; cursor: pointer; + transition: background 0.18s, border-color 0.18s, box-shadow 0.18s; + border: 1px solid transparent; +} +.bgp-btn--primary { + background: rgba(255,170,0,0.1); border-color: rgba(255,170,0,0.4); color: var(--warning); +} +.bgp-btn--primary:hover { + background: rgba(255,170,0,0.18); border-color: var(--warning); box-shadow: 0 0 18px rgba(255,170,0,0.3); +} +.bgp-btn--secondary { + background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.14); color: var(--text-muted); +} +.bgp-btn--secondary:hover { background: rgba(255,255,255,0.09); border-color: rgba(255,255,255,0.28); color: var(--text); } diff --git a/src/pages/502/BadGatewayPage.jsx b/src/pages/502/BadGatewayPage.jsx new file mode 100644 index 0000000..7cede30 --- /dev/null +++ b/src/pages/502/BadGatewayPage.jsx @@ -0,0 +1,37 @@ +import PageLayout from '../../components/PageLayout/PageLayout.jsx' +import TerminalLog from '../../components/TerminalLog/TerminalLog.jsx' +import './BadGatewayPage.css' + +/** + * 502 Bad Gateway โ€” placeholder. + * TODO: add a 502-themed mini-game or animation. + */ +export default function BadGatewayPage() { + return ( + +
+
+

502

+

Bad Gateway

+

+ The upstream service isn't responding. The proxy is confused. +

+ + $ curl -I https://app.lab โ€” 502 Bad Gateway + +
+ +
+ +
+ + +
+
+ ) +} diff --git a/src/pages/home/HomePage.css b/src/pages/home/HomePage.css new file mode 100644 index 0000000..942598b --- /dev/null +++ b/src/pages/home/HomePage.css @@ -0,0 +1,78 @@ +/* Home / landing page */ + +.home-container { + width: 100%; + max-width: 680px; + padding: 3rem 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 2.2rem; + text-align: center; +} + +.home-header { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; } + +.home-logo { + font-size: 4rem; + text-shadow: 0 0 30px var(--accent-glow); +} + +.home-title { + font-size: clamp(2rem, 6vw, 3rem); + font-weight: 900; + color: transparent; + -webkit-text-stroke: 2px var(--accent); + text-shadow: 0 0 40px var(--accent-glow); +} + +.home-subtitle { font-size: 1rem; color: var(--cyan-dim); } + +.home-status { + font-size: 0.78rem; color: var(--text-muted); + background: var(--surface); border: 1px solid var(--border); + border-radius: 4px; padding: 0.3rem 0.8rem; +} + +/* Service grid */ +.home-services { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 0.8rem; + width: 100%; +} + +.home-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 1rem 0.8rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + text-decoration: none; + color: var(--text); + transition: border-color 0.18s, box-shadow 0.18s; +} +.home-card:hover { + border-color: var(--accent); + box-shadow: 0 0 16px var(--accent-dim); +} + +.home-card-name { font-size: 0.9rem; font-weight: 600; } + +.home-card-badge { + font-size: 0.7rem; + padding: 0.2rem 0.5rem; + border-radius: 3px; + letter-spacing: 0.04em; +} +.home-card-badge--running { background: rgba(0,255,136,0.12); color: #00ff88; } +.home-card-badge--pending { background: rgba(68,153,255,0.12); color: #4499ff; } +.home-card-badge--error { background: rgba(255,68,85,0.12); color: var(--danger); } + +.home-footer { margin-top: 0.5rem; } +.home-footer-text { font-size: 0.82rem; color: var(--text-muted); } +.home-link { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; } +.home-link:hover { color: var(--cyan); } diff --git a/src/pages/home/HomePage.jsx b/src/pages/home/HomePage.jsx new file mode 100644 index 0000000..4b8dd62 --- /dev/null +++ b/src/pages/home/HomePage.jsx @@ -0,0 +1,43 @@ +import PageLayout from '../../components/PageLayout/PageLayout.jsx' +import './HomePage.css' + +/** + * Home / landing page โ€” placeholder. + * TODO: add service list, status dashboard, or project links. + */ +export default function HomePage() { + return ( + +
+
+ +

ErrorLab

+

Home Lab / DevOps Dashboard

+ cluster: online  ยท  nodes: healthy +
+ + {/* Placeholder service cards โ€” replace with real data */} +
+ {[ + { name: 'App', status: 'running', path: '#' }, + { name: 'Monitoring', status: 'running', path: '#' }, + { name: 'CI/CD', status: 'running', path: '#' }, + { name: 'Storage', status: 'pending', path: '#' }, + ].map(svc => ( + + {svc.name} + {svc.status} + + ))} +
+ + +
+
+ ) +} diff --git a/src/pages/maintenance/MaintenancePage.css b/src/pages/maintenance/MaintenancePage.css new file mode 100644 index 0000000..41269e3 --- /dev/null +++ b/src/pages/maintenance/MaintenancePage.css @@ -0,0 +1,74 @@ +/* Maintenance page */ + +.maint-container { + width: 100%; + max-width: 600px; + padding: 3rem 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 1.8rem; + text-align: center; +} + +.maint-header { display: flex; flex-direction: column; align-items: center; gap: 0.7rem; } + +.maint-icon { font-size: 3.5rem; } + +.maint-title { + font-size: clamp(1.5rem, 4vw, 2.2rem); + font-weight: 800; + color: var(--warning); + text-shadow: 0 0 30px rgba(255,170,0,0.3); +} + +.maint-subtitle { font-size: 1rem; color: var(--text); min-height: 1.6em; } + +.maint-status { + font-size: 0.78rem; color: var(--text-muted); + background: var(--surface); border: 1px solid var(--border); + border-radius: 4px; padding: 0.35rem 0.8rem; +} +.maint-status .info { color: var(--cyan); } + +/* Infinite looping progress bar */ +.maint-progress { + width: 100%; + max-width: 400px; + height: 4px; + background: var(--surface); + border-radius: 2px; + overflow: hidden; +} +@keyframes indeterminate { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(400%); } +} +.maint-progress-bar { + height: 100%; + width: 25%; + background: linear-gradient(90deg, transparent, var(--warning), transparent); + animation: indeterminate 1.8s linear infinite; +} + +.maint-note { font-size: 0.82rem; color: var(--text-muted); } + +.maint-footer { display: flex; justify-content: center; } + +.maint-btn { + padding: 0.55rem 1.6rem; + border-radius: 6px; + border: 1px solid rgba(255,170,0,0.35); + background: rgba(255,170,0,0.1); + color: var(--warning); + font-family: var(--font-mono); + font-size: 0.85rem; + font-weight: 600; + cursor: pointer; + transition: background 0.18s, border-color 0.18s, box-shadow 0.18s; +} +.maint-btn:hover { + background: rgba(255,170,0,0.18); + border-color: var(--warning); + box-shadow: 0 0 16px rgba(255,170,0,0.28); +} diff --git a/src/pages/maintenance/MaintenancePage.jsx b/src/pages/maintenance/MaintenancePage.jsx new file mode 100644 index 0000000..123aa6c --- /dev/null +++ b/src/pages/maintenance/MaintenancePage.jsx @@ -0,0 +1,51 @@ +import { useState, useEffect } from 'react' +import PageLayout from '../../components/PageLayout/PageLayout.jsx' +import './MaintenancePage.css' + +/** + * Maintenance โ€” placeholder. + * TODO: add real duration, progress bar, or status feed. + */ +export default function MaintenancePage() { + const [dots, setDots] = useState('.') + + // Animate the "working on it" ellipsis + useEffect(() => { + const id = setInterval(() => { + setDots(d => d.length >= 3 ? '.' : d + '.') + }, 600) + return () => clearInterval(id) + }, []) + + return ( + +
+
+ +

Under Maintenance

+

+ The lab is being upgraded{dots} +

+ + $ kubectl rollout status deploy/lab โ€” Waiting + +
+ + {/* Animated progress bar placeholder */} +
+
+
+ +

+ Services will be back shortly. Check back in a few minutes. +

+ + +
+ + ) +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..ef95f68 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + // Each page can be built independently later by pointing `build.rollupOptions.input` + // to its specific entry. For now, all pages share a single SPA dev server. +})