feat(auth): replace browser Basic Auth popup with custom Shadcn SSO AuthGateModal and .hl-input system

Browse Source
This commit is contained in:
Yin Kou committed 2026-11-07 00:33:10 +08:00
1 parent ba3cf726b4
commit cbc47336d6
13 files changed
+804 -208

No files matched your search

+3 -3
View File
@@ -10,7 +10,6 @@ status.961504.xyz:443 {
}
# 2. 家庭统一导航总入口免端口 443 访问 -> https://nav.961504.xyz
# (通过 Tailscale 加密专线回源至家里 Mac mini 的 Sun-Panel)
nav.961504.xyz:443 {
encode zstd gzip
reverse_proxy 100.108.205.40:3002
@@ -23,10 +22,11 @@ wx.961504.xyz:443 {
}
# 4. AGY 24小时云端 Web 工作台 (443 免端口访问) -> https://agy.961504.xyz
# 使用 forward_auth 对接 HomeLab SSO 2.0 自定义登录卡片,彻底告别浏览器原生丑陋弹窗!
agy.961504.xyz:443 {
encode zstd gzip
basicauth /* {
ixvii0iv $2a$14$VaaNvgIjsCJFzXCpIxjcOerKUIdkN7GhVWAlGh3/ZZjBOADY9lg..
forward_auth 100.108.205.40:3002 {
uri /api/auth/verify
}
reverse_proxy 100.108.205.40:7681
}
+9 -8
View File
@@ -8,7 +8,7 @@
auto_https disable_redirects
}
# 0. 家庭统一导航总入口 Sun-Panel (https://home.961504.xyz:10443)
# 0. 家庭统一导航总入口 HomeLab Console V3 (https://home.961504.xyz:10443)
home.961504.xyz:10443, nav.home.961504.xyz:10443 {
tls /Users/ixvii0iv/homelab/stacks/gateway/certs/fullchain.pem /Users/ixvii0iv/homelab/stacks/gateway/certs/privkey.pem
encode zstd gzip
@@ -22,12 +22,12 @@ nas.home.961504.xyz:10443, z4.home.961504.xyz:10443 {
reverse_proxy 192.168.31.185:5055
}
# 2. 路由器 Mihomo / Clash 面板 (已开启 HTTP Basic Auth 密码锁保护: ixvii0iv / Mac mini密码)
# 2. 路由器 Mihomo / Clash 面板 (通过 HomeLab SSO 2.0 自定义登录卡片保护)
clash.home.961504.xyz:10443 {
tls /Users/ixvii0iv/homelab/stacks/gateway/certs/fullchain.pem /Users/ixvii0iv/homelab/stacks/gateway/certs/privkey.pem
encode zstd gzip
basic_auth {
ixvii0iv $2a$14$VaaNvgIjsCJFzXCpIxjcOerKUIdkN7GhVWAlGh3/ZZjBOADY9lg..
forward_auth 127.0.0.1:3002 {
uri /api/auth/verify
}
reverse_proxy 192.168.31.1:9999
}
@@ -36,8 +36,8 @@ clash.home.961504.xyz:10443 {
router.home.961504.xyz:10443 {
tls /Users/ixvii0iv/homelab/stacks/gateway/certs/fullchain.pem /Users/ixvii0iv/homelab/stacks/gateway/certs/privkey.pem
encode zstd gzip
basic_auth {
ixvii0iv $2a$14$VaaNvgIjsCJFzXCpIxjcOerKUIdkN7GhVWAlGh3/ZZjBOADY9lg..
forward_auth 127.0.0.1:3002 {
uri /api/auth/verify
}
reverse_proxy 192.168.31.1:80
}
@@ -50,11 +50,12 @@ lucky.home.961504.xyz:10443 {
}
# 5. AGY 24小时云端 Web 工作台 (https://agy.home.961504.xyz:10443)
# 使用 forward_auth 对接 HomeLab SSO 2.0 自定义登录卡片,彻底告别浏览器原生丑陋弹窗!
agy.home.961504.xyz:10443 {
tls /Users/ixvii0iv/homelab/stacks/gateway/certs/fullchain.pem /Users/ixvii0iv/homelab/stacks/gateway/certs/privkey.pem
encode zstd gzip
basic_auth {
ixvii0iv $2a$14$VaaNvgIjsCJFzXCpIxjcOerKUIdkN7GhVWAlGh3/ZZjBOADY9lg..
forward_auth 127.0.0.1:3002 {
uri /api/auth/verify
}
reverse_proxy 127.0.0.1:7681
}
-1
View File
@@ -1 +0,0 @@
/*! tailwindcss v4.3.5 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:block))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-md:28rem;--container-xl:36rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--blur-sm:8px;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-Line truncated
+154
View File
@@ -0,0 +1,154 @@
(function(){const O=document.createElement("link").relList;if(O&&O.supports&&O.supports("modulepreload"))return;for(const E of document.querySelectorAll('link[rel="modulepreload"]'))d(E);new MutationObserver(E=>{for(const H of E)if(H.type==="childList")for(const J of H.addedNodes)J.tagName==="LINK"&&J.rel==="modulepreload"&&d(J)}).observe(document,{childList:!0,subtree:!0});function T(E){const H={};return E.integrity&&(H.integrity=E.integrity),E.referrerPolicy&&(H.referrerPolicy=E.referrerPolicy),E.crossOrigin==="use-credentials"?H.credentials="include":E.crossOrigin==="anonymous"?H.credentials="omit":H.credentials="same-origin",H}function d(E){if(E.ep)return;E.ep=!0;const H=T(E);fetch(E.href,H)}})();function g0(x){return x&&x.__esModule&&Object.prototype.hasOwnProperty.call(x,"default")?x.default:x}var Ts={exports:{}},nn={};/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var u0;function pv(){if(u0)return nn;u0=1;var x=Symbol.for("react.transitional.element"),O=Symbol.for("react.fragment");function T(d,E,H){var J=null;if(H!==void 0&&(J=""+H),E.key!==void 0&&(J=""+E.key),"key"in E){H={};for(var K in E)K!=="key"&&(H[K]=E[K])}else H=E;return E=H.ref,{$$typeof:x,type:d,key:J,ref:E!==void 0?E:null,props:H}}return nn.Fragment=O,nn.jsx=T,nn.jsxs=T,nn}var n0;function Nv(){return n0||(n0=1,Ts.exports=pv()),Ts.exports}var f=Nv(),_s={exports:{}},V={};/**
* @license React
* react.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var c0;function Tv(){if(c0)return V;c0=1;var x=Symbol.for("react.transitional.element"),O=Symbol.for("react.portal"),T=Symbol.for("react.fragment"),d=Symbol.for("react.strict_mode"),E=Symbol.for("react.profiler"),H=Symbol.for("react.consumer"),J=Symbol.for("react.context"),K=Symbol.for("react.forward_ref"),Q=Symbol.for("react.suspense"),W=Symbol.for("react.memo"),A=Symbol.for("react.lazy"),S=Symbol.for("react.activity"),D=Symbol.for("react.view_transition"),w=Symbol.iterator;function U(r){return r===null||typeof r!="object"?null:(r=w&&r[w]||r["@@iterator"],typeof r=="function"?r:null)}var vl={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},P=Object.assign,jl={};function Bl(r,_,B){this.props=r,this.context=_,this.refs=jl,this.updater=B||vl}Bl.prototype.isReactComponent={},Bl.prototype.setState=function(r,_){if(typeof r!="object"&&typeof r!="function"&&r!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,r,_,"setState")},Bl.prototype.forceUpdate=function(r){this.updater.enqueueForceUpdate(this,r,"forceUpdate")};function I(){}I.prototype=Bl.prototype;function il(r,_,B){this.props=r,this.context=_,this.refs=jl,this.updater=B||vl}var Il=il.prototype=new I;Il.constructor=il,P(Il,Bl.prototype),Il.isPureReactComponent=!0;var Pl=Array.isArray;function ll(){}var ol={H:null,A:null,T:null,S:null},Qt=Object.prototype.hasOwnProperty;function Tt(r,_,B){var q=B.ref;return{$$typeof:x,type:r,key:_,ref:q!==void 0?q:null,props:B}}function _t(r,_){return Tt(r.type,_,r.props)}function rt(r){return typeof r=="object"&&r!==null&&r.$$typeof===x}function be(r){var _={"=":"=0",":":"=2"};return"$"+r.replace(/[=:]/g,function(B){return _[B]})}var $e=/\/+/g;function ql(r,_){return typeof r=="object"&&r!==null&&r.key!=null?be(""+r.key):_.toString(36)}function j(r){switch(r.status){case"fulfilled":return r.value;case"rejected":throw r.reason;default:switch(typeof r.status=="string"?r.then(ll,ll):(r.status="pending",r.then(function(_){r.status==="pending"&&(r.status="fulfilled",r.value=_)},function(_){r.status==="pending"&&(r.status="rejected",r.reason=_)})),r.status){case"fulfilled":return r.value;case"rejected":throw r.reason}}throw r}function X(r,_,B,q,cl){var fl=typeof r;(fl==="undefined"||fl==="boolean")&&(r=null);var rl=!1;if(r===null)rl=!0;else switch(fl){case"bigint":case"string":case"number":rl=!0;break;case"object":switch(r.$$typeof){case x:case O:rl=!0;break;case A:return rl=r._init,X(rl(r._payload),_,B,q,cl)}}if(rl)return cl=cl(r),rl=q===""?"."+ql(r,0):q,Pl(cl)?(B="",rl!=null&&(B=rl.replace($e,"$&/")+"/"),X(cl,_,B,"",function(ee){return ee})):cl!=null&&(rt(cl)&&(cl=_t(cl,B+(cl.key==null||r&&r.key===cl.key?"":(""+cl.key).replace($e,"$&/")+"/")+rl)),_.push(cl)),1;rl=0;var R=q===""?".":q+":";if(Pl(r))for(var Z=0;Z<r.length;Z++)q=r[Z],fl=R+ql(q,Z),rl+=X(q,_,B,fl,cl);else if(Z=U(r),typeof Z=="function")for(r=Z.call(r),Z=0;!(q=r.next()).done;)q=q.value,fl=R+ql(q,Z++),rl+=X(q,_,B,fl,cl);else if(fl==="object"){if(typeof r.then=="function")return X(j(r),_,B,q,cl);throw _=String(r),Error("Objects are not valid as a React child (found: "+(_==="[object Object]"?"object with keys {"+Object.keys(r).join(", ")+"}":_)+"). If you meant to render a collection of children, use an array instead.")}return rl}function L(r,_,B){if(r==null)return r;var q=[],cl=0;return X(r,q,"","",function(fl){return _.call(B,fl,cl++)}),q}function pl(r){if(r._status===-1){var _=r._result,B=_();B.then(function(q){(r._status===0||r._status===-1)&&(r._status=1,r._result=q,B.status===void 0&&(B.status="fulfilled",B.value=q))},function(q){(r._status===0||r._status===-1)&&(r._status=2,r._result=q,B.status===void 0&&(B.status="rejected",B.reason=q))}),r._status===-1&&(r._status=0,r._result=B)}if(r._status===1)return r._result.default;throw r._result}var ml=typeof reportError=="function"?reportError:function(r){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var _=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof r=="object"&&r!==null&&typeof r.message=="string"?String(r.message):String(r),error:r});if(!window.dispatchEvent(_))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",r);return}console.error(r)};function Rt(r){var _=ol.T,B={};B.types=_!==null?_.types:null,ol.T=B;try{var q=r(),cl=ol.S;cl!==null&&cl(B,q),typeof q=="object"&&q!==null&&typeof q.then=="function"&&q.then(ll,ml)}catch(fl){ml(fl)}finally{_!==null&&B.types!==null&&(_.types=B.types),ol.T=_}}function te(r){var _=ol.T;if(_!==null){var B=_.types;B===null?_.types=[r]:B.indexOf(r)===-1&&B.push(r)}else Rt(te.bind(null,r))}var We={map:L,forEach:function(r,_,B){L(r,function(){_.apply(this,arguments)},B)},count:function(r){var _=0;return L(r,function(){_++}),_},toArray:function(r){return L(r,function(_){return _})||[]},only:function(r){if(!rLine truncated
* @license React
* scheduler.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var f0;function _v(){return f0||(f0=1,(function(x){function O(j,X){var L=j.length;j.push(X);l:for(;0<L;){var pl=L-1>>>1,ml=j[pl];if(0<E(ml,X))j[pl]=X,j[L]=ml,L=pl;else break l}}function T(j){return j.length===0?null:j[0]}function d(j){if(j.length===0)return null;var X=j[0],L=j.pop();if(L!==X){j[0]=L;l:for(var pl=0,ml=j.length,Rt=ml>>>1;pl<Rt;){var te=2*(pl+1)-1,We=j[te],r=te+1,_=j[r];if(0>E(We,L))r<ml&&0>E(_,We)?(j[pl]=_,j[r]=L,pl=r):(j[pl]=We,j[te]=L,pl=te);else if(r<ml&&0>E(_,L))j[pl]=_,j[r]=L,pl=r;else break l}}return X}function E(j,X){var L=j.sortIndex-X.sortIndex;return L!==0?L:j.id-X.id}if(x.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var H=performance;x.unstable_now=function(){return H.now()}}else{var J=Date,K=J.now();x.unstable_now=function(){return J.now()-K}}var Q=[],W=[],A=1,S=null,D=3,w=!1,U=!1,vl=!1,P=!1,jl=typeof setTimeout=="function"?setTimeout:null,Bl=typeof clearTimeout=="function"?clearTimeout:null,I=typeof setImmediate<"u"?setImmediate:null;function il(j){for(var X=T(W);X!==null;){if(X.callback===null)d(W);else if(X.startTime<=j)d(W),X.sortIndex=X.expirationTime,O(Q,X);else break;X=T(W)}}function Il(j){if(vl=!1,il(j),!U)if(T(Q)!==null)U=!0,Pl||(Pl=!0,rt());else{var X=T(W);X!==null&&ql(Il,X.startTime-j)}}var Pl=!1,ll=-1,ol=5,Qt=-1;function Tt(){return P?!0:!(x.unstable_now()-Qt<ol)}function _t(){if(P=!1,Pl){var j=x.unstable_now();Qt=j;var X=!0;try{l:{U=!1,vl&&(vl=!1,Bl(ll),ll=-1),w=!0;var L=D;try{t:{for(il(j),S=T(Q);S!==null&&!(S.expirationTime>j&&Tt());){var pl=S.callback;if(typeof pl=="function"){S.callback=null,D=S.priorityLevel;var ml=pl(S.expirationTime<=j);if(j=x.unstable_now(),typeof ml=="function"){S.callback=ml,il(j),X=!0;break t}S===T(Q)&&d(Q),il(j)}else d(Q);S=T(Q)}if(S!==null)X=!0;else{var Rt=T(W);Rt!==null&&ql(Il,Rt.startTime-j),X=!1}}break l}finally{S=null,D=L,w=!1}X=void 0}}finally{X?rt():Pl=!1}}}var rt;if(typeof I=="function")rt=function(){I(_t)};else if(typeof MessageChannel<"u"){var be=new MessageChannel,$e=be.port2;be.port1.onmessage=_t,rt=function(){$e.postMessage(null)}}else rt=function(){jl(_t,0)};function ql(j,X){ll=jl(function(){j(x.unstable_now())},X)}x.unstable_IdlePriority=5,x.unstable_ImmediatePriority=1,x.unstable_LowPriority=4,x.unstable_NormalPriority=3,x.unstable_Profiling=null,x.unstable_UserBlockingPriority=2,x.unstable_cancelCallback=function(j){j.callback=null},x.unstable_forceFrameRate=function(j){0>j||125<j?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ol=0<j?Math.floor(1e3/j):5},x.unstable_getCurrentPriorityLevel=function(){return D},x.unstable_next=function(j){switch(D){case 1:case 2:case 3:var X=3;break;default:X=D}var L=D;D=X;try{return j()}finally{D=L}},x.unstable_requestPaint=function(){P=!0},x.unstable_runWithPriority=function(j,X){switch(j){case 1:case 2:case 3:case 4:case 5:break;default:j=3}var L=D;D=j;try{return X()}finally{D=L}},x.unstable_scheduleCallback=function(j,X,L){var pl=x.unstable_now();switch(typeof L=="object"&&L!==null?(L=L.delay,L=typeof L=="number"&&0<L?pl+L:pl):L=pl,j){case 1:var ml=-1;break;case 2:ml=250;break;case 5:ml=1073741823;break;case 4:ml=1e4;break;default:ml=5e3}return ml=L+ml,j={id:A++,callback:X,priorityLevel:j,startTime:L,expirationTime:ml,sortIndex:-1},L>pl?(j.sortIndex=L,O(W,j),T(Q)===null&&j===T(W)&&(vl?(Bl(ll),ll=-1):vl=!0,ql(Il,L-pl))):(j.sortIndex=ml,O(Q,j),U||w||(U=!0,Pl||(Pl=!0,rt()))),j},x.unstable_shouldYield=Tt,x.unstable_wrapCallback=function(j){var X=D;return function(){var L=D;D=X;try{return j.apply(this,arguments)}finally{D=L}}}})(Os)),Os}var s0;function Ev(){return s0||(s0=1,zs.exports=_v()),zs.exports}var js={exports:{}},Wl={};/**
* @license React
* react-dom.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var o0;function zv(){if(o0)return Wl;o0=1;var x=As();function O(A){var S="https://react.dev/errors/"+A;if(1<arguments.length){S+="?args[]="+encodeURIComponent(arguments[1]);for(var D=2;D<arguments.length;D++)S+="&args[]="+encodeURIComponent(arguments[D])}return"Minified React error #"+A+"; visit "+S+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function T(){}var d={d:{f:T,r:function(){throw Error(O(522))},D:T,C:T,L:T,m:T,X:T,S:T,M:T},p:0,findDOMNode:null},E=Symbol.for("react.portal"),H=Symbol.for("react.recoverable"),J=Symbol.for("react.optimistic_key");function K(A,S,D){var w=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:E,key:w==null?null:w===J?J:""+w,children:A,containerInfo:S,implementation:D}}var Q=x.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function W(A,S){if(A==="font")return"";if(typeof S=="string")return S==="use-credentials"?S:""}return Wl.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=d,Wl.browser=function(A){return{$$typeof:H,_reason:A}},Wl.createPortal=function(A,S){var D=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!S||S.nodeType!==1&&S.nodeType!==9&&S.nodeType!==11)throw Error(O(299));return K(A,S,null,D)},Wl.flushSync=function(A){var S=Q.T,D=d.p;try{if(Q.T=null,d.p=2,A)return A()}finally{Q.T=S,d.p=D,d.d.f()}},Wl.preconnect=function(A,S){typeof A=="string"&&(S?(S=S.crossOrigin,S=typeof S=="string"?S==="use-credentials"?S:"":void 0):S=null,d.d.C(A,S))},Wl.prefetchDNS=function(A){typeof A=="string"&&d.d.D(A)},Wl.preinit=function(A,S){if(typeof A=="string"&&S&&typeof S.as=="string"){var D=S.as,w=W(D,S.crossOrigin),U=typeof S.integrity=="string"?S.integrity:void 0,vl=typeof S.fetchPriority=="string"?S.fetchPriority:void 0;D==="style"?d.d.S(A,typeof S.precedence=="string"?S.precedence:void 0,{crossOrigin:w,integrity:U,fetchPriority:vl}):D==="script"&&d.d.X(A,{crossOrigin:w,integrity:U,fetchPriority:vl,nonce:typeof S.nonce=="string"?S.nonce:void 0})}},Wl.preinitModule=function(A,S){if(typeof A=="string")if(typeof S=="object"&&S!==null){if(S.as==null||S.as==="script"){var D=W(S.as,S.crossOrigin);d.d.M(A,{crossOrigin:D,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0})}}else S==null&&d.d.M(A)},Wl.preload=function(A,S){if(typeof A=="string"&&typeof S=="object"&&S!==null&&typeof S.as=="string"){var D=S.as,w=W(D,S.crossOrigin);d.d.L(A,D,{crossOrigin:w,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,type:typeof S.type=="string"?S.type:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0,referrerPolicy:typeof S.referrerPolicy=="string"?S.referrerPolicy:void 0,imageSrcSet:typeof S.imageSrcSet=="string"?S.imageSrcSet:void 0,imageSizes:typeof S.imageSizes=="string"?S.imageSizes:void 0,media:typeof S.media=="string"?S.media:void 0})}},Wl.preloadModule=function(A,S){if(typeof A=="string")if(S){var D=W(S.as,S.crossOrigin);d.d.m(A,{as:typeof S.as=="string"&&S.as!=="script"?S.as:void 0,crossOrigin:D,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0})}else d.d.m(A)},Wl.requestFormReset=function(A){d.d.r(A)},Wl.unstable_batchedUpdates=function(A,S){return A(S)},Wl.useFormState=function(A,S,D){return Q.H.useFormState(A,S,D)},Wl.useFormStatus=function(){return Q.H.useHostTransitionStatus()},Wl.version="19.3.1",Wl}var r0;function Ov(){if(r0)return js.exports;r0=1;function x(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(x)}catch(O){console.error(O)}}return x(),js.exports=zv(),js.exports}/**
* @license React
* react-dom-client.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var d0;function jv(){if(d0)return cn;d0=1;var x=Ev(),O=As(),T=Ov();function d(l){var t="https://react.dev/errors/"+l;if(1<arguments.length){t+="?args[]="+encodeURIComponent(arguments[1]);for(var e=2;e<arguments.length;e++)t+="&args[]="+encodeURIComponent(arguments[e])}return"Minified React error #"+l+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function E(l){return!(!l||l.nodeType!==1&&l.nodeType!==9&&l.nodeType!==11)}function H(l){for(var t=l,e=t;e&&!e.alternate;)t=e,(t.flags&4098)!==0&&(l=t.return),e=t.return;for(;t.return;)t=t.return;return t.tag===3?l:null}function J(l){if(l.tag===13){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function K(l){if(l.tag===31){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function Q(l){if(H(l)!==l)throw Error(d(188))}function W(l){var t=l.alternate;if(!t){if(t=H(l),t===null)throw Error(d(188));return t!==l?null:l}for(var e=l,a=t;;){var u=e.return;if(u===null)break;var n=u.alternate;if(n===null){if(a=u.return,a!==null){e=a;continue}break}if(u.child===n.child){for(n=u.child;n;){if(n===e)return Q(u),l;if(n===a)return Q(u),t;n=n.sibling}throw Error(d(188))}if(e.return!==a.return)e=u,a=n;else{for(var c=!1,i=u.child;i;){if(i===e){c=!0,e=u,a=n;break}if(i===a){c=!0,a=u,e=n;break}i=i.sibling}if(!c){for(i=n.child;i;){if(i===e){c=!0,e=n,a=u;break}if(i===a){c=!0,a=n,e=u;break}i=i.sibling}if(!c)throw Error(d(189))}}if(e.alternate!==a)throw Error(d(190))}if(e.tag!==3)throw Error(d(188));return e.stateNode.current===e?l:t}function A(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l;for(l=l.child;l!==null;){if(t=A(l),t!==null)return t;l=l.sibling}return null}function S(l,t,e,a,u,n){for(;l!==null;){if((l.tag===5||l.tag===27||l.tag===6)&&e(l,a,u,n)||(l.tag!==22||l.memoizedState===null)&&(t||l.tag!==5&&l.tag!==27)&&S(l.child,t,e,a,u,n))return!0;l=l.sibling}return!1}function D(l){for(l=l.return;l!==null;){if(l.tag===3||l.tag===5||l.tag===27)return l;l=l.return}return null}function w(l){var t=!1;for(l=l.return;l!==null&&(l.tag===4&&(t=!0),!(l.tag===3||l.tag===5||l.tag===27));)l=l.return;return t}function U(l){var t=[null,null],e=D(l);return e===null||vl(t,l,e.child,{foundSelf:!1}),t}function vl(l,t,e,a){for(;e!==null;){if(e===t)a.foundSelf=!0;else if(e.tag===5||e.tag===27||e.tag===6){if(a.foundSelf)return l[1]=e,!0;l[0]=e}else if((e.tag!==22||e.memoizedState===null)&&vl(l,t,e.child,a))return!0;e=e.sibling}return!1}function P(l){switch(l.tag){case 5:case 27:case 6:return l.stateNode;case 3:return l.stateNode.containerInfo;default:throw Error(d(559))}}var jl=null,Bl=null;function I(l,t,e){return l===e?!0:l===t?(jl=l,!0):!1}function il(l,t,e){return l===e?(Bl=l,!1):l===t?(Bl!==null&&(jl=l),!0):!1}function Il(l){if(l===null)return null;do l=l===null?null:l.return;while(l&&l.tag!==5&&l.tag!==27&&l.tag!==3);return l||null}function Pl(l,t,e){for(var a=0,u=l;u;u=e(u))a++;u=0;for(var n=t;n;n=e(n))u++;for(;0<a-u;)l=e(l),a--;for(;0<u-a;)t=e(t),u--;for(;a--;){if(l===t||t!==null&&l===t.alternate)return l;l=e(l),t=e(t)}return null}var ll=Object.assign,ol=Symbol.for("react.element"),Qt=Symbol.for("react.transitional.element"),Tt=Symbol.for("react.portal"),_t=Symbol.for("react.fragment"),rt=Symbol.for("react.strict_mode"),be=Symbol.for("react.profiler"),$e=Symbol.for("react.consumer"),ql=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),X=Symbol.for("react.suspense"),L=Symbol.for("react.suspense_list"),pl=Symbol.for("react.memo"),ml=Symbol.for("react.lazy"),Rt=Symbol.for("react.activity"),te=Symbol.for("react.legacy_hidden"),We=Symbol.for("react.memo_cache_sentinel"),r=Symbol.for("react.view_transition"),_=Symbol.for("react.recoverable"),B=Symbol.iterator;function q(l){return l===null||typeof l!="object"?null:(l=B&&l[B]||l["@@iterator"],typeof l=="function"?l:null)}var cl=Symbol.for("react.client.reference");function fl(l){if(l==null)return null;if(typeof l=="function")return l.$$typeof===cl?null:l.displayName||l.name||null;if(typeof l=="string")return l;switch(l){case _t:return"Fragment";case be:return"Profiler";case rt:return"StrictMode";case X:return"Suspense";case L:return"SuspenseList";case Rt:return"Activity";case r:return"ViewTransition"}if(typeof l=="object")switch(l.$$typeof){case Tt:return"Portal";case ql:return l.displayName||"Context";case $e:return(l._context.displayName||"Context")+".Consumer";case j:var t=l.render;return l=l.displayName,l||(l=t.displayName||t.name||"",l=l!==""?"ForwardRef("+l+")":"ForwardRef"),l;case pl:return t=l.displayName||null,t!==null?t:fl(l.type)||"Memo";case ml:t=l._payload,l=l._init;try{return fl(l(t))}catch{}}return null}var rl=Array.isArray,R=O.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Z=T.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ee={pending:!1,data:null,method:null,action:nLine truncated
at`)?" (<anonymous>)":-1<e.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Lc+l+Us}var Zc=!1;function Vc(l,t){if(!l||Zc)return"";Zc=!0;var e=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(t){var N=function(){throw Error()};if(Object.defineProperty(N.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(N,[])}catch(z){var m=z}Reflect.construct(l,[],N)}else{try{N.call()}catch(z){m=z}N=!1;try{var g=Object.getOwnPropertyDescriptor(l.prototype,"props");Object.defineProperty(l.prototype,"props",{configurable:!0,set:function(){throw Error()}}),N=!0,new l}finally{N&&(g!==void 0?Object.defineProperty(l.prototype,"props",g):delete l.prototype.props)}}}else{try{throw Error()}catch(z){m=z}(N=l())&&typeof N.catch=="function"&&N.catch(function(){})}}catch(z){if(z&&m&&typeof z.stack=="string")return[z.stack,m.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var u=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");u&&u.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=a.DetermineComponentFrameRoot(),c=n[0],i=n[1];if(c&&i){var s=c.split(`
`),v=i.split(`
`);for(u=a=0;a<s.length&&!s[a].includes("DetermineComponentFrameRoot");)a++;for(;u<v.length&&!v[u].includes("DetermineComponentFrameRoot");)u++;if(a===s.length||u===v.length)for(a=s.length-1,u=v.length-1;1<=a&&0<=u&&s[a]!==v[u];)u--;for(;1<=a&&0<=u;a--,u--)if(s[a]!==v[u]){if(a!==1||u!==1)do if(a--,u--,0>u||s[a]!==v[u]){var b=`
`+s[a].replace(" at new "," at ");return l.displayName&&b.includes("<anonymous>")&&(b=b.replace("<anonymous>",l.displayName)),b}while(1<=a&&0<=u);break}}}finally{Zc=!1,Error.prepareStackTrace=e}return(e=l?l.displayName||l.name:"")?Se(e):""}function E0(l,t){switch(l.tag){case 26:case 27:case 5:return Se(l.type);case 16:return Se("Lazy");case 13:return l.child!==t&&t!==null?Se("Suspense Fallback"):Se("Suspense");case 19:return Se("SuspenseList");case 0:case 15:return Vc(l.type,!1);case 11:return Vc(l.type.render,!1);case 1:return Vc(l.type,!0);case 31:return Se("Activity");case 30:return Se("ViewTransition");default:return""}}function Rs(l){try{var t="",e=null;do t+=E0(l,e),e=l,l=l.return;while(l);return t}catch(a){return`
Error generating stack: `+a.message+`
`+a.stack}}var Kc=Object.prototype.hasOwnProperty,Jc=x.unstable_scheduleCallback,kc=x.unstable_cancelCallback,z0=x.unstable_shouldYield,O0=x.unstable_requestPaint,dt=x.unstable_now,j0=x.unstable_getCurrentPriorityLevel,Hs=x.unstable_ImmediatePriority,Ys=x.unstable_UserBlockingPriority,rn=x.unstable_NormalPriority,A0=x.unstable_LowPriority,Bs=x.unstable_IdlePriority,C0=x.log,M0=x.unstable_setDisableYieldValue,du=null,mt=null;function pe(l){if(typeof C0=="function"&&M0(l),mt&&typeof mt.setStrictMode=="function")try{mt.setStrictMode(du,l)}catch{}}var ht=Math.clz32?Math.clz32:R0,D0=Math.log,U0=Math.LN2;function R0(l){return l>>>=0,l===0?32:31-(D0(l)/U0|0)|0}var dn=256,mn=262144,hn=4194304;function Fe(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&-l;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function vn(l,t,e){var a=l.pendingLanes;if(a===0)return 0;var u=0,n=l.suspendedLanes,c=l.pingedLanes;l=l.warmLanes;var i=a&134217727;return i!==0?(a=i&~n,a!==0?u=Fe(a):(c&=i,c!==0?u=Fe(c):e||(e=i&~l,e!==0&&(u=Fe(e))))):(i=a&~n,i!==0?u=Fe(i):c!==0?u=Fe(c):e||(e=a&~l,e!==0&&(u=Fe(e)))),u===0?0:t!==0&&t!==u&&(t&n)===0&&(n=u&-u,e=t&-t,n>=e||n===32&&(e&4194048)!==0)?t:u}function mu(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function qs(l,t){(t&8)!==0&&(t|=t&32);var e=l.entangledLanes;if(e!==0)for(l=l.entanglements,e&=t;0<e;){var a=31-ht(e),u=1<<a;t|=l[a],e&=~u}return t}function H0(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ws(){var l=hn;return hn<<=1,(hn&62914560)===0&&(hn=4194304),l}function $c(l){for(var t=[],e=0;31>e;e++)t.push(l);return t}function hu(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function Y0(l,t,e,a,u,n){var c=l.pendingLanes;l.pendingLanes=e,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=e,l.entangledLanes&=e,l.errorRecoveryDisabledLanes&=e,l.shellSuspendCounter=0;var i=l.entanglements,s=l.expirationTimes,v=l.hiddenUpdates;for(e=c&~e;0<e;){var b=31-ht(e),N=1<<b;i[b]=0,s[b]=-1;var m=v[b];if(m!==null)for(v[b]=null,b=0;b<m.length;b++){var g=m[b];g!==null&&(g.lane&=-536870913)}e&=~N}a!==0&&Gs(l,a,0),n!==0&&u===0&&l.tag!==0&&(l.suspendedLanes|=n&~(c&~t))}function Gs(l,t,e){l.pendingLanes|=t,l.suspendedLanes&=~t;var a=31-ht(t);l.entangledLanes|=t,l.entanglements[a]=l.entanglements[a]|1073741824|e&261930}function Qs(l,t){var e=l.entangledLanes|=t;for(l=l.entanglements;e;){var a=31-ht(e),u=1<<a;u&t|l[a]&t&&(l[a]|=t),e&=~u}}function Xs(l,t){var e=t&-t;return e=(e&42)!==0?1:Wc(e),(e&(l.suspendedLanes|t))!==0?0:e}function Wc(l){switch(l){case 2:l=1;break;case 8:l=4;break;case 32:l=16;break;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:case 4194304:case 8388608:case 16777216:case 33554432:l=128;break;case 268435456:l=134217728;break;default:l=0}return l}function Fc(l){return l&=-l,2<l?8<l?(l&134217727)!==0?32:268435456:8:2}function Ls(){var l=Z.p;return l!==0?l:(l=window.event,l===void 0?32:Fm(l.type))}function Zs(l,t){var e=Z.p;try{return Z.p=l,t()}finally{Z.p=e}}var ae=Math.random().toString(36).slice(2),Zl="__reactFiber$"+ae,nt="__reactProps$"+ae,Na="__reactContainer$"+ae,Vs="__reactEvents$"+ae,B0="__reactListeners$"+ae,q0="__reactHandles$"+ae,Ks="__reactResources$"+ae,vu="__reactMarker$"+ae,yn="__reactLoad$"+ae;function gn(l){delete l[Zl],delete l[nt],delete l[B0],delete l[q0]}function Ie(l){var t;if(t=l[Zl])return t;for(var e=l.parentNode;e;){if(t=e[Na]||e[Zl]){if(e=t.alternate,t.child!==null||e!==null&&e.child!==null)for(l=Mm(l);l!==null;){if(e=l[Zl])return e;l=Mm(l)}return t}l=e,e=l.parentNode}return null}function Ta(l){if(l=l[Zl]||l[Na]){var t=l.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return l}return null}function yu(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l.stateNode;throw Error(d(33))}function _a(l){var t=l[Ks];return t||(t=l[Ks]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function wl(l){l[vu]=!0}function Js(l){l[yn]=void 0}var ks=new Set,$s={};function Pe(l,t){Ea(l,t),Ea(l+"Capture",t)}function Line truncated
`).replace(E1,"")}function rm(l,t){return t=om(t),om(l)===t}function bl(l,t,e,a,u,n){switch(e){case"children":if(typeof a=="string")t==="body"||t==="textarea"&&a===""||Oa(l,a);else if(typeof a=="number"||typeof a=="bigint")t!=="body"&&Oa(l,""+a);else return;break;case"className":xn(l,"class",a);break;case"tabIndex":xn(l,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":xn(l,e,a);break;case"style":no(l,a,n);return;case"data":if(t!=="object"){xn(l,"data",a);break}case"src":case"href":if(a===""&&(t!=="a"||e!=="href")){l.removeAttribute(e);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){l.removeAttribute(e);break}a=Sn(a),l.setAttribute(e,a);break;case"action":case"formAction":if(typeof a=="function"){l.setAttribute(e,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof n=="function"&&(e==="formAction"?(t!=="input"&&bl(l,t,"name",u.name,u,null),bl(l,t,"formEncType",u.formEncType,u,null),bl(l,t,"formMethod",u.formMethod,u,null),bl(l,t,"formTarget",u.formTarget,u,null)):(bl(l,t,"encType",u.encType,u,null),bl(l,t,"method",u.method,u,null),bl(l,t,"target",u.target,u,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){l.removeAttribute(e);break}a=Sn(a),l.setAttribute(e,a);break;case"onClick":a!=null&&(l.onclick=Zt);return;case"onScroll":a!=null&&el("scroll",l);return;case"onScrollEnd":a!=null&&el("scrollend",l);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(d(61));if(e=a.__html,e!=null){if(u.children!=null)throw Error(d(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"multiple":l.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":l.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){l.removeAttribute("xlink:href");break}e=Sn(a),l.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",e);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,a):l.removeAttribute(e);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,""):l.removeAttribute(e);break;case"capture":case"download":a===!0?l.setAttribute(e,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,a):l.removeAttribute(e);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?l.setAttribute(e,a):l.removeAttribute(e);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?l.removeAttribute(e):l.setAttribute(e,a);break;case"popover":el("beforetoggle",l),el("toggle",l),bn(l,"popover",a);break;case"xlinkActuate":ue(l,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":ue(l,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":ue(l,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":ue(l,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":ue(l,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":ue(l,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":ue(l,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":ue(l,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":ue(l,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":bn(l,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<e.length)||e[0]!=="o"&&e[0]!=="O"||e[1]!=="n"&&e[1]!=="N")e=Z0.get(e)||e,bn(l,e,a);else return}sl=!0}function ls(l,t,e,a,u,n){switch(e){case"style":no(l,a,n);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(d(61));if(e=a.__html,e!=null){if(u.children!=null)throw Error(d(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"children":if(typeof a=="string")Oa(l,a);else if(typeof a=="number"||typeof a=="bigint")Oa(l,""+a);else return;break;case"onScroll":a!=null&&el("scroll",l);return;case"onScrollEnd":a!=null&&el("scro Line truncated
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Qv=x=>x.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),S0=(...x)=>x.filter((O,T,d)=>!!O&&O.trim()!==""&&d.indexOf(O)===T).join(" ").trim();/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/var Xv={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Lv=xl.forwardRef(({color:x="currentColor",size:O=24,strokeWidth:T=2,absoluteStrokeWidth:d,className:E="",children:H,iconNode:J,...K},Q)=>xl.createElement("svg",{ref:Q,...Xv,width:O,height:O,stroke:x,strokeWidth:d?Number(T)*24/Number(O):T,className:S0("lucide",E),...K},[...J.map(([W,A])=>xl.createElement(W,A)),...Array.isArray(H)?H:[H]]));/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Fl=(x,O)=>{const T=xl.forwardRef(({className:d,...E},H)=>xl.createElement(Lv,{ref:H,iconNode:O,className:S0(`lucide-${Qv(x)}`,d),...E}));return T.displayName=`${x}`,T};/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Zv=[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]],p0=Fl("BookOpen",Zv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Vv=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],N0=Fl("Check",Vv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Kv=[["path",{d:"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49",key:"ct8e1f"}],["path",{d:"M14.084 14.158a3 3 0 0 1-4.242-4.242",key:"151rxh"}],["path",{d:"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143",key:"13bj9a"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],Jv=Fl("EyeOff",Kv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const kv=[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]],$v=Fl("Eye",kv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Wv=[["path",{d:"M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",key:"1s6t7t"}],["circle",{cx:"16.5",cy:"7.5",r:".5",fill:"currentColor",key:"w0ekpg"}]],y0=Fl("KeyRound",Wv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Fv=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]],Iv=Fl("Layers",Fv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Pv=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]],ly=Fl("LayoutGrid",Pv);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ty=[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]],ey=Fl("Lock",ty);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ay=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]],uy=Fl("Moon",ay);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ny=[["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["path",{d:"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z",key:"12rzf8"}]],cy=Fl("Palette",ny);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const iy=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M9 21V9",key:"1oto5p"}]],fy=Fl("PanelsTopLeft",iy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const sy=[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]],Cs=Fl("Search",sy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const oy=[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],ry=Fl("ShieldCheck",oy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const dy=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]],my=Fl("Sparkles",dy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const hy=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],vy=Fl("Sun",hy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const yy=[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]],Ms=Fl("Terminal",yy);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const gy=[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]],by=Fl("User",gy),xy=[{id:"auto",label:"智能路由"},{id:"edge443",label:"443 免端口"},{id:"home10443",label:"10443 家宽"},{id:"lan",label:"LAN 局域网"}],Sy=()=>{const{theme:x,resolvedTheme:O,routeMode:T,viewMode:d,telemetry:E,setTheme:H,toggleTheme:J,setRouteMode:K,setViewMode:Q,setShowAgyModal:W,setShowCmdPalette:A}=ke();return f.jsxs(f.Fragment,{children:[f.jsx("header",{className:"border-b hl-border sticky top-0 z-30 backdrop-blur-md",style:{backgroundColor:"color-mix(in srgb, var(--bg-card) 88%, transparent)"},children:f.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-14 flex items-center justify-between gap-4",children:[f.jsxs("div",{className:"flex items-center gap-4 shrink-0",children:[f.jsxs("div",{onClick:()=>Q("dashboard"),className:"flex items-center gap-2.5 cursor-pointer",children:[f.jsx("img",{src:"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/apple.png",alt:"Mac mini",className:"w-6 h-6 rounded"}),f.jsx("span",{className:"font-semibold text-sm tracking-tight",children:"HomeLab"}),f.jsx("span",{className:"w-2 h-2 rounded-full",style:{backgroundColor:"var(--success)"},title:"全屋 4 节点在线"})]}),f.jsx("div",{className:"h-4 w-[1px] hl-muted"}),f.jsxs("nav",{className:"flex items-center gap-1 text-xs",children:[f.jsxs("button",{onClick:()=>Q("dashboard"),className:"px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer",style:d==="dashboard"?{backgroundColor:"var(--bg-muted)",color:"var(--text-main)"}:{color:"var(--text-muted)"},children:[f.jsx(ly,{className:"w-3.5 h-3.5"}),f.jsx("span",{children:"中控台"})]}),f.jsxs("button",{onClick:()=>Q("storybook"),className:"px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer",style:d==="storybook"?{backgroundColor:"var(--bg-muted)",color:"var(--text-main)"}:{color:"var(--text-muted)"},children:[f.jsx(p0,{className:"w-3.5 h-3.5"}),f.jsx("span",{children:"Storybook 设计规范"})]})]})]}),f.jsxs("div",{className:"flex items-center gap-2.5 shrink-0 ml-auto",children:[f.jsxs("button",{onClick:()=>A(!0),className:"hl-btn-outline px-3 py-1.5 text-xs flex items-center gap-3 cursor-pointer",title:"按 Cmd+K 唤起全局命令面板",children:[f.jsxs("span",{className:"flex items-center gap-1.5",style:{color:"var(--text-muted)"},children:[f.jsx(Cs,{className:"w-3.5 h-3.5"}),f.jsx("span",{className:"hidden sm:inline",children:"搜索服务或指令..."})]}),f.jsx("kbd",{className:"px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-muted",style:{color:"var(--text-muted)"},children:"⌘K"})]}),f.jsxs("button",{onClick:()=>W(!0),className:"hl-btn-primary px-3 py-1.5 text-xs flex items-center gap-1.5 cursor-pointer",children:[f.jsx(Ms,{className:"w-3.5 h-3.5"}),f.jsx("span",{children:"AGY 终端"})]}),f.jsxs("div",{className:"flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs",children:[f.jsx("button",{onClick:J,className:"px-2.5 py-1 rounded-md font-medium transition-all flex items-center gap-1.5 cursor-pointer",style:{backgroundColor:"var(--bg-card)",color:"var(--text-main)",boxShadow:"0 1px 2px rgba(0,0,0,0.08)"},title:"点击切换深色 / 浅色主题(默认跟随系统)",children:O==="dark"?f.jsxs(f.Fragment,{children:[f.jsx(uy,{className:"w-3.5 h-3.5"}),f.jsx("span",{children:"深色"})]}):f.jsxs(f.Fragment,{children:[f.jsx(vy,{className:"w-3.5 h-3.5"}),f.jsx("span",{children:"浅色"})]})}),x!=="system"&&f.jsx("button",{onClick:()=>H("system"),className:"px-2 py-1 rounded-md text-[11px] transition-all cursor-pointer",style:{color:"var(--text-muted)"},title:"恢复跟随设备系统深浅色自动切换",children:"自动"})]})]})]})}),d==="dashboard"&&f.jsx("div",{className:"border-b hl-border",style:{backgroundColor:"var(--bg-card)"},children:f.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex flex-wrap items-center justify-between gap-3",children:[f.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-xs font-mono-num",children:[f.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",children:[f.jsx("span",{style:{color:"var(--text-muted)"},children:"M6 CPU"}),f.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:f.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,E.mini.cpu_pct)}%`}})}),f.jsxs("span",{className:"font-medium",children:[E.mini.cpu_pct,"%"]})]}),f.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",children:[f.jsx("span",{style:{color:"var(--text-muted)"},children:"RAM"}),f.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:f.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,E.mini.mem_pct)}%`}})}),f.jsxs("span",{className:"font-medium",children:[E.mini.mem_used_gb,"G"]})]}),f.jsxs("div",{className:"hl-pill px-2.Line truncated
-124
View File
@@ -1,124 +0,0 @@
(function(){const A=document.createElement("link").relList;if(A&&A.supports&&A.supports("modulepreload"))return;for(const O of document.querySelectorAll('link[rel="modulepreload"]'))d(O);new MutationObserver(O=>{for(const q of O)if(q.type==="childList")for(const al of q.addedNodes)al.tagName==="LINK"&&al.rel==="modulepreload"&&d(al)}).observe(document,{childList:!0,subtree:!0});function N(O){const q={};return O.integrity&&(q.integrity=O.integrity),O.referrerPolicy&&(q.referrerPolicy=O.referrerPolicy),O.crossOrigin==="use-credentials"?q.credentials="include":O.crossOrigin==="anonymous"?q.credentials="omit":q.credentials="same-origin",q}function d(O){if(O.ep)return;O.ep=!0;const q=N(O);fetch(O.href,q)}})();function m0(x){return x&&x.__esModule&&Object.prototype.hasOwnProperty.call(x,"default")?x.default:x}var Ns={exports:{}},nn={};/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var t0;function b1(){if(t0)return nn;t0=1;var x=Symbol.for("react.transitional.element"),A=Symbol.for("react.fragment");function N(d,O,q){var al=null;if(q!==void 0&&(al=""+q),O.key!==void 0&&(al=""+O.key),"key"in O){q={};for(var W in O)W!=="key"&&(q[W]=O[W])}else q=O;return O=q.ref,{$$typeof:x,type:d,key:al,ref:O!==void 0?O:null,props:q}}return nn.Fragment=A,nn.jsx=N,nn.jsxs=N,nn}var e0;function S1(){return e0||(e0=1,Ns.exports=b1()),Ns.exports}var s=S1(),Es={exports:{}},V={};/**
* @license React
* react.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var a0;function x1(){if(a0)return V;a0=1;var x=Symbol.for("react.transitional.element"),A=Symbol.for("react.portal"),N=Symbol.for("react.fragment"),d=Symbol.for("react.strict_mode"),O=Symbol.for("react.profiler"),q=Symbol.for("react.consumer"),al=Symbol.for("react.context"),W=Symbol.for("react.forward_ref"),k=Symbol.for("react.suspense"),F=Symbol.for("react.memo"),D=Symbol.for("react.lazy"),S=Symbol.for("react.activity"),_=Symbol.for("react.view_transition"),B=Symbol.iterator;function G(r){return r===null||typeof r!="object"?null:(r=B&&r[B]||r["@@iterator"],typeof r=="function"?r:null)}var $={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ul=Object.assign,Bl={};function nt(r,E,H){this.props=r,this.context=E,this.refs=Bl,this.updater=H||$}nt.prototype.isReactComponent={},nt.prototype.setState=function(r,E){if(typeof r!="object"&&typeof r!="function"&&r!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,r,E,"setState")},nt.prototype.forceUpdate=function(r){this.updater.enqueueForceUpdate(this,r,"forceUpdate")};function ke(){}ke.prototype=nt.prototype;function Bt(r,E,H){this.props=r,this.context=E,this.refs=Bl,this.updater=H||$}var Gt=Bt.prototype=new ke;Gt.constructor=Bt,ul(Gt,nt.prototype),Gt.isPureReactComponent=!0;var ct=Array.isArray;function I(){}var sl={H:null,A:null,T:null,S:null},wt=Object.prototype.hasOwnProperty;function St(r,E,H){var Y=H.ref;return{$$typeof:x,type:r,key:E,ref:Y!==void 0?Y:null,props:H}}function xt(r,E){return St(r.type,E,r.props)}function it(r){return typeof r=="object"&&r!==null&&r.$$typeof===x}function ge(r){var E={"=":"=0",":":"=2"};return"$"+r.replace(/[=:]/g,function(H){return E[H]})}var $e=/\/+/g;function Ul(r,E){return typeof r=="object"&&r!==null&&r.key!=null?ge(""+r.key):E.toString(36)}function M(r){switch(r.status){case"fulfilled":return r.value;case"rejected":throw r.reason;default:switch(typeof r.status=="string"?r.then(I,I):(r.status="pending",r.then(function(E){r.status==="pending"&&(r.status="fulfilled",r.value=E)},function(E){r.status==="pending"&&(r.status="rejected",r.reason=E)})),r.status){case"fulfilled":return r.value;case"rejected":throw r.reason}}throw r}function Q(r,E,H,Y,cl){var il=typeof r;(il==="undefined"||il==="boolean")&&(r=null);var ol=!1;if(r===null)ol=!0;else switch(il){case"bigint":case"string":case"number":ol=!0;break;case"object":switch(r.$$typeof){case x:case A:ol=!0;break;case D:return ol=r._init,Q(ol(r._payload),E,H,Y,cl)}}if(ol)return cl=cl(r),ol=Y===""?"."+Ul(r,0):Y,ct(cl)?(H="",ol!=null&&(H=ol.replace($e,"$&/")+"/"),Q(cl,E,H,"",function(te){return te})):cl!=null&&(it(cl)&&(cl=xt(cl,H+(cl.key==null||r&&r.key===cl.key?"":(""+cl.key).replace($e,"$&/")+"/")+ol)),E.push(cl)),1;ol=0;var U=Y===""?".":Y+":";if(ct(r))for(var Z=0;Z<r.length;Z++)Y=r[Z],il=U+Ul(Y,Z),ol+=Q(Y,E,H,il,cl);else if(Z=G(r),typeof Z=="function")for(r=Z.call(r),Z=0;!(Y=r.next()).done;)Y=Y.value,il=U+Ul(Y,Z++),ol+=Q(Y,E,H,il,cl);else if(il==="object"){if(typeof r.then=="function")return Q(M(r),E,H,Y,cl);throw E=String(r),Error("Objects are not valid as a React child (found: "+(E==="[object Object]"?"object with keys {"+Object.keys(r).join(", ")+"}":E)+"). If you meant to render a collection of children, use an array instead.")}return ol}function X(r,E,H){if(r==null)return r;var Y=[],cl=0;return Q(r,Y,"","",function(il){return E.call(H,il,cl++)}),Y}function bl(r){if(r._status===-1){var E=r._result,H=E();H.then(function(Y){(r._status===0||r._status===-1)&&(r._status=1,r._result=Y,H.status===void 0&&(H.status="fulfilled",H.value=Y))},function(Y){(r._status===0||r._status===-1)&&(r._status=2,r._result=Y,H.status===void 0&&(H.status="rejected",H.reason=Y))}),r._status===-1&&(r._status=0,r._result=H)}if(r._status===1)return r._result.default;throw r._result}var dl=typeof reportError=="function"?reportError:function(r){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var E=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof r=="object"&&r!==null&&typeof r.message=="string"?String(r.message):String(r),error:r});if(!window.dispatchEvent(E))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",r);return}console.error(r)};function jt(r){var E=sl.T,H={};H.types=E!==null?E.types:null,sl.T=H;try{var Y=r(),cl=sl.S;cl!==null&&cl(H,Y),typeof Y=="object"&&Y!==null&&typeof Y.then=="function"&&Y.then(I,dl)}catch(il){dl(il)}finally{E!==null&&H.types!==null&&(E.types=H.types),sl.T=E}}function le(r){var E=sl.T;if(E!==null){var H=E.types;H===null?E.types=[r]:H.indexOf(r)===-1&&H.push(r)}else jt(le.bind(null,r))}var We={map:X,forEach:function(r,E,H){X(r,function(){E.apply(this,arguments)},H)},count:function(r){var E=0;return X(r,function(){E++}),E},toArray:function(r){return X(r,function(E){return E})||[]},only:function(r){if(!itLine truncated
* @license React
* scheduler.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var n0;function p1(){return n0||(n0=1,(function(x){function A(M,Q){var X=M.length;M.push(Q);l:for(;0<X;){var bl=X-1>>>1,dl=M[bl];if(0<O(dl,Q))M[bl]=Q,M[X]=dl,X=bl;else break l}}function N(M){return M.length===0?null:M[0]}function d(M){if(M.length===0)return null;var Q=M[0],X=M.pop();if(X!==Q){M[0]=X;l:for(var bl=0,dl=M.length,jt=dl>>>1;bl<jt;){var le=2*(bl+1)-1,We=M[le],r=le+1,E=M[r];if(0>O(We,X))r<dl&&0>O(E,We)?(M[bl]=E,M[r]=X,bl=r):(M[bl]=We,M[le]=X,bl=le);else if(r<dl&&0>O(E,X))M[bl]=E,M[r]=X,bl=r;else break l}}return Q}function O(M,Q){var X=M.sortIndex-Q.sortIndex;return X!==0?X:M.id-Q.id}if(x.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var q=performance;x.unstable_now=function(){return q.now()}}else{var al=Date,W=al.now();x.unstable_now=function(){return al.now()-W}}var k=[],F=[],D=1,S=null,_=3,B=!1,G=!1,$=!1,ul=!1,Bl=typeof setTimeout=="function"?setTimeout:null,nt=typeof clearTimeout=="function"?clearTimeout:null,ke=typeof setImmediate<"u"?setImmediate:null;function Bt(M){for(var Q=N(F);Q!==null;){if(Q.callback===null)d(F);else if(Q.startTime<=M)d(F),Q.sortIndex=Q.expirationTime,A(k,Q);else break;Q=N(F)}}function Gt(M){if($=!1,Bt(M),!G)if(N(k)!==null)G=!0,ct||(ct=!0,it());else{var Q=N(F);Q!==null&&Ul(Gt,Q.startTime-M)}}var ct=!1,I=-1,sl=5,wt=-1;function St(){return ul?!0:!(x.unstable_now()-wt<sl)}function xt(){if(ul=!1,ct){var M=x.unstable_now();wt=M;var Q=!0;try{l:{G=!1,$&&($=!1,nt(I),I=-1),B=!0;var X=_;try{t:{for(Bt(M),S=N(k);S!==null&&!(S.expirationTime>M&&St());){var bl=S.callback;if(typeof bl=="function"){S.callback=null,_=S.priorityLevel;var dl=bl(S.expirationTime<=M);if(M=x.unstable_now(),typeof dl=="function"){S.callback=dl,Bt(M),Q=!0;break t}S===N(k)&&d(k),Bt(M)}else d(k);S=N(k)}if(S!==null)Q=!0;else{var jt=N(F);jt!==null&&Ul(Gt,jt.startTime-M),Q=!1}}break l}finally{S=null,_=X,B=!1}Q=void 0}}finally{Q?it():ct=!1}}}var it;if(typeof ke=="function")it=function(){ke(xt)};else if(typeof MessageChannel<"u"){var ge=new MessageChannel,$e=ge.port2;ge.port1.onmessage=xt,it=function(){$e.postMessage(null)}}else it=function(){Bl(xt,0)};function Ul(M,Q){I=Bl(function(){M(x.unstable_now())},Q)}x.unstable_IdlePriority=5,x.unstable_ImmediatePriority=1,x.unstable_LowPriority=4,x.unstable_NormalPriority=3,x.unstable_Profiling=null,x.unstable_UserBlockingPriority=2,x.unstable_cancelCallback=function(M){M.callback=null},x.unstable_forceFrameRate=function(M){0>M||125<M?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):sl=0<M?Math.floor(1e3/M):5},x.unstable_getCurrentPriorityLevel=function(){return _},x.unstable_next=function(M){switch(_){case 1:case 2:case 3:var Q=3;break;default:Q=_}var X=_;_=Q;try{return M()}finally{_=X}},x.unstable_requestPaint=function(){ul=!0},x.unstable_runWithPriority=function(M,Q){switch(M){case 1:case 2:case 3:case 4:case 5:break;default:M=3}var X=_;_=M;try{return Q()}finally{_=X}},x.unstable_scheduleCallback=function(M,Q,X){var bl=x.unstable_now();switch(typeof X=="object"&&X!==null?(X=X.delay,X=typeof X=="number"&&0<X?bl+X:bl):X=bl,M){case 1:var dl=-1;break;case 2:dl=250;break;case 5:dl=1073741823;break;case 4:dl=1e4;break;default:dl=5e3}return dl=X+dl,M={id:D++,callback:Q,priorityLevel:M,startTime:X,expirationTime:dl,sortIndex:-1},X>bl?(M.sortIndex=X,A(F,M),N(k)===null&&M===N(F)&&($?(nt(I),I=-1):$=!0,Ul(Gt,X-bl))):(M.sortIndex=dl,A(k,M),G||B||(G=!0,ct||(ct=!0,it()))),M},x.unstable_shouldYield=St,x.unstable_wrapCallback=function(M){var Q=_;return function(){var X=_;_=Q;try{return M.apply(this,arguments)}finally{_=X}}}})(Os)),Os}var c0;function T1(){return c0||(c0=1,zs.exports=p1()),zs.exports}var As={exports:{}},Kl={};/**
* @license React
* react-dom.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var i0;function N1(){if(i0)return Kl;i0=1;var x=Ms();function A(D){var S="https://react.dev/errors/"+D;if(1<arguments.length){S+="?args[]="+encodeURIComponent(arguments[1]);for(var _=2;_<arguments.length;_++)S+="&args[]="+encodeURIComponent(arguments[_])}return"Minified React error #"+D+"; visit "+S+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function N(){}var d={d:{f:N,r:function(){throw Error(A(522))},D:N,C:N,L:N,m:N,X:N,S:N,M:N},p:0,findDOMNode:null},O=Symbol.for("react.portal"),q=Symbol.for("react.recoverable"),al=Symbol.for("react.optimistic_key");function W(D,S,_){var B=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:O,key:B==null?null:B===al?al:""+B,children:D,containerInfo:S,implementation:_}}var k=x.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function F(D,S){if(D==="font")return"";if(typeof S=="string")return S==="use-credentials"?S:""}return Kl.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=d,Kl.browser=function(D){return{$$typeof:q,_reason:D}},Kl.createPortal=function(D,S){var _=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!S||S.nodeType!==1&&S.nodeType!==9&&S.nodeType!==11)throw Error(A(299));return W(D,S,null,_)},Kl.flushSync=function(D){var S=k.T,_=d.p;try{if(k.T=null,d.p=2,D)return D()}finally{k.T=S,d.p=_,d.d.f()}},Kl.preconnect=function(D,S){typeof D=="string"&&(S?(S=S.crossOrigin,S=typeof S=="string"?S==="use-credentials"?S:"":void 0):S=null,d.d.C(D,S))},Kl.prefetchDNS=function(D){typeof D=="string"&&d.d.D(D)},Kl.preinit=function(D,S){if(typeof D=="string"&&S&&typeof S.as=="string"){var _=S.as,B=F(_,S.crossOrigin),G=typeof S.integrity=="string"?S.integrity:void 0,$=typeof S.fetchPriority=="string"?S.fetchPriority:void 0;_==="style"?d.d.S(D,typeof S.precedence=="string"?S.precedence:void 0,{crossOrigin:B,integrity:G,fetchPriority:$}):_==="script"&&d.d.X(D,{crossOrigin:B,integrity:G,fetchPriority:$,nonce:typeof S.nonce=="string"?S.nonce:void 0})}},Kl.preinitModule=function(D,S){if(typeof D=="string")if(typeof S=="object"&&S!==null){if(S.as==null||S.as==="script"){var _=F(S.as,S.crossOrigin);d.d.M(D,{crossOrigin:_,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0})}}else S==null&&d.d.M(D)},Kl.preload=function(D,S){if(typeof D=="string"&&typeof S=="object"&&S!==null&&typeof S.as=="string"){var _=S.as,B=F(_,S.crossOrigin);d.d.L(D,_,{crossOrigin:B,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,type:typeof S.type=="string"?S.type:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0,referrerPolicy:typeof S.referrerPolicy=="string"?S.referrerPolicy:void 0,imageSrcSet:typeof S.imageSrcSet=="string"?S.imageSrcSet:void 0,imageSizes:typeof S.imageSizes=="string"?S.imageSizes:void 0,media:typeof S.media=="string"?S.media:void 0})}},Kl.preloadModule=function(D,S){if(typeof D=="string")if(S){var _=F(S.as,S.crossOrigin);d.d.m(D,{as:typeof S.as=="string"&&S.as!=="script"?S.as:void 0,crossOrigin:_,integrity:typeof S.integrity=="string"?S.integrity:void 0,nonce:typeof S.nonce=="string"?S.nonce:void 0,fetchPriority:typeof S.fetchPriority=="string"?S.fetchPriority:void 0})}else d.d.m(D)},Kl.requestFormReset=function(D){d.d.r(D)},Kl.unstable_batchedUpdates=function(D,S){return D(S)},Kl.useFormState=function(D,S,_){return k.H.useFormState(D,S,_)},Kl.useFormStatus=function(){return k.H.useHostTransitionStatus()},Kl.version="19.3.1",Kl}var f0;function E1(){if(f0)return As.exports;f0=1;function x(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(x)}catch(A){console.error(A)}}return x(),As.exports=N1(),As.exports}/**
* @license React
* react-dom-client.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/var s0;function _1(){if(s0)return cn;s0=1;var x=T1(),A=Ms(),N=E1();function d(l){var t="https://react.dev/errors/"+l;if(1<arguments.length){t+="?args[]="+encodeURIComponent(arguments[1]);for(var e=2;e<arguments.length;e++)t+="&args[]="+encodeURIComponent(arguments[e])}return"Minified React error #"+l+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function O(l){return!(!l||l.nodeType!==1&&l.nodeType!==9&&l.nodeType!==11)}function q(l){for(var t=l,e=t;e&&!e.alternate;)t=e,(t.flags&4098)!==0&&(l=t.return),e=t.return;for(;t.return;)t=t.return;return t.tag===3?l:null}function al(l){if(l.tag===13){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function W(l){if(l.tag===31){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function k(l){if(q(l)!==l)throw Error(d(188))}function F(l){var t=l.alternate;if(!t){if(t=q(l),t===null)throw Error(d(188));return t!==l?null:l}for(var e=l,a=t;;){var u=e.return;if(u===null)break;var n=u.alternate;if(n===null){if(a=u.return,a!==null){e=a;continue}break}if(u.child===n.child){for(n=u.child;n;){if(n===e)return k(u),l;if(n===a)return k(u),t;n=n.sibling}throw Error(d(188))}if(e.return!==a.return)e=u,a=n;else{for(var c=!1,i=u.child;i;){if(i===e){c=!0,e=u,a=n;break}if(i===a){c=!0,a=u,e=n;break}i=i.sibling}if(!c){for(i=n.child;i;){if(i===e){c=!0,e=n,a=u;break}if(i===a){c=!0,a=n,e=u;break}i=i.sibling}if(!c)throw Error(d(189))}}if(e.alternate!==a)throw Error(d(190))}if(e.tag!==3)throw Error(d(188));return e.stateNode.current===e?l:t}function D(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l;for(l=l.child;l!==null;){if(t=D(l),t!==null)return t;l=l.sibling}return null}function S(l,t,e,a,u,n){for(;l!==null;){if((l.tag===5||l.tag===27||l.tag===6)&&e(l,a,u,n)||(l.tag!==22||l.memoizedState===null)&&(t||l.tag!==5&&l.tag!==27)&&S(l.child,t,e,a,u,n))return!0;l=l.sibling}return!1}function _(l){for(l=l.return;l!==null;){if(l.tag===3||l.tag===5||l.tag===27)return l;l=l.return}return null}function B(l){var t=!1;for(l=l.return;l!==null&&(l.tag===4&&(t=!0),!(l.tag===3||l.tag===5||l.tag===27));)l=l.return;return t}function G(l){var t=[null,null],e=_(l);return e===null||$(t,l,e.child,{foundSelf:!1}),t}function $(l,t,e,a){for(;e!==null;){if(e===t)a.foundSelf=!0;else if(e.tag===5||e.tag===27||e.tag===6){if(a.foundSelf)return l[1]=e,!0;l[0]=e}else if((e.tag!==22||e.memoizedState===null)&&$(l,t,e.child,a))return!0;e=e.sibling}return!1}function ul(l){switch(l.tag){case 5:case 27:case 6:return l.stateNode;case 3:return l.stateNode.containerInfo;default:throw Error(d(559))}}var Bl=null,nt=null;function ke(l,t,e){return l===e?!0:l===t?(Bl=l,!0):!1}function Bt(l,t,e){return l===e?(nt=l,!1):l===t?(nt!==null&&(Bl=l),!0):!1}function Gt(l){if(l===null)return null;do l=l===null?null:l.return;while(l&&l.tag!==5&&l.tag!==27&&l.tag!==3);return l||null}function ct(l,t,e){for(var a=0,u=l;u;u=e(u))a++;u=0;for(var n=t;n;n=e(n))u++;for(;0<a-u;)l=e(l),a--;for(;0<u-a;)t=e(t),u--;for(;a--;){if(l===t||t!==null&&l===t.alternate)return l;l=e(l),t=e(t)}return null}var I=Object.assign,sl=Symbol.for("react.element"),wt=Symbol.for("react.transitional.element"),St=Symbol.for("react.portal"),xt=Symbol.for("react.fragment"),it=Symbol.for("react.strict_mode"),ge=Symbol.for("react.profiler"),$e=Symbol.for("react.consumer"),Ul=Symbol.for("react.context"),M=Symbol.for("react.forward_ref"),Q=Symbol.for("react.suspense"),X=Symbol.for("react.suspense_list"),bl=Symbol.for("react.memo"),dl=Symbol.for("react.lazy"),jt=Symbol.for("react.activity"),le=Symbol.for("react.legacy_hidden"),We=Symbol.for("react.memo_cache_sentinel"),r=Symbol.for("react.view_transition"),E=Symbol.for("react.recoverable"),H=Symbol.iterator;function Y(l){return l===null||typeof l!="object"?null:(l=H&&l[H]||l["@@iterator"],typeof l=="function"?l:null)}var cl=Symbol.for("react.client.reference");function il(l){if(l==null)return null;if(typeof l=="function")return l.$$typeof===cl?null:l.displayName||l.name||null;if(typeof l=="string")return l;switch(l){case xt:return"Fragment";case ge:return"Profiler";case it:return"StrictMode";case Q:return"Suspense";case X:return"SuspenseList";case jt:return"Activity";case r:return"ViewTransition"}if(typeof l=="object")switch(l.$$typeof){case St:return"Portal";case Ul:return l.displayName||"Context";case $e:return(l._context.displayName||"Context")+".Consumer";case M:var t=l.render;return l=l.displayName,l||(l=t.displayName||t.name||"",l=l!==""?"ForwardRef("+l+")":"ForwardRef"),l;case bl:return t=l.displayName||null,t!==null?t:il(l.type)||"Memo";case dl:t=l._payload,l=l._init;try{return il(l(t))}catch{}}return null}var ol=Array.isArray,U=A.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,Z=N.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,te={pending:!1,data:null,method:null,action:nuLine truncated
at`)?" (<anonymous>)":-1<e.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Zc+l+js}var Vc=!1;function Lc(l,t){if(!l||Vc)return"";Vc=!0;var e=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(t){var T=function(){throw Error()};if(Object.defineProperty(T.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(T,[])}catch(z){var m=z}Reflect.construct(l,[],T)}else{try{T.call()}catch(z){m=z}T=!1;try{var g=Object.getOwnPropertyDescriptor(l.prototype,"props");Object.defineProperty(l.prototype,"props",{configurable:!0,set:function(){throw Error()}}),T=!0,new l}finally{T&&(g!==void 0?Object.defineProperty(l.prototype,"props",g):delete l.prototype.props)}}}else{try{throw Error()}catch(z){m=z}(T=l())&&typeof T.catch=="function"&&T.catch(function(){})}}catch(z){if(z&&m&&typeof z.stack=="string")return[z.stack,m.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var u=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");u&&u.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=a.DetermineComponentFrameRoot(),c=n[0],i=n[1];if(c&&i){var f=c.split(`
`),v=i.split(`
`);for(u=a=0;a<f.length&&!f[a].includes("DetermineComponentFrameRoot");)a++;for(;u<v.length&&!v[u].includes("DetermineComponentFrameRoot");)u++;if(a===f.length||u===v.length)for(a=f.length-1,u=v.length-1;1<=a&&0<=u&&f[a]!==v[u];)u--;for(;1<=a&&0<=u;a--,u--)if(f[a]!==v[u]){if(a!==1||u!==1)do if(a--,u--,0>u||f[a]!==v[u]){var b=`
`+f[a].replace(" at new "," at ");return l.displayName&&b.includes("<anonymous>")&&(b=b.replace("<anonymous>",l.displayName)),b}while(1<=a&&0<=u);break}}}finally{Vc=!1,Error.prepareStackTrace=e}return(e=l?l.displayName||l.name:"")?Se(e):""}function T0(l,t){switch(l.tag){case 26:case 27:case 5:return Se(l.type);case 16:return Se("Lazy");case 13:return l.child!==t&&t!==null?Se("Suspense Fallback"):Se("Suspense");case 19:return Se("SuspenseList");case 0:case 15:return Lc(l.type,!1);case 11:return Lc(l.type.render,!1);case 1:return Lc(l.type,!0);case 31:return Se("Activity");case 30:return Se("ViewTransition");default:return""}}function Cs(l){try{var t="",e=null;do t+=T0(l,e),e=l,l=l.return;while(l);return t}catch(a){return`
Error generating stack: `+a.message+`
`+a.stack}}var Kc=Object.prototype.hasOwnProperty,Jc=x.unstable_scheduleCallback,kc=x.unstable_cancelCallback,N0=x.unstable_shouldYield,E0=x.unstable_requestPaint,ft=x.unstable_now,_0=x.unstable_getCurrentPriorityLevel,Ds=x.unstable_ImmediatePriority,Us=x.unstable_UserBlockingPriority,rn=x.unstable_NormalPriority,z0=x.unstable_LowPriority,Rs=x.unstable_IdlePriority,O0=x.log,A0=x.unstable_setDisableYieldValue,du=null,st=null;function xe(l){if(typeof O0=="function"&&A0(l),st&&typeof st.setStrictMode=="function")try{st.setStrictMode(du,l)}catch{}}var ot=Math.clz32?Math.clz32:C0,M0=Math.log,j0=Math.LN2;function C0(l){return l>>>=0,l===0?32:31-(M0(l)/j0|0)|0}var dn=256,mn=262144,hn=4194304;function Fe(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&-l;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function vn(l,t,e){var a=l.pendingLanes;if(a===0)return 0;var u=0,n=l.suspendedLanes,c=l.pingedLanes;l=l.warmLanes;var i=a&134217727;return i!==0?(a=i&~n,a!==0?u=Fe(a):(c&=i,c!==0?u=Fe(c):e||(e=i&~l,e!==0&&(u=Fe(e))))):(i=a&~n,i!==0?u=Fe(i):c!==0?u=Fe(c):e||(e=a&~l,e!==0&&(u=Fe(e)))),u===0?0:t!==0&&t!==u&&(t&n)===0&&(n=u&-u,e=t&-t,n>=e||n===32&&(e&4194048)!==0)?t:u}function mu(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function Hs(l,t){(t&8)!==0&&(t|=t&32);var e=l.entangledLanes;if(e!==0)for(l=l.entanglements,e&=t;0<e;){var a=31-ot(e),u=1<<a;t|=l[a],e&=~u}return t}function D0(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Ys(){var l=hn;return hn<<=1,(hn&62914560)===0&&(hn=4194304),l}function $c(l){for(var t=[],e=0;31>e;e++)t.push(l);return t}function hu(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function U0(l,t,e,a,u,n){var c=l.pendingLanes;l.pendingLanes=e,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=e,l.entangledLanes&=e,l.errorRecoveryDisabledLanes&=e,l.shellSuspendCounter=0;var i=l.entanglements,f=l.expirationTimes,v=l.hiddenUpdates;for(e=c&~e;0<e;){var b=31-ot(e),T=1<<b;i[b]=0,f[b]=-1;var m=v[b];if(m!==null)for(v[b]=null,b=0;b<m.length;b++){var g=m[b];g!==null&&(g.lane&=-536870913)}e&=~T}a!==0&&qs(l,a,0),n!==0&&u===0&&l.tag!==0&&(l.suspendedLanes|=n&~(c&~t))}function qs(l,t,e){l.pendingLanes|=t,l.suspendedLanes&=~t;var a=31-ot(t);l.entangledLanes|=t,l.entanglements[a]=l.entanglements[a]|1073741824|e&261930}function Bs(l,t){var e=l.entangledLanes|=t;for(l=l.entanglements;e;){var a=31-ot(e),u=1<<a;u&t|l[a]&t&&(l[a]|=t),e&=~u}}function Gs(l,t){var e=t&-t;return e=(e&42)!==0?1:Wc(e),(e&(l.suspendedLanes|t))!==0?0:e}function Wc(l){switch(l){case 2:l=1;break;case 8:l=4;break;case 32:l=16;break;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:case 4194304:case 8388608:case 16777216:case 33554432:l=128;break;case 268435456:l=134217728;break;default:l=0}return l}function Fc(l){return l&=-l,2<l?8<l?(l&134217727)!==0?32:268435456:8:2}function ws(){var l=Z.p;return l!==0?l:(l=window.event,l===void 0?32:km(l.type))}function Qs(l,t){var e=Z.p;try{return Z.p=l,t()}finally{Z.p=e}}var ee=Math.random().toString(36).slice(2),wl="__reactFiber$"+ee,Pl="__reactProps$"+ee,Ta="__reactContainer$"+ee,Xs="__reactEvents$"+ee,R0="__reactListeners$"+ee,H0="__reactHandles$"+ee,Zs="__reactResources$"+ee,vu="__reactMarker$"+ee,yn="__reactLoad$"+ee;function gn(l){delete l[wl],delete l[Pl],delete l[R0],delete l[H0]}function Ie(l){var t;if(t=l[wl])return t;for(var e=l.parentNode;e;){if(t=e[Ta]||e[wl]){if(e=t.alternate,t.child!==null||e!==null&&e.child!==null)for(l=Am(l);l!==null;){if(e=l[wl])return e;l=Am(l)}return t}l=e,e=l.parentNode}return null}function Na(l){if(l=l[wl]||l[Ta]){var t=l.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return l}return null}function yu(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l.stateNode;throw Error(d(33))}function Ea(l){var t=l[Zs];return t||(t=l[Zs]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function Rl(l){l[vu]=!0}function Vs(l){l[yn]=void 0}var Ls=new Set,Ks={};function Pe(l,t){_a(l,t),_a(l+"Capture",t)}function Line truncated
`).replace(Tv,"")}function fm(l,t){return t=im(t),im(l)===t}function yl(l,t,e,a,u,n){switch(e){case"children":if(typeof a=="string")t==="body"||t==="textarea"&&a===""||Oa(l,a);else if(typeof a=="number"||typeof a=="bigint")t!=="body"&&Oa(l,""+a);else return;break;case"className":Sn(l,"class",a);break;case"tabIndex":Sn(l,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":Sn(l,e,a);break;case"style":eo(l,a,n);return;case"data":if(t!=="object"){Sn(l,"data",a);break}case"src":case"href":if(a===""&&(t!=="a"||e!=="href")){l.removeAttribute(e);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){l.removeAttribute(e);break}a=xn(a),l.setAttribute(e,a);break;case"action":case"formAction":if(typeof a=="function"){l.setAttribute(e,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof n=="function"&&(e==="formAction"?(t!=="input"&&yl(l,t,"name",u.name,u,null),yl(l,t,"formEncType",u.formEncType,u,null),yl(l,t,"formMethod",u.formMethod,u,null),yl(l,t,"formTarget",u.formTarget,u,null)):(yl(l,t,"encType",u.encType,u,null),yl(l,t,"method",u.method,u,null),yl(l,t,"target",u.target,u,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){l.removeAttribute(e);break}a=xn(a),l.setAttribute(e,a);break;case"onClick":a!=null&&(l.onclick=Zt);return;case"onScroll":a!=null&&ll("scroll",l);return;case"onScrollEnd":a!=null&&ll("scrollend",l);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(d(61));if(e=a.__html,e!=null){if(u.children!=null)throw Error(d(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"multiple":l.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":l.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){l.removeAttribute("xlink:href");break}e=xn(a),l.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",e);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,a):l.removeAttribute(e);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,""):l.removeAttribute(e);break;case"capture":case"download":a===!0?l.setAttribute(e,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?l.setAttribute(e,a):l.removeAttribute(e);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?l.setAttribute(e,a):l.removeAttribute(e);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?l.removeAttribute(e):l.setAttribute(e,a);break;case"popover":ll("beforetoggle",l),ll("toggle",l),bn(l,"popover",a);break;case"xlinkActuate":ae(l,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":ae(l,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":ae(l,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":ae(l,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":ae(l,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":ae(l,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":ae(l,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":ae(l,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":ae(l,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":bn(l,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<e.length)||e[0]!=="o"&&e[0]!=="O"||e[1]!=="n"&&e[1]!=="N")e=Q0.get(e)||e,bn(l,e,a);else return}fl=!0}function ls(l,t,e,a,u,n){switch(e){case"style":eo(l,a,n);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(d(61));if(e=a.__html,e!=null){if(u.children!=null)throw Error(d(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"children":if(typeof a=="string")Oa(l,a);else if(typeof a=="number"||typeof a=="bigint")Oa(l,""+a);else return;break;case"onScroll":a!=null&&ll("scroll",l);return;case"onScrollEnd":a!=null&&ll("scro Line truncated
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const B1=x=>x.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),y0=(...x)=>x.filter((A,N,d)=>!!A&&A.trim()!==""&&d.indexOf(A)===N).join(" ").trim();/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/var G1={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const w1=Jl.forwardRef(({color:x="currentColor",size:A=24,strokeWidth:N=2,absoluteStrokeWidth:d,className:O="",children:q,iconNode:al,...W},k)=>Jl.createElement("svg",{ref:k,...G1,width:A,height:A,stroke:x,strokeWidth:d?Number(N)*24/Number(A):N,className:y0("lucide",O),...W},[...al.map(([F,D])=>Jl.createElement(F,D)),...Array.isArray(q)?q:[q]]));/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const qt=(x,A)=>{const N=Jl.forwardRef(({className:d,...O},q)=>Jl.createElement(w1,{ref:q,iconNode:A,className:y0(`lucide-${B1(x)}`,d),...O}));return N.displayName=`${x}`,N};/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const Q1=[["path",{d:"M12 7v14",key:"1akyts"}],["path",{d:"M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",key:"ruj8y"}]],g0=qt("BookOpen",Q1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const X1=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],Z1=qt("Check",X1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const V1=[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]],L1=qt("Layers",V1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const K1=[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]],J1=qt("LayoutGrid",K1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const k1=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]],$1=qt("Moon",k1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const W1=[["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["path",{d:"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z",key:"12rzf8"}]],F1=qt("Palette",W1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const I1=[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M9 21V9",key:"1oto5p"}]],P1=qt("PanelsTopLeft",I1);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ly=[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]],b0=qt("Search",ly);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ty=[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]],ey=qt("Sparkles",ty);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ay=[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]],uy=qt("Sun",ay);/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const ny=[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]],S0=qt("Terminal",ny),cy=[{id:"auto",label:"智能路由"},{id:"edge443",label:"443 免端口"},{id:"home10443",label:"10443 家宽"},{id:"lan",label:"LAN 局域网"}],iy=()=>{const{theme:x,resolvedTheme:A,routeMode:N,viewMode:d,telemetry:O,setTheme:q,toggleTheme:al,setRouteMode:W,setViewMode:k,setShowAgyModal:F,setShowCmdPalette:D}=Je();return s.jsxs(s.Fragment,{children:[s.jsx("header",{className:"border-b hl-border sticky top-0 z-30 backdrop-blur-md",style:{backgroundColor:"color-mix(in srgb, var(--bg-card) 88%, transparent)"},children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-14 flex items-center justify-between gap-4",children:[s.jsxs("div",{className:"flex items-center gap-4 shrink-0",children:[s.jsxs("div",{onClick:()=>k("dashboard"),className:"flex items-center gap-2.5 cursor-pointer",children:[s.jsx("img",{src:"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/apple.png",alt:"Mac mini",className:"w-6 h-6 rounded"}),s.jsx("span",{className:"font-semibold text-sm tracking-tight",children:"HomeLab"}),s.jsx("span",{className:"w-2 h-2 rounded-full",style:{backgroundColor:"var(--success)"},title:"全屋 4 节点在线"})]}),s.jsx("div",{className:"h-4 w-[1px] hl-muted"}),s.jsxs("nav",{className:"flex items-center gap-1 text-xs",children:[s.jsxs("button",{onClick:()=>k("dashboard"),className:"px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer",style:d==="dashboard"?{backgroundColor:"var(--bg-muted)",color:"var(--text-main)"}:{color:"var(--text-muted)"},children:[s.jsx(J1,{className:"w-3.5 h-3.5"}),s.jsx("span",{children:"中控台"})]}),s.jsxs("button",{onClick:()=>k("storybook"),className:"px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer",style:d==="storybook"?{backgroundColor:"var(--bg-muted)",color:"var(--text-main)"}:{color:"var(--text-muted)"},children:[s.jsx(g0,{className:"w-3.5 h-3.5"}),s.jsx("span",{children:"Storybook 设计规范"})]})]})]}),s.jsxs("div",{className:"flex items-center gap-2.5 shrink-0 ml-auto",children:[s.jsxs("button",{onClick:()=>D(!0),className:"hl-btn-outline px-3 py-1.5 text-xs flex items-center gap-3 cursor-pointer",title:"按 Cmd+K 唤起全局命令面板",children:[s.jsxs("span",{className:"flex items-center gap-1.5",style:{color:"var(--text-muted)"},children:[s.jsx(b0,{className:"w-3.5 h-3.5"}),s.jsx("span",{className:"hidden sm:inline",children:"搜索服务或指令..."})]}),s.jsx("kbd",{className:"px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-muted",style:{color:"var(--text-muted)"},children:"⌘K"})]}),s.jsxs("button",{onClick:()=>F(!0),className:"hl-btn-primary px-3 py-1.5 text-xs flex items-center gap-1.5 cursor-pointer",children:[s.jsx(S0,{className:"w-3.5 h-3.5"}),s.jsx("span",{children:"AGY 终端"})]}),s.jsxs("div",{className:"flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs",children:[s.jsx("button",{onClick:al,className:"px-2.5 py-1 rounded-md font-medium transition-all flex items-center gap-1.5 cursor-pointer",style:{backgroundColor:"var(--bg-card)",color:"var(--text-main)",boxShadow:"0 1px 2px rgba(0,0,0,0.08)"},title:"点击切换深色 / 浅色主题(默认跟随系统)",children:A==="dark"?s.jsxs(s.Fragment,{children:[s.jsx($1,{className:"w-3.5 h-3.5"}),s.jsx("span",{children:"深色"})]}):s.jsxs(s.Fragment,{children:[s.jsx(uy,{className:"w-3.5 h-3.5"}),s.jsx("span",{children:"浅色"})]})}),x!=="system"&&s.jsx("button",{onClick:()=>q("system"),className:"px-2 py-1 rounded-md text-[11px] transition-all cursor-pointer",style:{color:"var(--text-muted)"},title:"恢复跟随设备系统深浅色自动切换",children:"自动"})]})]})]})}),d==="dashboard"&&s.jsx("div",{className:"border-b hl-border",style:{backgroundColor:"var(--bg-card)"},children:s.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2.5 flex flex-wrap items-center justify-between gap-3",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2 text-xs font-mono-num",children:[s.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",children:[s.jsx("span",{style:{color:"var(--text-muted)"},children:"M6 CPU"}),s.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:s.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,O.mini.cpu_pct)}%`}})}),s.jsxs("span",{className:"font-medium",children:[O.mini.cpu_pct,"%"]})]}),s.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",children:[s.jsx("span",{style:{color:"var(--text-muted)"},children:"RAM"}),s.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:s.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,O.mini.mem_pct)}%`}})}),s.jsxs("span",{className:"font-medium",children:[O.mini.mem_used_gb,"G"]})]}),s.jsxs("div",{className:"hl-pill px-2.5 pLine truncated
+1
View File
@@ -0,0 +1 @@
/*! tailwindcss v4.3.5 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:block))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-md:28rem;--container-xl:36rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--blur-sm:8px;--blur-md:12px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-Line truncated
+2 -2
View File
@@ -11,8 +11,8 @@
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<script type="module" crossorigin src="/assets/index-D9ftMUDG.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B_rkFolS.css">
<script type="module" crossorigin src="/assets/index-CC6K5dbV.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-R7AnvXyQ.css">
</head>
<body class="min-h-screen">
<div id="root"></div>
+148 -7
View File
@@ -1,18 +1,26 @@
#!/usr/bin/env python3
# ==============================================================================
# HomeLab All-in-One 专属中控台后端服务 (Port 3002)
# 提供 Apple/Shadcn 风格单页应用与全屋设备实时遥测 API (/api/telemetry, /api/action)
# HomeLab All-in-One 专属中控台后端服务 (Port 3002 & 3004)
# 提供:
# 1. Apple/Shadcn 风格单页应用静态资源
# 2. 全屋设备实时遥测 API (/api/telemetry, /api/action/*)
# 3. 统一自定义 SSO 认证网关 (/api/auth/verify, /api/auth/login, /api/auth/status)
# 彻底替代浏览器原生丑陋的 HTTP Basic Auth 弹窗!
# ==============================================================================
import hashlib
import hmac
import json
import mimetypes
import os
import re
import socket
import subprocess
import threading
import time
import urllib.parse
import urllib.request
import mimetypes
from concurrent.futures import ThreadPoolExecutor
from http.cookies import SimpleCookie
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
@@ -21,10 +29,40 @@ BASE_DIR = Path(__file__).resolve().parent
DIST_DIR = BASE_DIR / "dist"
INDEX_FILE = BASE_DIR / "index.html"
# Unified SSO Credentials & HMAC Secret
AUTH_USER = os.environ.get("HOMELAB_USER", "ixvii0iv")
AUTH_PASS = os.environ.get("HOMELAB_PASS", "k10y15ZXC.")
AUTH_SECRET = os.environ.get("HOMELAB_SECRET", "hl-m6-macmini-sso-secret-2026-961504xyz").encode("utf-8")
COOKIE_NAME = "hl_sso"
COOKIE_MAX_AGE = 30 * 86400 # 30 Days
_cache = {"data": None, "ts": 0.0}
_cache_lock = threading.Lock()
def create_sso_token(username: str) -> str:
exp = int(time.time()) + COOKIE_MAX_AGE
payload = f"{username}:{exp}"
sig = hmac.new(AUTH_SECRET, payload.encode("utf-8"), hashlib.sha256).hexdigest()
return f"{payload}:{sig}"
def verify_sso_token(token: str) -> tuple[bool, str]:
if not token or token.count(":") != 2:
return False, ""
try:
username, exp_str, sig = token.split(":", 2)
if int(exp_str) < int(time.time()):
return False, ""
payload = f"{username}:{exp_str}"
expected = hmac.new(AUTH_SECRET, payload.encode("utf-8"), hashlib.sha256).hexdigest()
if hmac.compare_digest(sig, expected):
return True, username
except Exception:
pass
return False, ""
def run_cmd(cmd: str, timeout: float = 2.0) -> str:
try:
env = os.environ.copy()
@@ -50,7 +88,6 @@ def check_tcp(host: str, port: int, timeout: float = 0.8) -> tuple[bool, int]:
def get_mac_mini_stats() -> dict:
# 1. CPU Load & Core count
load_str = run_cmd("sysctl -n vm.loadavg")
# Format: "{ 1.42 1.18 1.05 }"
loads = [float(x) for x in re.findall(r"[0-9]+\.[0-9]+", load_str)] or [0.0, 0.0, 0.0]
cores = int(run_cmd("sysctl -n hw.ncpu") or "12")
cpu_pct = min(100, round((loads[0] / max(1, cores)) * 100, 1))
@@ -273,21 +310,80 @@ class PortalHandler(BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
pass
def _send_json(self, obj: dict, status: int = 200):
def _get_cookie_token(self) -> str:
raw_cookie = self.headers.get("Cookie", "")
if not raw_cookie:
return ""
c = SimpleCookie()
try:
c.load(raw_cookie)
if COOKIE_NAME in c:
return c[COOKIE_NAME].value
except Exception:
pass
# Fallback regex parse
m = re.search(rf"{COOKIE_NAME}=([^;\s]+)", raw_cookie)
return m.group(1) if m else ""
def _is_authenticated(self) -> tuple[bool, str]:
token = self._get_cookie_token()
return verify_sso_token(token)
def _send_json(self, obj: dict, status: int = 200, extra_headers=None):
body = json.dumps(obj, ensure_ascii=False).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Cache-Control", "no-store")
if extra_headers:
for k, v in extra_headers:
self.send_header(k, v)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self):
# 1. Caddy forward_auth verification endpoint (/api/auth/verify)
if self.path.startswith("/api/auth/verify"):
ok, username = self._is_authenticated()
if ok:
self.send_response(200)
self.send_header("X-Auth-User", username)
self.send_header("Content-Length", "2")
self.end_headers()
self.wfile.write(b"OK")
return
# Not authenticated -> Redirect to our custom-styled HomeLab Console Login Modal!
fwd_proto = self.headers.get("X-Forwarded-Proto", "https")
fwd_host = self.headers.get("X-Forwarded-Host") or self.headers.get("Host", "agy.961504.xyz")
fwd_uri = self.headers.get("X-Forwarded-Uri", "/")
target_url = f"{fwd_proto}://{fwd_host}{fwd_uri}"
encoded_target = urllib.parse.quote(target_url, safe="")
if ".home.961504.xyz" in fwd_host or ":10443" in fwd_host:
login_portal = f"https://home.961504.xyz:10443/?auth_redirect={encoded_target}"
else:
login_portal = f"https://nav.961504.xyz/?auth_redirect={encoded_target}"
self.send_response(302)
self.send_header("Location", login_portal)
self.send_header("Cache-Control", "no-store")
self.send_header("Content-Length", "0")
self.end_headers()
return
# 2. Check current SSO status (/api/auth/status)
if self.path.startswith("/api/auth/status"):
ok, username = self._is_authenticated()
self._send_json({"authenticated": ok, "username": username if ok else None})
return
# 3. Telemetry endpoint (/api/telemetry)
if self.path.startswith("/api/telemetry"):
self._send_json(collect_telemetry())
return
# Serve static assets from Vite build output (dist/) if available
# 4. Serve static assets from Vite build output (dist/) if available
clean_path = self.path.split("?", 1)[0].lstrip("/")
if clean_path and DIST_DIR.exists():
asset_file = (DIST_DIR / clean_path).resolve()
@@ -302,7 +398,7 @@ class PortalHandler(BaseHTTPRequestHandler):
self.wfile.write(body)
return
# Serve SPA index.html from dist/index.html (React+Vite build) or fallback index.html
# 5. Serve SPA index.html from dist/index.html
spa_index = (DIST_DIR / "index.html") if (DIST_DIR / "index.html").exists() else INDEX_FILE
if spa_index.exists():
body = spa_index.read_bytes()
@@ -316,6 +412,51 @@ class PortalHandler(BaseHTTPRequestHandler):
self.send_error(404, "index.html not found")
def do_POST(self):
# 1. Unified Custom Login Endpoint (/api/auth/login)
if self.path == "/api/auth/login":
length = int(self.headers.get("Content-Length", "0"))
raw = self.rfile.read(length).decode("utf-8") if length > 0 else "{}"
try:
body = json.loads(raw)
except Exception:
body = {}
username = str(body.get("username", "")).strip()
password = str(body.get("password", ""))
if username == AUTH_USER and password == AUTH_PASS:
token = create_sso_token(username)
host = self.headers.get("Host", "")
headers = []
# Set cross-subdomain cookie for *.961504.xyz (works for both nav.961504.xyz, agy.961504.xyz, *.home.961504.xyz)
if "961504.xyz" in host:
headers.append(
(
"Set-Cookie",
f"{COOKIE_NAME}={token}; Domain=.961504.xyz; Path=/; Max-Age={COOKIE_MAX_AGE}; HttpOnly; Secure; SameSite=Lax",
)
)
else:
headers.append(
(
"Set-Cookie",
f"{COOKIE_NAME}={token}; Path=/; Max-Age={COOKIE_MAX_AGE}; HttpOnly; SameSite=Lax",
)
)
self._send_json({"ok": True, "username": username}, status=200, extra_headers=headers)
else:
self._send_json({"ok": False, "message": "用户名或访问密码不正确"}, status=401)
return
# 2. Logout Endpoint (/api/auth/logout)
if self.path == "/api/auth/logout":
headers = [
("Set-Cookie", f"{COOKIE_NAME}=; Domain=.961504.xyz; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax"),
("Set-Cookie", f"{COOKIE_NAME}=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax"),
]
self._send_json({"ok": True}, status=200, extra_headers=headers)
return
# 3. Operations Actions
if self.path == "/api/action/backup_nas":
out = run_cmd("/Users/ixvii0iv/homelab/scripts/backup-to-nas.sh", timeout=15.0)
with _cache_lock:
+26 -2
View File
@@ -1,10 +1,11 @@
import React, { useEffect, useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { usePortalStore } from './store/usePortalStore';
import { TopRibbon } from './components/TopRibbon';
import { ServiceCard } from './components/ServiceCard';
import { SshShelf } from './components/SshShelf';
import { CommandPalette } from './components/CommandPalette';
import { AgyTerminalModal } from './components/AgyTerminalModal';
import { AuthGateCard } from './components/AuthGateModal';
import { StorybookWorkbench } from './components/StorybookWorkbench';
import { LAYOUT_PRESETS } from './design-system/tokens';
import type { ServiceGroup } from './types/telemetry';
@@ -21,6 +22,12 @@ export const App: React.FC = () => {
routeMode,
} = usePortalStore();
const [authRedirect, setAuthRedirect] = useState<string | null>(() => {
if (typeof window === 'undefined') return null;
const params = new URLSearchParams(window.location.search);
return params.get('auth_redirect');
});
useEffect(() => {
fetchTelemetry();
const timer = setInterval(fetchTelemetry, 3000);
@@ -45,7 +52,8 @@ export const App: React.FC = () => {
return '443 免端口 HTTPS 直达 (*.961504.xyz)';
}, [getEffectiveMode, routeMode]);
const gridClass = LAYOUT_PRESETS[layoutPreset]?.gridClass || LAYOUT_PRESETS['homepage-3col'].gridClass;
const gridClass =
LAYOUT_PRESETS[layoutPreset]?.gridClass || LAYOUT_PRESETS['homepage-3col'].gridClass;
const serviceGroups = useMemo<ServiceGroup[]>(() => {
const t = telemetry;
@@ -235,6 +243,22 @@ export const App: React.FC = () => {
{/* 5. Embedded AGY Cloud Terminal Modal */}
<AgyTerminalModal />
{/* 6. Custom SSO Auth Gate Modal (Replaces Browser Native Basic Auth Popup!) */}
{authRedirect && (
<div className="fixed inset-0 z-50 bg-black/75 backdrop-blur-sm flex items-center justify-center p-4">
<AuthGateCard
targetUrl={authRedirect}
onSuccess={() => {
window.location.href = authRedirect;
}}
onCancel={() => {
setAuthRedirect(null);
window.history.replaceState({}, '', '/');
}}
/>
</div>
)}
</div>
);
};
@@ -1,10 +1,11 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { usePortalStore } from '../store/usePortalStore';
import { AuthGateCard } from './AuthGateModal';
export const AgyTerminalModal: React.FC = () => {
const { showAgyModal, setShowAgyModal, getEffectiveMode } = usePortalStore();
if (!showAgyModal) return null;
const [authChecked, setAuthChecked] = useState(false);
const [authenticated, setAuthenticated] = useState(false);
const mode = getEffectiveMode();
const url =
@@ -14,6 +15,28 @@ export const AgyTerminalModal: React.FC = () => {
? 'https://agy.home.961504.xyz:10443'
: 'https://agy.961504.xyz';
useEffect(() => {
if (!showAgyModal) return;
if (mode === 'lan') {
setAuthenticated(true);
setAuthChecked(true);
return;
}
setAuthChecked(false);
fetch('/api/auth/status', { credentials: 'include' })
.then((r) => r.json())
.then((d) => {
setAuthenticated(Boolean(d.authenticated));
setAuthChecked(true);
})
.catch(() => {
setAuthenticated(false);
setAuthChecked(true);
});
}, [showAgyModal, mode]);
if (!showAgyModal) return null;
return (
<div className="fixed inset-0 z-50 bg-black/75 backdrop-blur-sm flex items-center justify-center p-3 sm:p-6">
<div className="w-full h-full max-w-6xl hl-card flex flex-col overflow-hidden shadow-2xl">
@@ -45,7 +68,22 @@ export const AgyTerminalModal: React.FC = () => {
</button>
</div>
</div>
{!authChecked ? (
<div className="flex-1 flex items-center justify-center text-xs font-mono-num" style={{ color: 'var(--text-muted)' }}>
正在校验 HomeLab SSO 通行证...
</div>
) : !authenticated ? (
<div className="flex-1 flex items-center justify-center p-4" style={{ backgroundColor: 'var(--bg-page)' }}>
<AuthGateCard
targetUrl={url}
onSuccess={() => setAuthenticated(true)}
onCancel={() => setShowAgyModal(false)}
/>
</div>
) : (
<iframe src={url} title="AGY Cloud Workbench" className="w-full flex-1 bg-black border-0" />
)}
</div>
</div>
);
@@ -0,0 +1,200 @@
import React, { useState } from 'react';
import { Check, Eye, EyeOff, Lock, ShieldCheck, Terminal, User } from 'lucide-react';
interface AuthGateCardProps {
targetUrl?: string;
onSuccess?: () => void;
onCancel?: () => void;
embedded?: boolean;
}
export const AuthGateCard: React.FC<AuthGateCardProps> = ({
targetUrl = 'https://agy.961504.xyz',
onSuccess,
onCancel,
embedded = false,
}) => {
const [username, setUsername] = useState('ixvii0iv');
const [password, setPassword] = useState('');
const [showPass, setShowPass] = useState(false);
const [rememberMe, setRememberMe] = useState(true);
const [loading, setLoading] = useState(false);
const [errorMsg, setErrorMsg] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!username.trim() || !password) {
setErrorMsg('请输入用户名与访问密钥');
return;
}
setLoading(true);
setErrorMsg(null);
try {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username: username.trim(), password, remember: rememberMe }),
});
const data = await res.json();
if (res.ok && data.ok) {
onSuccess?.();
} else {
setErrorMsg(data.message || '认证未通过,请检查密码');
}
} catch {
setErrorMsg('网络请求异常,请稍后重试');
} finally {
setLoading(false);
}
};
return (
<div
className={`w-full max-w-md hl-card p-6 space-y-5 ${
embedded ? '' : 'shadow-2xl'
}`}
>
{/* Header */}
<div className="flex items-start justify-between gap-3 border-b hl-border pb-4">
<div className="flex items-center gap-3">
<div
className="w-10 h-10 rounded-xl flex items-center justify-center border hl-border shrink-0"
style={{ backgroundColor: 'var(--accent-soft)', color: 'var(--accent)' }}
>
<ShieldCheck className="w-5 h-5" />
</div>
<div>
<div className="flex items-center gap-2">
<h3 className="text-sm font-bold tracking-tight">HomeLab 统一安全通行证</h3>
<span
className="text-[10px] font-mono-num px-1.5 py-0.5 rounded font-semibold"
style={{ backgroundColor: 'var(--success-soft)', color: 'var(--success)' }}
>
SSO 2.0
</span>
</div>
<p className="text-xs font-mono-num mt-0.5 truncate max-w-[260px]" style={{ color: 'var(--text-muted)' }}>
{targetUrl}
</p>
</div>
</div>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-1.5">
<label className="block text-xs font-semibold" style={{ color: 'var(--text-muted)' }}>
管理员账号 (Username)
</label>
<div className="hl-input-group">
<User className="w-4 h-4 shrink-0" style={{ color: 'var(--text-subtle)' }} />
<input
type="text"
autoComplete="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="输入管理员账号 (如 ixvii0iv)"
className="font-mono-num"
/>
</div>
</div>
<div className="space-y-1.5">
<div className="flex items-center justify-between">
<label className="block text-xs font-semibold" style={{ color: 'var(--text-muted)' }}>
访问通行密钥 (Access Key)
</label>
<span className="text-[10px] font-mono-num" style={{ color: 'var(--text-subtle)' }}>
与 Mac mini 密码一致
</span>
</div>
<div className="hl-input-group">
<Lock className="w-4 h-4 shrink-0" style={{ color: 'var(--text-subtle)' }} />
<input
type={showPass ? 'text' : 'password'}
autoComplete="current-password"
autoFocus
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="输入访问密钥..."
className="font-mono-num"
/>
<button
type="button"
onClick={() => setShowPass(!showPass)}
className="p-1 rounded hover:opacity-80 cursor-pointer"
style={{ color: 'var(--text-muted)' }}
title={showPass ? '隐藏密码' : '显示密码'}
>
{showPass ? <EyeOff className="w-3.5 h-3.5" /> : <Eye className="w-3.5 h-3.5" />}
</button>
</div>
</div>
{/* Custom Checkbox */}
<div className="flex items-center justify-between pt-0.5">
<button
type="button"
onClick={() => setRememberMe(!rememberMe)}
className="flex items-center gap-2 text-xs cursor-pointer select-none"
style={{ color: 'var(--text-muted)' }}
>
<span
className="w-4 h-4 rounded flex items-center justify-center border transition-colors"
style={
rememberMe
? {
backgroundColor: 'var(--accent)',
borderColor: 'var(--accent)',
color: '#ffffff',
}
: {
backgroundColor: 'var(--bg-muted)',
borderColor: 'var(--border-color)',
}
}
>
{rememberMe && <Check className="w-3 h-3" />}
</span>
<span>在此设备保持 30 天免密通行 (*.961504.xyz)</span>
</button>
</div>
{errorMsg && (
<div
className="px-3 py-2 rounded-lg text-xs font-medium border"
style={{
backgroundColor: 'rgba(239, 68, 68, 0.1)',
borderColor: 'rgba(239, 68, 68, 0.3)',
color: '#ef4444',
}}
>
{errorMsg}
</div>
)}
{/* Actions */}
<div className="flex items-center justify-end gap-2.5 pt-2">
{onCancel && (
<button
type="button"
onClick={onCancel}
className="hl-btn-outline px-4 py-2 text-xs font-medium cursor-pointer"
>
取消
</button>
)}
<button
type="submit"
disabled={loading}
className="hl-btn-primary px-5 py-2 text-xs font-semibold flex items-center gap-2 cursor-pointer"
>
<Terminal className="w-3.5 h-3.5" />
<span>{loading ? '正在验证签名...' : '解锁并进入工作台'}</span>
</button>
</div>
</form>
</div>
);
};
@@ -1,14 +1,30 @@
import React, { useState } from 'react';
import { BookOpen, Check, Layers, Layout, Palette, Sparkles, Terminal } from 'lucide-react';
import {
BookOpen,
Check,
KeyRound,
Layers,
Layout,
Palette,
Search,
Sparkles,
Terminal,
} from 'lucide-react';
import { LAYOUT_PRESETS, THEME_PALETTES, type LayoutPresetId } from '../design-system/tokens';
import { usePortalStore } from '../store/usePortalStore';
import { AuthGateCard } from './AuthGateModal';
import { ServiceCard } from './ServiceCard';
import { SshShelf } from './SshShelf';
type StoryId = 'layout-architecture' | 'theme-tokens' | 'service-card' | 'ssh-shelf';
type StoryId =
| 'layout-architecture'
| 'theme-tokens'
| 'form-inputs'
| 'service-card'
| 'ssh-shelf';
export const StorybookWorkbench: React.FC = () => {
const [activeStory, setActiveStory] = useState<StoryId>('layout-architecture');
const [activeStory, setActiveStory] = useState<StoryId>('form-inputs');
const {
theme,
resolvedTheme,
@@ -17,12 +33,17 @@ export const StorybookWorkbench: React.FC = () => {
setLayoutPreset,
setViewMode,
telemetry,
showToast,
} = usePortalStore();
// Interactive Playground Props for ServiceCard story
const [demoOnline, setDemoOnline] = useState(true);
const [demoAction, setDemoAction] = useState<'drawer' | 'backup' | undefined>('drawer');
// Interactive Playground Props for Form Inputs story
const [demoSearch, setDemoSearch] = useState('');
const [demoHost, setDemoHost] = useState('192.168.31.164');
const stories: Array<{ id: StoryId; group: string; title: string; icon: React.ReactNode }> = [
{
id: 'layout-architecture',
@@ -36,16 +57,22 @@ export const StorybookWorkbench: React.FC = () => {
title: '02. 双主题色板与自动切换规范',
icon: <Palette className="w-3.5 h-3.5" />,
},
{
id: 'form-inputs',
group: 'COMPONENTS / 核心组件',
title: '03. Input & AuthGate 输入框与登录弹窗',
icon: <KeyRound className="w-3.5 h-3.5" />,
},
{
id: 'service-card',
group: 'COMPONENTS / 核心组件',
title: '03. ServiceCard 内联遥测卡片',
title: '04. ServiceCard 内联遥测卡片',
icon: <Layers className="w-3.5 h-3.5" />,
},
{
id: 'ssh-shelf',
group: 'COMPONENTS / 核心组件',
title: '04. SshShelf 快捷指令栏',
title: '05. SshShelf 快捷指令栏',
icon: <Terminal className="w-3.5 h-3.5" />,
},
];
@@ -60,7 +87,7 @@ export const StorybookWorkbench: React.FC = () => {
<BookOpen className="w-4 h-4" style={{ color: 'var(--accent)' }} />
<span className="text-xs font-bold uppercase tracking-wider">Storybook 组件库</span>
</div>
<span className="text-[10px] font-mono-num px-1.5 py-0.5 rounded hl-muted">v3.1</span>
<span className="text-[10px] font-mono-num px-1.5 py-0.5 rounded hl-muted">v3.2</span>
</div>
<div className="space-y-1">
@@ -295,7 +322,93 @@ export const StorybookWorkbench: React.FC = () => {
</div>
)}
{/* STORY 3: ServiceCard Interactive Playground */}
{/* STORY 3: Custom Form Inputs & AuthGate Card */}
{activeStory === 'form-inputs' && (
<div className="hl-card p-6 space-y-6">
<div className="border-b hl-border pb-4 flex items-center justify-between">
<div>
<span
className="text-[11px] font-mono-num uppercase tracking-wider"
style={{ color: 'var(--accent)' }}
>
Components / Form Controls & SSO Auth Gate
</span>
<h2 className="text-lg font-bold mt-0.5">
统一自定义输入框 (`.hl-input`) 与 SSO 认证卡片(彻底禁用浏览器原生弹窗)
</h2>
</div>
<KeyRound className="w-4 h-4" style={{ color: 'var(--accent)' }} />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
{/* Left: Input Primitives Showcase */}
<div className="p-5 rounded-xl hl-muted border hl-border space-y-4">
<div className="text-xs font-bold uppercase tracking-wider" style={{ color: 'var(--text-muted)' }}>
1. 基础表单输入组件规范 (`.hl-input` / `.hl-input-group`)
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium" style={{ color: 'var(--text-muted)' }}>
带前缀图标与快捷键提示的搜索框
</label>
<div className="hl-input-group">
<Search className="w-4 h-4 shrink-0" style={{ color: 'var(--text-subtle)' }} />
<input
type="text"
value={demoSearch}
onChange={(e) => setDemoSearch(e.target.value)}
placeholder="搜索服务、SSH 指令或节点..."
/>
<kbd
className="px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-card border hl-border"
style={{ color: 'var(--text-muted)' }}
>
⌘K
</kbd>
</div>
</div>
<div className="space-y-1.5">
<label className="block text-xs font-medium" style={{ color: 'var(--text-muted)' }}>
等宽字体 IP / 域名输入框 (`.hl-input .font-mono-num`)
</label>
<input
type="text"
value={demoHost}
onChange={(e) => setDemoHost(e.target.value)}
className="hl-input font-mono-num"
placeholder="192.168.31.164"
/>
</div>
<div className="p-3 rounded-lg hl-card text-[11px] leading-relaxed space-y-1" style={{ color: 'var(--text-muted)' }}>
<div className="font-semibold" style={{ color: 'var(--text-main)' }}>
为什么不再出现浏览器默认输入框和弹窗?
</div>
<p>
1. 全局 CSS 已设置 <code>appearance: none</code> 与自定义聚焦光环{' '}
<code>box-shadow: 0 0 0 3px var(--accent-soft)</code>,并覆盖了 Chrome 自动填充的黄色背景。
</p>
<p>
2. Caddy 网关已移除 <code>basicauth</code>,改用 <code>forward_auth</code> 对接右侧的{' '}
<strong>HomeLab SSO 2.0 统一认证卡片</strong>,一次登录全域名 30 天免密通行。
</p>
</div>
</div>
{/* Right: Live AuthGateCard Preview */}
<div className="flex flex-col items-center">
<AuthGateCard
embedded
targetUrl="https://agy.961504.xyz"
onSuccess={() => showToast('SSO 认证成功!已签发 30 天全域名通行 Cookie')}
/>
</div>
</div>
</div>
)}
{/* STORY 4: ServiceCard Interactive Playground */}
{activeStory === 'service-card' && (
<div className="hl-card p-6 space-y-6">
<div className="border-b hl-border pb-4 flex items-center justify-between">
@@ -366,7 +479,7 @@ export const StorybookWorkbench: React.FC = () => {
</div>
)}
{/* STORY 4: SshShelf */}
{/* STORY 5: SshShelf */}
{activeStory === 'ssh-shelf' && (
<div className="hl-card p-6 space-y-6">
<div className="border-b hl-border pb-4">
+99 -50
View File
@@ -2,11 +2,11 @@
/* =========================================================================
HomeLab Console V3 (React + Vite + TypeScript + Zustand + Tailwind v4)
Architecture: gethomepage.dev (Top Ribbon + 3-Col Rows + Inline Widgets)
Design System: shadcn/ui + Raycast Command Palette + 4 Official Themes
Architecture: gethomepage.dev (Two-Tier AppShell + 3-Col Rows + Inline Widgets)
Design System: shadcn/ui + Catppuccin/Linear + Custom Form Controls & Auth Gate
========================================================================= */
:root,
[data-theme="zinc-dark"] {
[data-theme="dark"] {
--bg-page: #09090b;
--bg-card: #121215;
--bg-widget: #16161a;
@@ -21,13 +21,13 @@
--primary: #fafafa;
--primary-fg: #09090b;
--accent: #3b82f6;
--accent-soft: rgba(59, 130, 246, 0.14);
--accent-soft: rgba(59, 130, 246, 0.16);
--success: #10b981;
--success-soft: rgba(16, 185, 129, 0.14);
--card-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.5);
--card-shadow: 0 4px 16px -4px rgba(0, 0, 0, 0.55);
}
[data-theme="zinc-light"] {
[data-theme="light"] {
--bg-page: #f8fafc;
--bg-card: #ffffff;
--bg-widget: #f8fafc;
@@ -42,54 +42,12 @@
--primary: #0f172a;
--primary-fg: #ffffff;
--accent: #2563eb;
--accent-soft: rgba(37, 99, 235, 0.08);
--accent-soft: rgba(37, 99, 235, 0.12);
--success: #10b981;
--success-soft: rgba(16, 185, 129, 0.1);
--card-shadow: 0 1px 3px 0 rgba(15, 23, 42, 0.04), 0 1px 2px -1px rgba(15, 23, 42, 0.03);
}
[data-theme="catppuccin"] {
--bg-page: #11111b;
--bg-card: #1e1e2e;
--bg-widget: #181825;
--bg-muted: #313244;
--bg-hover: #242438;
--text-main: #cdd6f4;
--text-muted: #a6adc8;
--text-subtle: #6c7086;
--border-color: #313244;
--border-subtle: rgba(205, 214, 244, 0.06);
--border-hover: #45475a;
--primary: #cba6f7;
--primary-fg: #11111b;
--accent: #89b4fa;
--accent-soft: rgba(137, 180, 250, 0.15);
--success: #a6e3a1;
--success-soft: rgba(166, 227, 161, 0.15);
--card-shadow: 0 4px 16px -4px rgba(17, 17, 27, 0.6);
}
[data-theme="nord"] {
--bg-page: #242933;
--bg-card: #2e3440;
--bg-widget: #292e39;
--bg-muted: #3b4252;
--bg-hover: #353c4a;
--text-main: #eceff4;
--text-muted: #d8dee9;
--text-subtle: #81a1c1;
--border-color: #3b4252;
--border-subtle: rgba(236, 239, 244, 0.06);
--border-hover: #4c566a;
--primary: #88c0d0;
--primary-fg: #2e3440;
--accent: #88c0d0;
--accent-soft: rgba(136, 192, 208, 0.15);
--success: #a3be8c;
--success-soft: rgba(163, 190, 140, 0.15);
--card-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.35);
}
body {
font-family: "Inter", -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
background-color: var(--bg-page);
@@ -98,6 +56,94 @@ body {
-webkit-font-smoothing: antialiased;
}
/* =========================================================================
Global Browser Default Input Reset & Custom Input System (.hl-input)
彻底禁用浏览器原生丑陋的 input 边框、外发光与黄色自动填充背景
========================================================================= */
input,
textarea,
select,
button {
-webkit-appearance: none;
appearance: none;
outline: none;
font-family: inherit;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill {
-webkit-text-fill-color: var(--text-main) !important;
-webkit-box-shadow: 0 0 0px 1000px var(--bg-muted) inset !important;
transition: background-color 5000s ease-in-out 0s;
}
.hl-input {
width: 100%;
background-color: var(--bg-muted);
color: var(--text-main);
border: 1px solid var(--border-color);
border-radius: 0.625rem;
padding: 0.55rem 0.75rem;
font-size: 0.8125rem;
line-height: 1.25rem;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.hl-input::placeholder {
color: var(--text-subtle);
}
.hl-input:hover {
border-color: var(--border-hover);
}
.hl-input:focus,
.hl-input:focus-visible {
background-color: var(--bg-card);
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.hl-input-group {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
background-color: var(--bg-muted);
color: var(--text-main);
border: 1px solid var(--border-color);
border-radius: 0.625rem;
padding: 0 0.75rem;
height: 2.375rem;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.hl-input-group:hover {
border-color: var(--border-hover);
}
.hl-input-group:focus-within {
background-color: var(--bg-card);
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-soft);
}
.hl-input-group input {
flex: 1;
min-width: 0;
height: 100%;
background: transparent;
border: 0;
color: var(--text-main);
font-size: 0.8125rem;
}
.hl-input-group input::placeholder {
color: var(--text-subtle);
}
.font-mono-num {
font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
font-variant-numeric: tabular-nums;
@@ -141,11 +187,14 @@ body {
color: var(--primary-fg);
font-weight: 500;
border-radius: 0.5rem;
transition: opacity 0.15s ease;
transition: opacity 0.15s ease, transform 0.1s ease;
}
.hl-btn-primary:hover {
opacity: 0.9;
}
.hl-btn-primary:active {
transform: scale(0.99);
}
.hl-btn-outline {
background-color: var(--bg-card);