feat(portal): implement two-tier AppShell header (fix top-left wrapping) and interactive Storybook workbench

Browse Source
This commit is contained in:
Yin Kou committed 2026-11-07 00:17:06 +08:00
1 parent 4ca86da55f
commit 6b589fa62a
11 files changed
+935 -178

No files matched your search

-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-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-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);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wider:.05em;--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-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-fielLine 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
+124
View File
@@ -0,0 +1,124 @@
(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
-89
View File
@@ -1,89 +0,0 @@
(function(){const p=document.createElement("link").relList;if(p&&p.supports&&p.supports("modulepreload"))return;for(const A of document.querySelectorAll('link[rel="modulepreload"]'))o(A);new MutationObserver(A=>{for(const q of A)if(q.type==="childList")for(const nl of q.addedNodes)nl.tagName==="LINK"&&nl.rel==="modulepreload"&&o(nl)}).observe(document,{childList:!0,subtree:!0});function N(A){const q={};return A.integrity&&(q.integrity=A.integrity),A.referrerPolicy&&(q.referrerPolicy=A.referrerPolicy),A.crossOrigin==="use-credentials"?q.credentials="include":A.crossOrigin==="anonymous"?q.credentials="omit":q.credentials="same-origin",q}function o(A){if(A.ep)return;A.ep=!0;const q=N(A);fetch(A.href,q)}})();function m0(b){return b&&b.__esModule&&Object.prototype.hasOwnProperty.call(b,"default")?b.default:b}var zs={exports:{}},an={};/**
* @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 l0;function ry(){if(l0)return an;l0=1;var b=Symbol.for("react.transitional.element"),p=Symbol.for("react.fragment");function N(o,A,q){var nl=null;if(q!==void 0&&(nl=""+q),A.key!==void 0&&(nl=""+A.key),"key"in A){q={};for(var ll in A)ll!=="key"&&(q[ll]=A[ll])}else q=A;return A=q.ref,{$$typeof:b,type:o,key:nl,ref:A!==void 0?A:null,props:q}}return an.Fragment=p,an.jsx=N,an.jsxs=N,an}var t0;function vy(){return t0||(t0=1,zs.exports=ry()),zs.exports}var E=vy(),_s={exports:{}},L={};/**
* @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 e0;function hy(){if(e0)return L;e0=1;var b=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),N=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),A=Symbol.for("react.profiler"),q=Symbol.for("react.consumer"),nl=Symbol.for("react.context"),ll=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),J=Symbol.for("react.memo"),C=Symbol.for("react.lazy"),S=Symbol.for("react.activity"),B=Symbol.for("react.view_transition"),Z=Symbol.iterator;function El(d){return d===null||typeof d!="object"?null:(d=Z&&d[Z]||d["@@iterator"],typeof d=="function"?d:null)}var wl={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},vl=Object.assign,kl={};function nt(d,_,H){this.props=d,this.context=_,this.refs=kl,this.updater=H||wl}nt.prototype.isReactComponent={},nt.prototype.setState=function(d,_){if(typeof d!="object"&&typeof d!="function"&&d!=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,d,_,"setState")},nt.prototype.forceUpdate=function(d){this.updater.enqueueForceUpdate(this,d,"forceUpdate")};function Ke(){}Ke.prototype=nt.prototype;function Bt(d,_,H){this.props=d,this.context=_,this.refs=kl,this.updater=H||wl}var qt=Bt.prototype=new Ke;qt.constructor=Bt,vl(qt,nt.prototype),qt.isPureReactComponent=!0;var it=Array.isArray;function W(){}var il={H:null,A:null,T:null,S:null},Gt=Object.prototype.hasOwnProperty;function bt(d,_,H){var Y=H.ref;return{$$typeof:b,type:d,key:_,ref:Y!==void 0?Y:null,props:H}}function Tt(d,_){return bt(d.type,_,d.props)}function ct(d){return typeof d=="object"&&d!==null&&d.$$typeof===b}function ye(d){var _={"=":"=0",":":"=2"};return"$"+d.replace(/[=:]/g,function(H){return _[H]})}var Je=/\/+/g;function Cl(d,_){return typeof d=="object"&&d!==null&&d.key!=null?ye(""+d.key):_.toString(36)}function O(d){switch(d.status){case"fulfilled":return d.value;case"rejected":throw d.reason;default:switch(typeof d.status=="string"?d.then(W,W):(d.status="pending",d.then(function(_){d.status==="pending"&&(d.status="fulfilled",d.value=_)},function(_){d.status==="pending"&&(d.status="rejected",d.reason=_)})),d.status){case"fulfilled":return d.value;case"rejected":throw d.reason}}throw d}function Q(d,_,H,Y,el){var ul=typeof d;(ul==="undefined"||ul==="boolean")&&(d=null);var cl=!1;if(d===null)cl=!0;else switch(ul){case"bigint":case"string":case"number":cl=!0;break;case"object":switch(d.$$typeof){case b:case p:cl=!0;break;case C:return cl=d._init,Q(cl(d._payload),_,H,Y,el)}}if(cl)return el=el(d),cl=Y===""?"."+Cl(d,0):Y,it(el)?(H="",cl!=null&&(H=cl.replace(Je,"$&/")+"/"),Q(el,_,H,"",function(le){return le})):el!=null&&(ct(el)&&(el=Tt(el,H+(el.key==null||d&&d.key===el.key?"":(""+el.key).replace(Je,"$&/")+"/")+cl)),_.push(el)),1;cl=0;var R=Y===""?".":Y+":";if(it(d))for(var V=0;V<d.length;V++)Y=d[V],ul=R+Cl(Y,V),cl+=Q(Y,_,H,ul,el);else if(V=El(d),typeof V=="function")for(d=V.call(d),V=0;!(Y=d.next()).done;)Y=Y.value,ul=R+Cl(Y,V++),cl+=Q(Y,_,H,ul,el);else if(ul==="object"){if(typeof d.then=="function")return Q(O(d),_,H,Y,el);throw _=String(d),Error("Objects are not valid as a React child (found: "+(_==="[object Object]"?"object with keys {"+Object.keys(d).join(", ")+"}":_)+"). If you meant to render a collection of children, use an array instead.")}return cl}function X(d,_,H){if(d==null)return d;var Y=[],el=0;return Q(d,Y,"","",function(ul){return _.call(H,ul,el++)}),Y}function yl(d){if(d._status===-1){var _=d._result,H=_();H.then(function(Y){(d._status===0||d._status===-1)&&(d._status=1,d._result=Y,H.status===void 0&&(H.status="fulfilled",H.value=Y))},function(Y){(d._status===0||d._status===-1)&&(d._status=2,d._result=Y,H.status===void 0&&(H.status="rejected",H.reason=Y))}),d._status===-1&&(d._status=0,d._result=H)}if(d._status===1)return d._result.default;throw d._result}var sl=typeof reportError=="function"?reportError:function(d){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var _=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof d=="object"&&d!==null&&typeof d.message=="string"?String(d.message):String(d),error:d});if(!window.dispatchEvent(_))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",d);return}console.error(d)};function Dt(d){var _=il.T,H={};H.types=_!==null?_.types:null,il.T=H;try{var Y=d(),el=il.S;el!==null&&el(H,Y),typeof Y=="object"&&Y!==null&&typeof Y.then=="function"&&Y.then(W,sl)}catch(ul){sl(ul)}finally{_!==null&&H.types!==null&&(_.types=H.types),il.T=_}}function Pt(d){var _=il.T;if(_!==null){var H=_.types;H===null?_.types=[d]:H.indexOf(d)===-1&&H.push(d)}else Dt(Pt.bind(null,d))}var $e={map:X,forEach:function(d,_,H){X(d,function(){_.apply(this,arguments)},H)},count:function(d){var _=0;return X(d,function(){_++}),_},toArray:function(d){return X(d,function(_){return _})||[]},only:function(d){Line 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 a0;function yy(){return a0||(a0=1,(function(b){function p(O,Q){var X=O.length;O.push(Q);l:for(;0<X;){var yl=X-1>>>1,sl=O[yl];if(0<A(sl,Q))O[yl]=Q,O[X]=sl,X=yl;else break l}}function N(O){return O.length===0?null:O[0]}function o(O){if(O.length===0)return null;var Q=O[0],X=O.pop();if(X!==Q){O[0]=X;l:for(var yl=0,sl=O.length,Dt=sl>>>1;yl<Dt;){var Pt=2*(yl+1)-1,$e=O[Pt],d=Pt+1,_=O[d];if(0>A($e,X))d<sl&&0>A(_,$e)?(O[yl]=_,O[d]=X,yl=d):(O[yl]=$e,O[Pt]=X,yl=Pt);else if(d<sl&&0>A(_,X))O[yl]=_,O[d]=X,yl=d;else break l}}return Q}function A(O,Q){var X=O.sortIndex-Q.sortIndex;return X!==0?X:O.id-Q.id}if(b.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var q=performance;b.unstable_now=function(){return q.now()}}else{var nl=Date,ll=nl.now();b.unstable_now=function(){return nl.now()-ll}}var U=[],J=[],C=1,S=null,B=3,Z=!1,El=!1,wl=!1,vl=!1,kl=typeof setTimeout=="function"?setTimeout:null,nt=typeof clearTimeout=="function"?clearTimeout:null,Ke=typeof setImmediate<"u"?setImmediate:null;function Bt(O){for(var Q=N(J);Q!==null;){if(Q.callback===null)o(J);else if(Q.startTime<=O)o(J),Q.sortIndex=Q.expirationTime,p(U,Q);else break;Q=N(J)}}function qt(O){if(wl=!1,Bt(O),!El)if(N(U)!==null)El=!0,it||(it=!0,ct());else{var Q=N(J);Q!==null&&Cl(qt,Q.startTime-O)}}var it=!1,W=-1,il=5,Gt=-1;function bt(){return vl?!0:!(b.unstable_now()-Gt<il)}function Tt(){if(vl=!1,it){var O=b.unstable_now();Gt=O;var Q=!0;try{l:{El=!1,wl&&(wl=!1,nt(W),W=-1),Z=!0;var X=B;try{t:{for(Bt(O),S=N(U);S!==null&&!(S.expirationTime>O&&bt());){var yl=S.callback;if(typeof yl=="function"){S.callback=null,B=S.priorityLevel;var sl=yl(S.expirationTime<=O);if(O=b.unstable_now(),typeof sl=="function"){S.callback=sl,Bt(O),Q=!0;break t}S===N(U)&&o(U),Bt(O)}else o(U);S=N(U)}if(S!==null)Q=!0;else{var Dt=N(J);Dt!==null&&Cl(qt,Dt.startTime-O),Q=!1}}break l}finally{S=null,B=X,Z=!1}Q=void 0}}finally{Q?ct():it=!1}}}var ct;if(typeof Ke=="function")ct=function(){Ke(Tt)};else if(typeof MessageChannel<"u"){var ye=new MessageChannel,Je=ye.port2;ye.port1.onmessage=Tt,ct=function(){Je.postMessage(null)}}else ct=function(){kl(Tt,0)};function Cl(O,Q){W=kl(function(){O(b.unstable_now())},Q)}b.unstable_IdlePriority=5,b.unstable_ImmediatePriority=1,b.unstable_LowPriority=4,b.unstable_NormalPriority=3,b.unstable_Profiling=null,b.unstable_UserBlockingPriority=2,b.unstable_cancelCallback=function(O){O.callback=null},b.unstable_forceFrameRate=function(O){0>O||125<O?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):il=0<O?Math.floor(1e3/O):5},b.unstable_getCurrentPriorityLevel=function(){return B},b.unstable_next=function(O){switch(B){case 1:case 2:case 3:var Q=3;break;default:Q=B}var X=B;B=Q;try{return O()}finally{B=X}},b.unstable_requestPaint=function(){vl=!0},b.unstable_runWithPriority=function(O,Q){switch(O){case 1:case 2:case 3:case 4:case 5:break;default:O=3}var X=B;B=O;try{return Q()}finally{B=X}},b.unstable_scheduleCallback=function(O,Q,X){var yl=b.unstable_now();switch(typeof X=="object"&&X!==null?(X=X.delay,X=typeof X=="number"&&0<X?yl+X:yl):X=yl,O){case 1:var sl=-1;break;case 2:sl=250;break;case 5:sl=1073741823;break;case 4:sl=1e4;break;default:sl=5e3}return sl=X+sl,O={id:C++,callback:Q,priorityLevel:O,startTime:X,expirationTime:sl,sortIndex:-1},X>yl?(O.sortIndex=X,p(J,O),N(U)===null&&O===N(J)&&(wl?(nt(W),W=-1):wl=!0,Cl(qt,X-yl))):(O.sortIndex=sl,p(U,O),El||Z||(El=!0,it||(it=!0,ct()))),O},b.unstable_shouldYield=bt,b.unstable_wrapCallback=function(O){var Q=B;return function(){var X=B;B=Q;try{return O.apply(this,arguments)}finally{B=X}}}})(ps)),ps}var n0;function gy(){return n0||(n0=1,xs.exports=yy()),xs.exports}var Os={exports:{}},Ll={};/**
* @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 Sy(){if(i0)return Ll;i0=1;var b=As();function p(C){var S="https://react.dev/errors/"+C;if(1<arguments.length){S+="?args[]="+encodeURIComponent(arguments[1]);for(var B=2;B<arguments.length;B++)S+="&args[]="+encodeURIComponent(arguments[B])}return"Minified React error #"+C+"; visit "+S+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function N(){}var o={d:{f:N,r:function(){throw Error(p(522))},D:N,C:N,L:N,m:N,X:N,S:N,M:N},p:0,findDOMNode:null},A=Symbol.for("react.portal"),q=Symbol.for("react.recoverable"),nl=Symbol.for("react.optimistic_key");function ll(C,S,B){var Z=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:A,key:Z==null?null:Z===nl?nl:""+Z,children:C,containerInfo:S,implementation:B}}var U=b.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function J(C,S){if(C==="font")return"";if(typeof S=="string")return S==="use-credentials"?S:""}return Ll.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=o,Ll.browser=function(C){return{$$typeof:q,_reason:C}},Ll.createPortal=function(C,S){var B=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!S||S.nodeType!==1&&S.nodeType!==9&&S.nodeType!==11)throw Error(p(299));return ll(C,S,null,B)},Ll.flushSync=function(C){var S=U.T,B=o.p;try{if(U.T=null,o.p=2,C)return C()}finally{U.T=S,o.p=B,o.d.f()}},Ll.preconnect=function(C,S){typeof C=="string"&&(S?(S=S.crossOrigin,S=typeof S=="string"?S==="use-credentials"?S:"":void 0):S=null,o.d.C(C,S))},Ll.prefetchDNS=function(C){typeof C=="string"&&o.d.D(C)},Ll.preinit=function(C,S){if(typeof C=="string"&&S&&typeof S.as=="string"){var B=S.as,Z=J(B,S.crossOrigin),El=typeof S.integrity=="string"?S.integrity:void 0,wl=typeof S.fetchPriority=="string"?S.fetchPriority:void 0;B==="style"?o.d.S(C,typeof S.precedence=="string"?S.precedence:void 0,{crossOrigin:Z,integrity:El,fetchPriority:wl}):B==="script"&&o.d.X(C,{crossOrigin:Z,integrity:El,fetchPriority:wl,nonce:typeof S.nonce=="string"?S.nonce:void 0})}},Ll.preinitModule=function(C,S){if(typeof C=="string")if(typeof S=="object"&&S!==null){if(S.as==null||S.as==="script"){var B=J(S.as,S.crossOrigin);o.d.M(C,{crossOrigin:B,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&&o.d.M(C)},Ll.preload=function(C,S){if(typeof C=="string"&&typeof S=="object"&&S!==null&&typeof S.as=="string"){var B=S.as,Z=J(B,S.crossOrigin);o.d.L(C,B,{crossOrigin:Z,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})}},Ll.preloadModule=function(C,S){if(typeof C=="string")if(S){var B=J(S.as,S.crossOrigin);o.d.m(C,{as:typeof S.as=="string"&&S.as!=="script"?S.as:void 0,crossOrigin:B,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 o.d.m(C)},Ll.requestFormReset=function(C){o.d.r(C)},Ll.unstable_batchedUpdates=function(C,S){return C(S)},Ll.useFormState=function(C,S,B){return U.H.useFormState(C,S,B)},Ll.useFormStatus=function(){return U.H.useHostTransitionStatus()},Ll.version="19.3.1",Ll}var c0;function by(){if(c0)return Os.exports;c0=1;function b(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(b)}catch(p){console.error(p)}}return b(),Os.exports=Sy(),Os.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 f0;function Ty(){if(f0)return nn;f0=1;var b=gy(),p=As(),N=by();function o(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 A(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 nl(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 ll(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 U(l){if(q(l)!==l)throw Error(o(188))}function J(l){var t=l.alternate;if(!t){if(t=q(l),t===null)throw Error(o(188));return t!==l?null:l}for(var e=l,u=t;;){var a=e.return;if(a===null)break;var n=a.alternate;if(n===null){if(u=a.return,u!==null){e=u;continue}break}if(a.child===n.child){for(n=a.child;n;){if(n===e)return U(a),l;if(n===u)return U(a),t;n=n.sibling}throw Error(o(188))}if(e.return!==u.return)e=a,u=n;else{for(var i=!1,c=a.child;c;){if(c===e){i=!0,e=a,u=n;break}if(c===u){i=!0,u=a,e=n;break}c=c.sibling}if(!i){for(c=n.child;c;){if(c===e){i=!0,e=n,u=a;break}if(c===u){i=!0,u=n,e=a;break}c=c.sibling}if(!i)throw Error(o(189))}}if(e.alternate!==u)throw Error(o(190))}if(e.tag!==3)throw Error(o(188));return e.stateNode.current===e?l:t}function C(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l;for(l=l.child;l!==null;){if(t=C(l),t!==null)return t;l=l.sibling}return null}function S(l,t,e,u,a,n){for(;l!==null;){if((l.tag===5||l.tag===27||l.tag===6)&&e(l,u,a,n)||(l.tag!==22||l.memoizedState===null)&&(t||l.tag!==5&&l.tag!==27)&&S(l.child,t,e,u,a,n))return!0;l=l.sibling}return!1}function B(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 Z(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 El(l){var t=[null,null],e=B(l);return e===null||wl(t,l,e.child,{foundSelf:!1}),t}function wl(l,t,e,u){for(;e!==null;){if(e===t)u.foundSelf=!0;else if(e.tag===5||e.tag===27||e.tag===6){if(u.foundSelf)return l[1]=e,!0;l[0]=e}else if((e.tag!==22||e.memoizedState===null)&&wl(l,t,e.child,u))return!0;e=e.sibling}return!1}function vl(l){switch(l.tag){case 5:case 27:case 6:return l.stateNode;case 3:return l.stateNode.containerInfo;default:throw Error(o(559))}}var kl=null,nt=null;function Ke(l,t,e){return l===e?!0:l===t?(kl=l,!0):!1}function Bt(l,t,e){return l===e?(nt=l,!1):l===t?(nt!==null&&(kl=l),!0):!1}function qt(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 it(l,t,e){for(var u=0,a=l;a;a=e(a))u++;a=0;for(var n=t;n;n=e(n))a++;for(;0<u-a;)l=e(l),u--;for(;0<a-u;)t=e(t),a--;for(;u--;){if(l===t||t!==null&&l===t.alternate)return l;l=e(l),t=e(t)}return null}var W=Object.assign,il=Symbol.for("react.element"),Gt=Symbol.for("react.transitional.element"),bt=Symbol.for("react.portal"),Tt=Symbol.for("react.fragment"),ct=Symbol.for("react.strict_mode"),ye=Symbol.for("react.profiler"),Je=Symbol.for("react.consumer"),Cl=Symbol.for("react.context"),O=Symbol.for("react.forward_ref"),Q=Symbol.for("react.suspense"),X=Symbol.for("react.suspense_list"),yl=Symbol.for("react.memo"),sl=Symbol.for("react.lazy"),Dt=Symbol.for("react.activity"),Pt=Symbol.for("react.legacy_hidden"),$e=Symbol.for("react.memo_cache_sentinel"),d=Symbol.for("react.view_transition"),_=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 el=Symbol.for("react.client.reference");function ul(l){if(l==null)return null;if(typeof l=="function")return l.$$typeof===el?null:l.displayName||l.name||null;if(typeof l=="string")return l;switch(l){case Tt:return"Fragment";case ye:return"Profiler";case ct:return"StrictMode";case Q:return"Suspense";case X:return"SuspenseList";case Dt:return"Activity";case d:return"ViewTransition"}if(typeof l=="object")switch(l.$$typeof){case bt:return"Portal";case Cl:return l.displayName||"Context";case Je:return(l._context.displayName||"Context")+".Consumer";case O:var t=l.render;return l=l.displayName,l||(l=t.displayName||t.name||"",l=l!==""?"ForwardRef("+l+")":"ForwardRef"),l;case yl:return t=l.displayName||null,t!==null?t:ul(l.type)||"Memo";case sl:t=l._payload,l=l._init;try{return ul(l(t))}catch{}}return null}var cl=Array.isArray,R=p.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,V=N.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,le={pending:!1,data:null,method:null,actiLine truncated
at`)?" (<anonymous>)":-1<e.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Zi+l+Ms}var Vi=!1;function Li(l,t){if(!l||Vi)return"";Vi=!0;var e=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var u={DetermineComponentFrameRoot:function(){try{if(t){var z=function(){throw Error()};if(Object.defineProperty(z.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(z,[])}catch(x){var m=x}Reflect.construct(l,[],z)}else{try{z.call()}catch(x){m=x}z=!1;try{var y=Object.getOwnPropertyDescriptor(l.prototype,"props");Object.defineProperty(l.prototype,"props",{configurable:!0,set:function(){throw Error()}}),z=!0,new l}finally{z&&(y!==void 0?Object.defineProperty(l.prototype,"props",y):delete l.prototype.props)}}}else{try{throw Error()}catch(x){m=x}(z=l())&&typeof z.catch=="function"&&z.catch(function(){})}}catch(x){if(x&&m&&typeof x.stack=="string")return[x.stack,m.stack]}return[null,null]}};u.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var a=Object.getOwnPropertyDescriptor(u.DetermineComponentFrameRoot,"name");a&&a.configurable&&Object.defineProperty(u.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=u.DetermineComponentFrameRoot(),i=n[0],c=n[1];if(i&&c){var f=i.split(`
`),v=c.split(`
`);for(a=u=0;u<f.length&&!f[u].includes("DetermineComponentFrameRoot");)u++;for(;a<v.length&&!v[a].includes("DetermineComponentFrameRoot");)a++;if(u===f.length||a===v.length)for(u=f.length-1,a=v.length-1;1<=u&&0<=a&&f[u]!==v[a];)a--;for(;1<=u&&0<=a;u--,a--)if(f[u]!==v[a]){if(u!==1||a!==1)do if(u--,a--,0>a||f[u]!==v[a]){var g=`
`+f[u].replace(" at new "," at ");return l.displayName&&g.includes("<anonymous>")&&(g=g.replace("<anonymous>",l.displayName)),g}while(1<=u&&0<=a);break}}}finally{Vi=!1,Error.prepareStackTrace=e}return(e=l?l.displayName||l.name:"")?Se(e):""}function g0(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 Li(l.type,!1);case 11:return Li(l.type.render,!1);case 1:return Li(l.type,!0);case 31:return Se("Activity");case 30:return Se("ViewTransition");default:return""}}function Ds(l){try{var t="",e=null;do t+=g0(l,e),e=l,l=l.return;while(l);return t}catch(u){return`
Error generating stack: `+u.message+`
`+u.stack}}var wi=Object.prototype.hasOwnProperty,Ki=b.unstable_scheduleCallback,Ji=b.unstable_cancelCallback,S0=b.unstable_shouldYield,b0=b.unstable_requestPaint,ft=b.unstable_now,T0=b.unstable_getCurrentPriorityLevel,Us=b.unstable_ImmediatePriority,Cs=b.unstable_UserBlockingPriority,on=b.unstable_NormalPriority,E0=b.unstable_LowPriority,Rs=b.unstable_IdlePriority,z0=b.log,_0=b.unstable_setDisableYieldValue,da=null,st=null;function be(l){if(typeof z0=="function"&&_0(l),st&&typeof st.setStrictMode=="function")try{st.setStrictMode(da,l)}catch{}}var ot=Math.clz32?Math.clz32:p0,N0=Math.log,x0=Math.LN2;function p0(l){return l>>>=0,l===0?32:31-(N0(l)/x0|0)|0}var dn=256,mn=262144,rn=4194304;function We(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 u=l.pendingLanes;if(u===0)return 0;var a=0,n=l.suspendedLanes,i=l.pingedLanes;l=l.warmLanes;var c=u&134217727;return c!==0?(u=c&~n,u!==0?a=We(u):(i&=c,i!==0?a=We(i):e||(e=c&~l,e!==0&&(a=We(e))))):(c=u&~n,c!==0?a=We(c):i!==0?a=We(i):e||(e=u&~l,e!==0&&(a=We(e)))),a===0?0:t!==0&&t!==a&&(t&n)===0&&(n=a&-a,e=t&-t,n>=e||n===32&&(e&4194048)!==0)?t:a}function ma(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function js(l,t){(t&8)!==0&&(t|=t&32);var e=l.entangledLanes;if(e!==0)for(l=l.entanglements,e&=t;0<e;){var u=31-ot(e),a=1<<u;t|=l[u],e&=~a}return t}function O0(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 Hs(){var l=rn;return rn<<=1,(rn&62914560)===0&&(rn=4194304),l}function $i(l){for(var t=[],e=0;31>e;e++)t.push(l);return t}function ra(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function A0(l,t,e,u,a,n){var i=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 c=l.entanglements,f=l.expirationTimes,v=l.hiddenUpdates;for(e=i&~e;0<e;){var g=31-ot(e),z=1<<g;c[g]=0,f[g]=-1;var m=v[g];if(m!==null)for(v[g]=null,g=0;g<m.length;g++){var y=m[g];y!==null&&(y.lane&=-536870913)}e&=~z}u!==0&&Ys(l,u,0),n!==0&&a===0&&l.tag!==0&&(l.suspendedLanes|=n&~(i&~t))}function Ys(l,t,e){l.pendingLanes|=t,l.suspendedLanes&=~t;var u=31-ot(t);l.entangledLanes|=t,l.entanglements[u]=l.entanglements[u]|1073741824|e&261930}function Bs(l,t){var e=l.entangledLanes|=t;for(l=l.entanglements;e;){var u=31-ot(e),a=1<<u;a&t|l[u]&t&&(l[u]|=t),e&=~a}}function qs(l,t){var e=t&-t;return e=(e&42)!==0?1:Wi(e),(e&(l.suspendedLanes|t))!==0?0:e}function Wi(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 Fi(l){return l&=-l,2<l?8<l?(l&134217727)!==0?32:268435456:8:2}function Gs(){var l=V.p;return l!==0?l:(l=window.event,l===void 0?32:Jr(l.type))}function Qs(l,t){var e=V.p;try{return V.p=l,t()}finally{V.p=e}}var te=Math.random().toString(36).slice(2),ql="__reactFiber$"+te,Il="__reactProps$"+te,Eu="__reactContainer$"+te,Xs="__reactEvents$"+te,M0="__reactListeners$"+te,D0="__reactHandles$"+te,Zs="__reactResources$"+te,va="__reactMarker$"+te,hn="__reactLoad$"+te;function yn(l){delete l[ql],delete l[Il],delete l[M0],delete l[D0]}function Fe(l){var t;if(t=l[ql])return t;for(var e=l.parentNode;e;){if(t=e[Eu]||e[ql]){if(e=t.alternate,t.child!==null||e!==null&&e.child!==null)for(l=Or(l);l!==null;){if(e=l[ql])return e;l=Or(l)}return t}l=e,e=l.parentNode}return null}function zu(l){if(l=l[ql]||l[Eu]){var t=l.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return l}return null}function ha(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l.stateNode;throw Error(o(33))}function _u(l){var t=l[Zs];return t||(t=l[Zs]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function Rl(l){l[va]=!0}function Vs(l){l[hn]=void 0}var Ls=new Set,ws={};function ke(l,t){Nu(l,t),Nu(l+"Capture",t)}function Line truncated
`).replace(gh,"")}function cr(l,t){return t=ir(t),ir(l)===t}function rl(l,t,e,u,a,n){switch(e){case"children":if(typeof u=="string")t==="body"||t==="textarea"&&u===""||pu(l,u);else if(typeof u=="number"||typeof u=="bigint")t!=="body"&&pu(l,""+u);else return;break;case"className":Sn(l,"class",u);break;case"tabIndex":Sn(l,"tabindex",u);break;case"dir":case"role":case"viewBox":case"width":case"height":Sn(l,e,u);break;case"style":to(l,u,n);return;case"data":if(t!=="object"){Sn(l,"data",u);break}case"src":case"href":if(u===""&&(t!=="a"||e!=="href")){l.removeAttribute(e);break}if(u==null||typeof u=="function"||typeof u=="symbol"||typeof u=="boolean"){l.removeAttribute(e);break}u=bn(u),l.setAttribute(e,u);break;case"action":case"formAction":if(typeof u=="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"&&rl(l,t,"name",a.name,a,null),rl(l,t,"formEncType",a.formEncType,a,null),rl(l,t,"formMethod",a.formMethod,a,null),rl(l,t,"formTarget",a.formTarget,a,null)):(rl(l,t,"encType",a.encType,a,null),rl(l,t,"method",a.method,a,null),rl(l,t,"target",a.target,a,null)));if(u==null||typeof u=="symbol"||typeof u=="boolean"){l.removeAttribute(e);break}u=bn(u),l.setAttribute(e,u);break;case"onClick":u!=null&&(l.onclick=Zt);return;case"onScroll":u!=null&&k("scroll",l);return;case"onScrollEnd":u!=null&&k("scrollend",l);return;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(o(61));if(e=u.__html,e!=null){if(a.children!=null)throw Error(o(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"multiple":l.multiple=u&&typeof u!="function"&&typeof u!="symbol";break;case"muted":l.muted=u&&typeof u!="function"&&typeof u!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(u==null||typeof u=="function"||typeof u=="boolean"||typeof u=="symbol"){l.removeAttribute("xlink:href");break}e=bn(u),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":u!=null&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,u):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":u&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,""):l.removeAttribute(e);break;case"capture":case"download":u===!0?l.setAttribute(e,""):u!==!1&&u!=null&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,u):l.removeAttribute(e);break;case"cols":case"rows":case"size":case"span":u!=null&&typeof u!="function"&&typeof u!="symbol"&&!isNaN(u)&&1<=u?l.setAttribute(e,u):l.removeAttribute(e);break;case"rowSpan":case"start":u==null||typeof u=="function"||typeof u=="symbol"||isNaN(u)?l.removeAttribute(e):l.setAttribute(e,u);break;case"popover":k("beforetoggle",l),k("toggle",l),gn(l,"popover",u);break;case"xlinkActuate":ee(l,"http://www.w3.org/1999/xlink","xlink:actuate",u);break;case"xlinkArcrole":ee(l,"http://www.w3.org/1999/xlink","xlink:arcrole",u);break;case"xlinkRole":ee(l,"http://www.w3.org/1999/xlink","xlink:role",u);break;case"xlinkShow":ee(l,"http://www.w3.org/1999/xlink","xlink:show",u);break;case"xlinkTitle":ee(l,"http://www.w3.org/1999/xlink","xlink:title",u);break;case"xlinkType":ee(l,"http://www.w3.org/1999/xlink","xlink:type",u);break;case"xmlBase":ee(l,"http://www.w3.org/XML/1998/namespace","xml:base",u);break;case"xmlLang":ee(l,"http://www.w3.org/XML/1998/namespace","xml:lang",u);break;case"xmlSpace":ee(l,"http://www.w3.org/XML/1998/namespace","xml:space",u);break;case"is":gn(l,"is",u);break;case"innerText":case"textContent":return;default:if(!(2<e.length)||e[0]!=="o"&&e[0]!=="O"||e[1]!=="n"&&e[1]!=="N")e=Y0.get(e)||e,gn(l,e,u);else return}al=!0}function Pf(l,t,e,u,a,n){switch(e){case"style":to(l,u,n);return;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(o(61));if(e=u.__html,e!=null){if(a.children!=null)throw Error(o(60));(n!=null?n.__html:void 0)!==e&&(l.innerHTML=e)}}break;case"children":if(typeof u=="string")pu(l,u);else if(typeof u=="number"||typeof u=="bigint")pu(l,""+u);else return;break;case"onScroll":u!=null&&k("scroll",l);return;case"onScrollEnd":u!=null&&k("scrollend" 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 Uy=b=>b.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),h0=(...b)=>b.filter((p,N,o)=>!!p&&p.trim()!==""&&o.indexOf(p)===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 Cy={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 Ry=at.forwardRef(({color:b="currentColor",size:p=24,strokeWidth:N=2,absoluteStrokeWidth:o,className:A="",children:q,iconNode:nl,...ll},U)=>at.createElement("svg",{ref:U,...Cy,width:p,height:p,stroke:b,strokeWidth:o?Number(N)*24/Number(p):N,className:h0("lucide",A),...ll},[...nl.map(([J,C])=>at.createElement(J,C)),...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 Gi=(b,p)=>{const N=at.forwardRef(({className:o,...A},q)=>at.createElement(Ry,{ref:q,iconNode:p,className:h0(`lucide-${Uy(b)}`,o),...A}));return N.displayName=`${b}`,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 jy=[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]],Hy=Gi("Moon",jy);/**
* @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=[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]],y0=Gi("Search",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 By=[["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"}]],qy=Gi("Sun",By);/**
* @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=[["polyline",{points:"4 17 10 11 4 5",key:"akl6gq"}],["line",{x1:"12",x2:"20",y1:"19",y2:"19",key:"q2wloq"}]],Qy=Gi("Terminal",Gy),Xy=[{id:"auto",label:"智能路由"},{id:"edge443",label:"443 免端口"},{id:"home10443",label:"10443 家宽"},{id:"lan",label:"LAN 局域网"}],Zy=()=>{const{theme:b,resolvedTheme:p,routeMode:N,telemetry:o,setTheme:A,toggleTheme:q,setRouteMode:nl,setShowAgyModal:ll,setShowCmdPalette:U}=Su();return E.jsx("header",{className:"border-b hl-border sticky top-0 z-30 backdrop-blur-md",style:{backgroundColor:"color-mix(in srgb, var(--bg-page) 86%, transparent)"},children:E.jsxs("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3 flex flex-wrap items-center justify-between gap-3",children:[E.jsxs("div",{className:"flex flex-wrap items-center gap-2.5 sm:gap-3",children:[E.jsxs("div",{className:"flex items-center gap-2.5 pr-1",children:[E.jsx("img",{src:"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/apple.png",alt:"Mac mini",className:"w-6 h-6 rounded"}),E.jsx("span",{className:"font-semibold text-sm tracking-tight",children:"HomeLab"}),E.jsx("span",{className:"w-2 h-2 rounded-full",style:{backgroundColor:"var(--success)"},title:"全屋 4 节点在线"})]}),E.jsxs("div",{className:"hidden xl:flex items-center gap-2 text-xs font-mono-num",children:[E.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",title:"Apple M6 12核处理器负载",children:[E.jsx("span",{style:{color:"var(--text-muted)"},children:"M6 CPU"}),E.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:E.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,o.mini.cpu_pct)}%`}})}),E.jsxs("span",{className:"font-medium",children:[o.mini.cpu_pct,"%"]})]}),E.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",title:"Mac mini 24GB 统一内存",children:[E.jsx("span",{style:{color:"var(--text-muted)"},children:"RAM"}),E.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:E.jsx("div",{className:"h-full hl-bar-fill",style:{width:`${Math.max(5,o.mini.mem_pct)}%`}})}),E.jsxs("span",{className:"font-medium",children:[o.mini.mem_used_gb,"G"]})]}),E.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-2",title:"极空间 Z4 20TB 存储阵列",children:[E.jsx("span",{style:{color:"var(--text-muted)"},children:"NAS 余量"}),E.jsx("div",{className:"w-12 h-1.5 rounded-full hl-muted overflow-hidden",children:E.jsx("div",{className:"h-full hl-bar-fill-green",style:{width:`${Math.max(5,o.nas.pct)}%`}})}),E.jsxs("span",{className:"font-medium",style:{color:"var(--success)"},children:[o.nas.free_tb," TB"]})]}),E.jsxs("div",{className:"hl-pill px-2.5 py-1 flex items-center gap-1.5",title:"AGY 锁定新加坡 Azure 专线出口",children:[E.jsx("span",{className:"w-1.5 h-1.5 rounded-full",style:{backgroundColor:"var(--success)"}}),E.jsx("span",{style:{color:"var(--text-muted)"},children:"SG 专线"}),E.jsx("span",{className:"font-medium",children:o.network.sg_delay_ms>0?`${o.network.sg_delay_ms}ms`:"直连"})]})]})]}),E.jsxs("div",{className:"flex items-center gap-2",children:[E.jsxs("button",{onClick:()=>U(!0),className:"hl-btn-outline px-2.5 py-1 text-xs flex items-center gap-3 cursor-pointer",title:"按 Cmd+K 唤起全局命令面板",children:[E.jsxs("span",{className:"flex items-center gap-1.5",style:{color:"var(--text-muted)"},children:[E.jsx(y0,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:"搜索..."})]}),E.jsx("kbd",{className:"px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-muted",style:{color:"var(--text-muted)"},children:"⌘K"})]}),E.jsx("div",{className:"hidden md:flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs",children:Xy.map(J=>E.jsx("button",{onClick:()=>nl(J.id),className:"px-2 py-1 rounded-md font-medium transition-all cursor-pointer",style:N===J.id?{backgroundColor:"var(--bg-card)",color:"var(--text-main)",boxShadow:"0 1px 2px rgba(0,0,0,0.08)"}:{color:"var(--text-muted)"},children:J.label},J.id))}),E.jsxs("button",{onClick:()=>ll(!0),className:"hl-btn-primary px-3 py-1.5 text-xs flex items-center gap-1.5 cursor-pointer",children:[E.jsx(Qy,{className:"w-3.5 h-3.5"}),E.jsx("span",{children:"AGY 终端"})]}),E.jsxs("div",{className:"flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs",children:[E.jsx("button",{onClick:q,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:p==="dark"?E.jsxs(E.Fragment,{children:[E.jsx(Hy,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:"深色"})]}):E.jsxs(E.Fragment,{children:[E.jsx(qy,{className:"w-3.5 h-3.5"}),E.jsx("span",{className:"hidden sm:inline",children:"浅色"})]})}),b!=="system"&&E.jsx("button",{onClick:()=>A("system"),classNLine 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" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<script type="module" crossorigin src="/assets/index-KlI07XBk.js"></script> <script type="module" crossorigin src="/assets/index-D9ftMUDG.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-6-qoGEVu.css"> <link rel="stylesheet" crossorigin href="/assets/index-B_rkFolS.css">
</head> </head>
<body class="min-h-screen"> <body class="min-h-screen">
<div id="root"></div> <div id="root"></div>
+13 -3
View File
@@ -5,12 +5,16 @@ import { ServiceCard } from './components/ServiceCard';
import { SshShelf } from './components/SshShelf'; import { SshShelf } from './components/SshShelf';
import { CommandPalette } from './components/CommandPalette'; import { CommandPalette } from './components/CommandPalette';
import { AgyTerminalModal } from './components/AgyTerminalModal'; import { AgyTerminalModal } from './components/AgyTerminalModal';
import { StorybookWorkbench } from './components/StorybookWorkbench';
import { LAYOUT_PRESETS } from './design-system/tokens';
import type { ServiceGroup } from './types/telemetry'; import type { ServiceGroup } from './types/telemetry';
export const App: React.FC = () => { export const App: React.FC = () => {
const { const {
telemetry, telemetry,
toast, toast,
viewMode,
layoutPreset,
fetchTelemetry, fetchTelemetry,
setShowCmdPalette, setShowCmdPalette,
getEffectiveMode, getEffectiveMode,
@@ -41,6 +45,8 @@ export const App: React.FC = () => {
return '443 免端口 HTTPS 直达 (*.961504.xyz)'; return '443 免端口 HTTPS 直达 (*.961504.xyz)';
}, [getEffectiveMode, routeMode]); }, [getEffectiveMode, routeMode]);
const gridClass = LAYOUT_PRESETS[layoutPreset]?.gridClass || LAYOUT_PRESETS['homepage-3col'].gridClass;
const serviceGroups = useMemo<ServiceGroup[]>(() => { const serviceGroups = useMemo<ServiceGroup[]>(() => {
const t = telemetry; const t = telemetry;
return [ return [
@@ -176,10 +182,13 @@ export const App: React.FC = () => {
</div> </div>
)} )}
{/* 1. Top Utility & System Resources Ribbon */} {/* 1. Standard Two-Tier AppShell Header & Telemetry Subheader */}
<TopRibbon /> <TopRibbon />
{/* 2. Main Categorized 3-Column Rows */} {/* 2. View Switch: Dashboard vs Storybook Workbench */}
{viewMode === 'storybook' ? (
<StorybookWorkbench />
) : (
<main className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 space-y-7"> <main className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 space-y-7">
{serviceGroups.map((group) => ( {serviceGroups.map((group) => (
<section key={group.title} className="space-y-3"> <section key={group.title} className="space-y-3">
@@ -208,7 +217,7 @@ export const App: React.FC = () => {
)} )}
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className={`grid ${gridClass}`}>
{group.items.map((card) => ( {group.items.map((card) => (
<ServiceCard key={card.id} card={card} /> <ServiceCard key={card.id} card={card} />
))} ))}
@@ -219,6 +228,7 @@ export const App: React.FC = () => {
{/* 3. Bottom SSH Quick Copy & Maintenance Shelf */} {/* 3. Bottom SSH Quick Copy & Maintenance Shelf */}
<SshShelf /> <SshShelf />
</main> </main>
)}
{/* 4. Global Cmd+K Command Palette */} {/* 4. Global Cmd+K Command Palette */}
<CommandPalette /> <CommandPalette />
@@ -0,0 +1,388 @@
import React, { useState } from 'react';
import { BookOpen, Check, Layers, Layout, Palette, Sparkles, Terminal } from 'lucide-react';
import { LAYOUT_PRESETS, THEME_PALETTES, type LayoutPresetId } from '../design-system/tokens';
import { usePortalStore } from '../store/usePortalStore';
import { ServiceCard } from './ServiceCard';
import { SshShelf } from './SshShelf';
type StoryId = 'layout-architecture' | 'theme-tokens' | 'service-card' | 'ssh-shelf';
export const StorybookWorkbench: React.FC = () => {
const [activeStory, setActiveStory] = useState<StoryId>('layout-architecture');
const {
theme,
resolvedTheme,
layoutPreset,
setTheme,
setLayoutPreset,
setViewMode,
telemetry,
} = usePortalStore();
// Interactive Playground Props for ServiceCard story
const [demoOnline, setDemoOnline] = useState(true);
const [demoAction, setDemoAction] = useState<'drawer' | 'backup' | undefined>('drawer');
const stories: Array<{ id: StoryId; group: string; title: string; icon: React.ReactNode }> = [
{
id: 'layout-architecture',
group: 'FOUNDATIONS / 架构与布局',
title: '01. AppShell 双层顶栏与网格布局',
icon: <Layout className="w-3.5 h-3.5" />,
},
{
id: 'theme-tokens',
group: 'FOUNDATIONS / 架构与布局',
title: '02. 双主题色板与自动切换规范',
icon: <Palette className="w-3.5 h-3.5" />,
},
{
id: 'service-card',
group: 'COMPONENTS / 核心组件',
title: '03. ServiceCard 内联遥测卡片',
icon: <Layers className="w-3.5 h-3.5" />,
},
{
id: 'ssh-shelf',
group: 'COMPONENTS / 核心组件',
title: '04. SshShelf 快捷指令栏',
icon: <Terminal className="w-3.5 h-3.5" />,
},
];
return (
<div className="flex-1 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 items-start">
{/* Left Sidebar: Storybook Navigation Tree */}
<aside className="hl-card p-4 space-y-4 lg:sticky lg:top-20">
<div className="flex items-center justify-between border-b hl-border pb-3">
<div className="flex items-center gap-2">
<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>
</div>
<div className="space-y-1">
{stories.map((s) => (
<button
key={s.id}
onClick={() => setActiveStory(s.id)}
className="w-full px-3 py-2 rounded-lg text-left text-xs font-medium flex items-center gap-2.5 transition-all cursor-pointer"
style={
activeStory === s.id
? {
backgroundColor: 'var(--accent-soft)',
color: 'var(--accent)',
}
: { color: 'var(--text-muted)' }
}
>
{s.icon}
<span className="truncate">{s.title}</span>
</button>
))}
</div>
<div className="pt-3 border-t hl-border space-y-2 text-xs">
<div className="text-[11px] font-semibold" style={{ color: 'var(--text-muted)' }}>
全局外观状态预览
</div>
<div className="grid grid-cols-3 gap-1 p-1 rounded-lg hl-muted border hl-border">
{(['system', 'light', 'dark'] as const).map((t) => (
<button
key={t}
onClick={() => setTheme(t)}
className="py-1 rounded text-[11px] font-medium cursor-pointer"
style={
theme === t
? { backgroundColor: 'var(--bg-card)', color: 'var(--text-main)' }
: { color: 'var(--text-muted)' }
}
>
{t === 'system' ? '自动' : t === 'light' ? '浅色' : '深色'}
</button>
))}
</div>
<button
onClick={() => setViewMode('dashboard')}
className="w-full hl-btn-primary py-2 text-xs text-center mt-2 cursor-pointer"
>
返回中控台主页
</button>
</div>
</aside>
{/* Right Canvas: Interactive Story Preview & Controls */}
<div className="lg:col-span-3 space-y-6">
{/* STORY 1: Layout Architecture */}
{activeStory === 'layout-architecture' && (
<div className="space-y-6">
<div className="hl-card p-6 space-y-4">
<div className="flex items-center justify-between border-b hl-border pb-4">
<div>
<span
className="text-[11px] font-mono-num uppercase tracking-wider"
style={{ color: 'var(--accent)' }}
>
Foundations / AppShell Layout
</span>
<h2 className="text-lg font-bold mt-0.5">
标准双层顶栏架构 (Two-Tier AppShell) & 响应式网格切换
</h2>
</div>
</div>
<p className="text-xs leading-relaxed" style={{ color: 'var(--text-muted)' }}>
参考 <strong>Vercel Geist / shadcn/ui Dashboard-01 / GitHub AppShell</strong>{' '}
标准规范,将原本容易挤压折行的单层顶栏拆分为职责清晰的双层结构:
</p>
{/* Visual Blueprint Diagram of Two-Tier Header */}
<div className="p-4 rounded-xl hl-muted border hl-border space-y-2.5 font-mono-num text-xs">
<div className="p-3 rounded-lg hl-card flex items-center justify-between">
<span>[左] 🍎 HomeLab · 导航切换 (中控台 / Storybook)</span>
<span style={{ color: 'var(--accent)' }}>
[最右固定] 🔍 搜索 ⌘K · &gt;_ AGY 终端 · ☀️/🌙 主题切换
</span>
</div>
<div className="p-3 rounded-lg hl-card flex items-center justify-between">
<span style={{ color: 'var(--success)' }}>
[左] 实时硬件遥测条 (M6 CPU · RAM · Z4 20TB · SG 专线)
</span>
<span>[右对齐] 路由模式切换器 (智能路由 / 443 / 10443 / LAN)</span>
</div>
</div>
{/* Interactive Grid Preset Selector */}
<div className="pt-2 space-y-3">
<div className="text-xs font-semibold">
选择主页卡片网格布局预设(点击立即应用到主页):
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3.5">
{(Object.keys(LAYOUT_PRESETS) as LayoutPresetId[]).map((key) => {
const preset = LAYOUT_PRESETS[key];
const active = layoutPreset === key;
return (
<div
key={key}
onClick={() => setLayoutPreset(key)}
className="hl-card hl-card-interactive p-4 cursor-pointer space-y-2"
style={active ? { borderColor: 'var(--accent)' } : {}}
>
<div className="flex items-center justify-between">
<span className="text-xs font-bold">{preset.name}</span>
{active && (
<span
className="w-4 h-4 rounded-full flex items-center justify-center"
style={{
backgroundColor: 'var(--accent)',
color: '#fff',
}}
>
<Check className="w-2.5 h-2.5" />
</span>
)}
</div>
<p className="text-[11px]" style={{ color: 'var(--text-muted)' }}>
{preset.description}
</p>
<code
className="text-[10px] font-mono-num block pt-1"
style={{ color: 'var(--accent)' }}
>
{preset.gridClass}
</code>
</div>
);
})}
</div>
</div>
</div>
</div>
)}
{/* STORY 2: Theme Tokens */}
{activeStory === 'theme-tokens' && (
<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)' }}
>
Foundations / Color System
</span>
<h2 className="text-lg font-bold mt-0.5">
主流双主题色板规范(当前生效:{resolvedTheme === 'dark' ? '深色极客' : '浅色纯净'})
</h2>
</div>
<span className="text-xs font-mono-num px-2.5 py-1 rounded hl-muted">
模式: {theme === 'system' ? '跟随设备自动切换' : '手动锁定'}
</span>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
{(['light', 'dark'] as const).map((modeKey) => {
const p = THEME_PALETTES[modeKey];
return (
<div
key={p.id}
className="rounded-xl p-5 space-y-4 border"
style={{
backgroundColor: p.tokens['--bg-page'],
color: p.tokens['--text-main'],
borderColor: p.tokens['--border-color'],
}}
>
<div
className="flex items-center justify-between border-b pb-3"
style={{ borderColor: p.tokens['--border-color'] }}
>
<div>
<div className="text-sm font-bold">{p.name}</div>
<div className="text-xs mt-0.5" style={{ color: p.tokens['--text-muted'] }}>
{p.description}
</div>
</div>
<button
onClick={() => setTheme(modeKey)}
className="px-2.5 py-1 rounded-md text-xs font-mono-num font-semibold cursor-pointer"
style={{
backgroundColor: p.tokens['--primary'],
color: p.tokens['--primary-fg'],
}}
>
预览此主题
</button>
</div>
<div className="grid grid-cols-2 gap-2">
{Object.entries(p.tokens).map(([tokenName, hex]) => (
<div
key={tokenName}
className="p-2 rounded-lg border flex items-center gap-2"
style={{
backgroundColor: p.tokens['--bg-card'],
borderColor: p.tokens['--border-color'],
}}
>
<span
className="w-5 h-5 rounded border shrink-0"
style={{
backgroundColor: hex,
borderColor: p.tokens['--border-hover'],
}}
/>
<div className="min-w-0">
<div className="text-[10px] font-mono-num font-semibold truncate">
{tokenName}
</div>
<div
className="text-[10px] font-mono-num truncate"
style={{ color: p.tokens['--text-muted'] }}
>
{hex}
</div>
</div>
</div>
))}
</div>
</div>
);
})}
</div>
</div>
)}
{/* STORY 3: 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">
<div>
<span
className="text-[11px] font-mono-num uppercase tracking-wider"
style={{ color: 'var(--accent)' }}
>
Components / ServiceCard
</span>
<h2 className="text-lg font-bold mt-0.5">
三段式内联遥测卡片 (ServiceCard Interactive Story)
</h2>
</div>
<Sparkles className="w-4 h-4" style={{ color: 'var(--accent)' }} />
</div>
{/* Interactive Controls */}
<div className="p-4 rounded-xl hl-muted border hl-border flex flex-wrap items-center gap-6 text-xs">
<div className="flex items-center gap-2">
<span className="font-semibold">在线状态 (online):</span>
<button
onClick={() => setDemoOnline(!demoOnline)}
className="px-2.5 py-1 rounded hl-card font-mono-num cursor-pointer"
>
{demoOnline ? 'true (绿灯)' : 'false (离线红灯)'}
</button>
</div>
<div className="flex items-center gap-2">
<span className="font-semibold">右上角动作按钮 (actionType):</span>
{(['drawer', 'backup', undefined] as const).map((act, i) => (
<button
key={i}
onClick={() => setDemoAction(act)}
className="px-2.5 py-1 rounded hl-card font-mono-num cursor-pointer"
style={demoAction === act ? { borderColor: 'var(--accent)' } : {}}
>
{act || 'route-badge'}
</button>
))}
</div>
</div>
{/* Live Component Canvas */}
<div className="max-w-md mx-auto py-4">
<ServiceCard
card={{
id: 'agy',
title: 'AGY 云端工作台',
subtitle: 'agy.961504.xyz · ttyd + tmux',
iconUrl:
'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/google-gemini.png',
online: demoOnline,
actionType: demoAction,
desc: 'M6 Mac mini 上的 24 小时常驻 AI 编程终端,已同步 Google 登录态并锁定新加坡出口,关掉网页后台继续运行。',
edgeUrl: 'https://agy.961504.xyz',
homeUrl: 'https://agy.home.961504.xyz:10443',
lanUrl: 'http://192.168.31.164:7681',
stats: [
{ label: 'M6 CPU 负载', value: `${telemetry.mini.cpu_pct}%` },
{ label: '已用内存', value: `${telemetry.mini.mem_used_gb} / 24 GB` },
{ label: 'tmux 会话', value: '常驻运行中', highlight: true },
],
}}
/>
</div>
</div>
)}
{/* STORY 4: SshShelf */}
{activeStory === 'ssh-shelf' && (
<div className="hl-card p-6 space-y-6">
<div className="border-b hl-border pb-4">
<span
className="text-[11px] font-mono-num uppercase tracking-wider"
style={{ color: 'var(--accent)' }}
>
Components / SshShelf
</span>
<h2 className="text-lg font-bold mt-0.5">底部 SSH 快捷复制与运维操作栏</h2>
</div>
<SshShelf />
</div>
)}
</div>
</div>
</div>
);
};
+144 -83
View File
@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Moon, Search, Sun, Terminal } from 'lucide-react'; import { BookOpen, LayoutGrid, Moon, Search, Sun, Terminal } from 'lucide-react';
import { usePortalStore } from '../store/usePortalStore'; import { usePortalStore } from '../store/usePortalStore';
import type { RouteModeId } from '../types/telemetry'; import type { RouteModeId } from '../types/telemetry';
@@ -15,23 +15,34 @@ export const TopRibbon: React.FC = () => {
theme, theme,
resolvedTheme, resolvedTheme,
routeMode, routeMode,
viewMode,
telemetry, telemetry,
setTheme, setTheme,
toggleTheme, toggleTheme,
setRouteMode, setRouteMode,
setViewMode,
setShowAgyModal, setShowAgyModal,
setShowCmdPalette, setShowCmdPalette,
} = usePortalStore(); } = usePortalStore();
return ( return (
<>
{/* =====================================================================
Tier 1: Standard AppShell Global Top Navbar (h-14, NEVER wraps!)
Left: Brand + View Tabs (中控台 / Storybook)
Far Right (Red Box): Search (Cmd+K) + AGY Terminal + Theme Switcher
===================================================================== */}
<header <header
className="border-b hl-border sticky top-0 z-30 backdrop-blur-md" className="border-b hl-border sticky top-0 z-30 backdrop-blur-md"
style={{ backgroundColor: 'color-mix(in srgb, var(--bg-page) 86%, transparent)' }} style={{ backgroundColor: 'color-mix(in srgb, var(--bg-card) 88%, transparent)' }}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-14 flex items-center justify-between gap-4">
{/* Left Group: Brand Logo + Navigation Tabs */}
<div className="flex items-center gap-4 shrink-0">
<div
onClick={() => setViewMode('dashboard')}
className="flex items-center gap-2.5 cursor-pointer"
> >
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3 flex flex-wrap items-center justify-between gap-3">
{/* Left: Brand + Live System Micro-Gauges Ribbon */}
<div className="flex flex-wrap items-center gap-2.5 sm:gap-3">
<div className="flex items-center gap-2.5 pr-1">
<img <img
src="https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/apple.png" src="https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/apple.png"
alt="Mac mini" alt="Mac mini"
@@ -45,66 +56,54 @@ export const TopRibbon: React.FC = () => {
/> />
</div> </div>
<div className="hidden xl:flex items-center gap-2 text-xs font-mono-num"> <div className="h-4 w-[1px] hl-muted" />
{/* CPU Gauge */}
<div className="hl-pill px-2.5 py-1 flex items-center gap-2" title="Apple M6 12核处理器负载"> {/* View Switcher Tabs: Dashboard vs Storybook */}
<span style={{ color: 'var(--text-muted)' }}>M6 CPU</span> <nav className="flex items-center gap-1 text-xs">
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden"> <button
<div onClick={() => setViewMode('dashboard')}
className="h-full hl-bar-fill" className="px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer"
style={{ width: `${Math.max(5, telemetry.mini.cpu_pct)}%` }} style={
/> viewMode === 'dashboard'
</div> ? {
<span className="font-medium">{telemetry.mini.cpu_pct}%</span> backgroundColor: 'var(--bg-muted)',
color: 'var(--text-main)',
}
: { color: 'var(--text-muted)' }
}
>
<LayoutGrid className="w-3.5 h-3.5" />
<span>中控台</span>
</button>
<button
onClick={() => setViewMode('storybook')}
className="px-2.5 py-1.5 rounded-md font-medium flex items-center gap-1.5 transition-all cursor-pointer"
style={
viewMode === 'storybook'
? {
backgroundColor: 'var(--bg-muted)',
color: 'var(--text-main)',
}
: { color: 'var(--text-muted)' }
}
>
<BookOpen className="w-3.5 h-3.5" />
<span>Storybook 设计规范</span>
</button>
</nav>
</div> </div>
{/* RAM Gauge */} {/* Far Right Group: Pinned to Right Edge (Search + AGY + Theme Switcher) */}
<div className="hl-pill px-2.5 py-1 flex items-center gap-2" title="Mac mini 24GB 统一内存"> <div className="flex items-center gap-2.5 shrink-0 ml-auto">
<span style={{ color: 'var(--text-muted)' }}>RAM</span>
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden">
<div
className="h-full hl-bar-fill"
style={{ width: `${Math.max(5, telemetry.mini.mem_pct)}%` }}
/>
</div>
<span className="font-medium">{telemetry.mini.mem_used_gb}G</span>
</div>
{/* ZSpace 20TB Gauge */}
<div className="hl-pill px-2.5 py-1 flex items-center gap-2" title="极空间 Z4 20TB 存储阵列">
<span style={{ color: 'var(--text-muted)' }}>NAS 余量</span>
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden">
<div
className="h-full hl-bar-fill-green"
style={{ width: `${Math.max(5, telemetry.nas.pct)}%` }}
/>
</div>
<span className="font-medium" style={{ color: 'var(--success)' }}>
{telemetry.nas.free_tb} TB
</span>
</div>
{/* SG AI Line Gauge */}
<div className="hl-pill px-2.5 py-1 flex items-center gap-1.5" title="AGY 锁定新加坡 Azure 专线出口">
<span className="w-1.5 h-1.5 rounded-full" style={{ backgroundColor: 'var(--success)' }} />
<span style={{ color: 'var(--text-muted)' }}>SG 专线</span>
<span className="font-medium">
{telemetry.network.sg_delay_ms > 0 ? `${telemetry.network.sg_delay_ms}ms` : '直连'}
</span>
</div>
</div>
</div>
{/* Right: Search (Cmd+K), Route Switcher, AGY Terminal, Top-Right Theme Switcher */}
<div className="flex items-center gap-2">
<button <button
onClick={() => setShowCmdPalette(true)} onClick={() => setShowCmdPalette(true)}
className="hl-btn-outline px-2.5 py-1 text-xs flex items-center gap-3 cursor-pointer" className="hl-btn-outline px-3 py-1.5 text-xs flex items-center gap-3 cursor-pointer"
title="按 Cmd+K 唤起全局命令面板" title="按 Cmd+K 唤起全局命令面板"
> >
<span className="flex items-center gap-1.5" style={{ color: 'var(--text-muted)' }}> <span className="flex items-center gap-1.5" style={{ color: 'var(--text-muted)' }}>
<Search className="w-3.5 h-3.5" /> <Search className="w-3.5 h-3.5" />
<span className="hidden sm:inline">搜索...</span> <span className="hidden sm:inline">搜索服务或指令...</span>
</span> </span>
<kbd <kbd
className="px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-muted" className="px-1.5 py-0.5 text-[10px] font-mono-num rounded hl-muted"
@@ -114,27 +113,6 @@ export const TopRibbon: React.FC = () => {
</kbd> </kbd>
</button> </button>
<div className="hidden md:flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs">
{MODES.map((m) => (
<button
key={m.id}
onClick={() => setRouteMode(m.id)}
className="px-2 py-1 rounded-md font-medium transition-all cursor-pointer"
style={
routeMode === m.id
? {
backgroundColor: 'var(--bg-card)',
color: 'var(--text-main)',
boxShadow: '0 1px 2px rgba(0,0,0,0.08)',
}
: { color: 'var(--text-muted)' }
}
>
{m.label}
</button>
))}
</div>
<button <button
onClick={() => setShowAgyModal(true)} onClick={() => setShowAgyModal(true)}
className="hl-btn-primary px-3 py-1.5 text-xs flex items-center gap-1.5 cursor-pointer" className="hl-btn-primary px-3 py-1.5 text-xs flex items-center gap-1.5 cursor-pointer"
@@ -143,7 +121,7 @@ export const TopRibbon: React.FC = () => {
<span>AGY 终端</span> <span>AGY 终端</span>
</button> </button>
{/* Top-Right Mainstream Theme Switcher (Auto System + Light/Dark 2 Mainstream Themes) */} {/* Top-Right Theme Toggle (Pinned at Far Right Corner!) */}
<div className="flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs"> <div className="flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs">
<button <button
onClick={toggleTheme} onClick={toggleTheme}
@@ -153,17 +131,17 @@ export const TopRibbon: React.FC = () => {
color: 'var(--text-main)', color: 'var(--text-main)',
boxShadow: '0 1px 2px rgba(0,0,0,0.08)', boxShadow: '0 1px 2px rgba(0,0,0,0.08)',
}} }}
title="点击切换深色 / 浅色主题" title="点击切换深色 / 浅色主题(默认跟随系统)"
> >
{resolvedTheme === 'dark' ? ( {resolvedTheme === 'dark' ? (
<> <>
<Moon className="w-3.5 h-3.5" /> <Moon className="w-3.5 h-3.5" />
<span className="hidden sm:inline">深色</span> <span>深色</span>
</> </>
) : ( ) : (
<> <>
<Sun className="w-3.5 h-3.5" /> <Sun className="w-3.5 h-3.5" />
<span className="hidden sm:inline">浅色</span> <span>浅色</span>
</> </>
)} )}
</button> </button>
@@ -181,5 +159,88 @@ export const TopRibbon: React.FC = () => {
</div> </div>
</div> </div>
</header> </header>
{/* =====================================================================
Tier 2: Subheader Telemetry & Route Mode Bar (Aligned with Grid)
Left: 4 Hardware & Network Gauges
Right: Route Mode Segmented Switcher (智能路由 / 443 / 10443 / LAN)
===================================================================== */}
{viewMode === 'dashboard' && (
<div className="border-b hl-border" style={{ backgroundColor: 'var(--bg-card)' }}>
<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">
{/* Left: System Telemetry Strip */}
<div className="flex flex-wrap items-center gap-2 text-xs font-mono-num">
<div className="hl-pill px-2.5 py-1 flex items-center gap-2">
<span style={{ color: 'var(--text-muted)' }}>M6 CPU</span>
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden">
<div
className="h-full hl-bar-fill"
style={{ width: `${Math.max(5, telemetry.mini.cpu_pct)}%` }}
/>
</div>
<span className="font-medium">{telemetry.mini.cpu_pct}%</span>
</div>
<div className="hl-pill px-2.5 py-1 flex items-center gap-2">
<span style={{ color: 'var(--text-muted)' }}>RAM</span>
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden">
<div
className="h-full hl-bar-fill"
style={{ width: `${Math.max(5, telemetry.mini.mem_pct)}%` }}
/>
</div>
<span className="font-medium">{telemetry.mini.mem_used_gb}G</span>
</div>
<div className="hl-pill px-2.5 py-1 flex items-center gap-2">
<span style={{ color: 'var(--text-muted)' }}>NAS 余量</span>
<div className="w-12 h-1.5 rounded-full hl-muted overflow-hidden">
<div
className="h-full hl-bar-fill-green"
style={{ width: `${Math.max(5, telemetry.nas.pct)}%` }}
/>
</div>
<span className="font-medium" style={{ color: 'var(--success)' }}>
{telemetry.nas.free_tb} TB
</span>
</div>
<div className="hl-pill px-2.5 py-1 flex items-center gap-1.5">
<span
className="w-1.5 h-1.5 rounded-full"
style={{ backgroundColor: 'var(--success)' }}
/>
<span style={{ color: 'var(--text-muted)' }}>SG 专线</span>
<span className="font-medium">
{telemetry.network.sg_delay_ms > 0 ? `${telemetry.network.sg_delay_ms}ms` : '直连'}
</span>
</div>
</div>
{/* Right: Route Mode Switcher (Pinned to Right Edge of Subheader) */}
<div className="flex items-center p-0.5 rounded-lg hl-muted border hl-border text-xs ml-auto">
{MODES.map((m) => (
<button
key={m.id}
onClick={() => setRouteMode(m.id)}
className="px-2.5 py-1 rounded-md font-medium transition-all cursor-pointer"
style={
routeMode === m.id
? {
backgroundColor: 'var(--bg-card)',
color: 'var(--text-main)',
boxShadow: '0 1px 2px rgba(0,0,0,0.08)',
}
: { color: 'var(--text-muted)' }
}
>
{m.label}
</button>
))}
</div>
</div>
</div>
)}
</>
); );
}; };
@@ -0,0 +1,135 @@
import React from 'react';
import { THEME_PALETTES, LAYOUT_PRESETS } from './tokens';
export const DesignTokensShowcase: React.FC = () => {
return (
<div className="p-6 sm:p-8 space-y-10 max-w-6xl mx-auto">
{/* Header */}
<div className="space-y-2 border-b hl-border pb-5">
<div className="text-xs font-mono-num uppercase tracking-widest" style={{ color: 'var(--accent)' }}>
HomeLab Design System · Storybook v3.0
</div>
<h1 className="text-2xl font-bold tracking-tight">主题色彩、排版与布局规范 (Design Tokens)</h1>
<p className="text-sm" style={{ color: 'var(--text-muted)' }}>
全系统仅保留两套高标准主流主题(浅色纯净 Light & 深色极客 Dark),默认通过{' '}
<code className="font-mono-num px-1.5 py-0.5 rounded hl-muted">prefers-color-scheme</code>{' '}
自动跟随设备切换,并支持右上角一键手动覆盖。
</p>
</div>
{/* Side-by-Side Theme Palettes */}
<section className="space-y-4">
<h2 className="text-base font-semibold">1. 双主题色彩变量对照表 (Dual Mainstream Palettes)</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{(['light', 'dark'] as const).map((key) => {
const p = THEME_PALETTES[key];
return (
<div
key={p.id}
className="rounded-xl p-5 space-y-4 border"
style={{
backgroundColor: p.tokens['--bg-page'],
color: p.tokens['--text-main'],
borderColor: p.tokens['--border-color'],
}}
>
<div className="flex items-center justify-between border-b pb-3" style={{ borderColor: p.tokens['--border-color'] }}>
<div>
<div className="text-sm font-bold">{p.name}</div>
<div className="text-xs mt-0.5" style={{ color: p.tokens['--text-muted'] }}>
{p.description}
</div>
</div>
<span
className="px-2.5 py-1 rounded-md text-xs font-mono-num font-semibold"
style={{
backgroundColor: p.tokens['--primary'],
color: p.tokens['--primary-fg'],
}}
>
{p.id.toUpperCase()}
</span>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2.5">
{Object.entries(p.tokens).map(([tokenName, hex]) => (
<div
key={tokenName}
className="p-2.5 rounded-lg border flex items-center gap-2.5"
style={{
backgroundColor: p.tokens['--bg-card'],
borderColor: p.tokens['--border-color'],
}}
>
<span
className="w-6 h-6 rounded-md border shrink-0"
style={{
backgroundColor: hex,
borderColor: p.tokens['--border-hover'],
}}
/>
<div className="min-w-0">
<div className="text-[11px] font-mono-num font-semibold truncate">{tokenName}</div>
<div className="text-[10px] font-mono-num truncate" style={{ color: p.tokens['--text-muted'] }}>
{hex}
</div>
</div>
</div>
))}
</div>
</div>
);
})}
</div>
</section>
{/* Typography Scale */}
<section className="space-y-4">
<h2 className="text-base font-semibold">2. 字体与等宽数字规范 (Typography & Tabular Numbers)</h2>
<div className="hl-card p-5 space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<div className="text-xs font-mono-num uppercase" style={{ color: 'var(--text-muted)' }}>
UI Sans Font · Inter / SF Pro Display
</div>
<div className="text-xl font-bold">AGY 云端工作台 · 极空间 Z4 私有云</div>
<p className="text-xs leading-relaxed" style={{ color: 'var(--text-muted)' }}>
正文描述统一采用 12px (0.75rem) 搭配 1.625 行高,限制最最多 2 行截断 (line-clamp-2),确保所有卡片高度严格对齐。
</p>
</div>
<div className="space-y-2">
<div className="text-xs font-mono-num uppercase" style={{ color: 'var(--text-muted)' }}>
Telemetry Mono Font · JetBrains Mono (tabular-nums)
</div>
<div className="text-xl font-bold font-mono-num" style={{ color: 'var(--success)' }}>
16.58 TB FREE · 68 ms · 104.43.105.243
</div>
<p className="text-xs leading-relaxed" style={{ color: 'var(--text-muted)' }}>
所有实时跳动的 CPU 百分比、内存、存储余量、网络延迟强制启用{' '}
<code className="font-mono-num">font-variant-numeric: tabular-nums</code>,杜绝每 3 秒刷新时产生像素抖动。
</p>
</div>
</div>
</div>
</section>
{/* Layout Grid Presets */}
<section className="space-y-4">
<h2 className="text-base font-semibold">3. 响应式布局网格预设 (Layout Grid Presets)</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{Object.values(LAYOUT_PRESETS).map((lp) => (
<div key={lp.id} className="hl-card p-4 space-y-2">
<div className="text-sm font-semibold">{lp.name}</div>
<code className="text-[11px] font-mono-num block" style={{ color: 'var(--accent)' }}>
{lp.gridClass}
</code>
<p className="text-xs" style={{ color: 'var(--text-muted)' }}>
{lp.description}
</p>
</div>
))}
</div>
</section>
</div>
);
};
@@ -0,0 +1,102 @@
export interface ThemePalette {
id: 'light' | 'dark';
name: string;
description: string;
tokens: {
'--bg-page': string;
'--bg-card': string;
'--bg-widget': string;
'--bg-muted': string;
'--bg-hover': string;
'--text-main': string;
'--text-muted': string;
'--text-subtle': string;
'--border-color': string;
'--border-hover': string;
'--primary': string;
'--primary-fg': string;
'--accent': string;
'--accent-soft': string;
'--success': string;
'--success-soft': string;
};
}
export const THEME_PALETTES: Record<'light' | 'dark', ThemePalette> = {
light: {
id: 'light',
name: 'Shadcn Clean Light (浅色纯净)',
description: '对标 Apple / Stripe / Linear 浅色日光模式,高对比深灰文字配纯净白卡片与冷灰微边框。',
tokens: {
'--bg-page': '#f8fafc',
'--bg-card': '#ffffff',
'--bg-widget': '#f8fafc',
'--bg-muted': '#f1f5f9',
'--bg-hover': '#ffffff',
'--text-main': '#0f172a',
'--text-muted': '#64748b',
'--text-subtle': '#94a3b8',
'--border-color': '#e2e8f0',
'--border-hover': '#cbd5e1',
'--primary': '#0f172a',
'--primary-fg': '#ffffff',
'--accent': '#2563eb',
'--accent-soft': 'rgba(37, 99, 235, 0.08)',
'--success': '#10b981',
'--success-soft': 'rgba(16, 185, 129, 0.10)',
},
},
dark: {
id: 'dark',
name: 'Catppuccin / Linear Dark (深色极客)',
description: '融合 Catppuccin Mocha 与 Linear 暗黑规范,柔和护眼的深邃蓝黑基底配高辨识度状态色。',
tokens: {
'--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-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)',
},
},
};
export type LayoutPresetId = 'homepage-3col' | 'bento-split' | 'compact-2col';
export interface LayoutPreset {
id: LayoutPresetId;
name: string;
description: string;
gridClass: string;
}
export const LAYOUT_PRESETS: Record<LayoutPresetId, LayoutPreset> = {
'homepage-3col': {
id: 'homepage-3col',
name: 'Homepage 经典三列矩阵 (3-Column Grid)',
description: 'gethomepage.dev 标志性对称 3 列分组卡片,底部内嵌 3 格实时遥测单元。',
gridClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4',
},
'bento-split': {
id: 'bento-split',
name: 'Shadcn 双列宽屏卡片 (2-Column Wide Bento)',
description: '适合大字号阅读的 2 列宽体卡片布局,留白更充裕。',
gridClass: 'grid-cols-1 lg:grid-cols-2 gap-5',
},
'compact-2col': {
id: 'compact-2col',
name: '高密度四列紧凑矩阵 (4-Column Dense)',
description: '适合超宽显示器的一屏高密度 4 列布局。',
gridClass: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3.5',
},
};
@@ -1,10 +1,13 @@
import { create } from 'zustand'; import { create } from 'zustand';
import type { RouteModeId, TelemetryData, ThemeId } from '../types/telemetry'; import type { RouteModeId, TelemetryData, ThemeId } from '../types/telemetry';
import type { LayoutPresetId } from '../design-system/tokens';
interface PortalState { interface PortalState {
theme: ThemeId; theme: ThemeId;
resolvedTheme: 'light' | 'dark'; resolvedTheme: 'light' | 'dark';
routeMode: RouteModeId; routeMode: RouteModeId;
layoutPreset: LayoutPresetId;
viewMode: 'dashboard' | 'storybook';
showAgyModal: boolean; showAgyModal: boolean;
showCmdPalette: boolean; showCmdPalette: boolean;
toast: string; toast: string;
@@ -13,6 +16,8 @@ interface PortalState {
setTheme: (theme: ThemeId) => void; setTheme: (theme: ThemeId) => void;
toggleTheme: () => void; toggleTheme: () => void;
setRouteMode: (mode: RouteModeId) => void; setRouteMode: (mode: RouteModeId) => void;
setLayoutPreset: (preset: LayoutPresetId) => void;
setViewMode: (view: 'dashboard' | 'storybook') => void;
setShowAgyModal: (open: boolean) => void; setShowAgyModal: (open: boolean) => void;
setShowCmdPalette: (open: boolean) => void; setShowCmdPalette: (open: boolean) => void;
showToast: (msg: string) => void; showToast: (msg: string) => void;
@@ -43,11 +48,19 @@ const initialTheme: ThemeId =
: 'system'; : 'system';
const initialResolved = applyDomTheme(initialTheme); const initialResolved = applyDomTheme(initialTheme);
const initialRouteMode = (localStorage.getItem('hl_route_mode') as RouteModeId) || 'auto'; const initialRouteMode = (localStorage.getItem('hl_route_mode') as RouteModeId) || 'auto';
const initialLayout =
(localStorage.getItem('hl_layout_preset') as LayoutPresetId) || 'homepage-3col';
const initialView =
typeof window !== 'undefined' && window.location.pathname.startsWith('/storybook')
? 'storybook'
: 'dashboard';
export const usePortalStore = create<PortalState>((set, get) => ({ export const usePortalStore = create<PortalState>((set, get) => ({
theme: initialTheme, theme: initialTheme,
resolvedTheme: initialResolved, resolvedTheme: initialResolved,
routeMode: initialRouteMode, routeMode: initialRouteMode,
layoutPreset: initialLayout,
viewMode: initialView,
showAgyModal: false, showAgyModal: false,
showCmdPalette: false, showCmdPalette: false,
toast: '', toast: '',
@@ -111,6 +124,19 @@ export const usePortalStore = create<PortalState>((set, get) => ({
set({ routeMode }); set({ routeMode });
}, },
setLayoutPreset: (layoutPreset) => {
localStorage.setItem('hl_layout_preset', layoutPreset);
set({ layoutPreset });
},
setViewMode: (viewMode) => {
if (typeof window !== 'undefined') {
const nextUrl = viewMode === 'storybook' ? '/storybook' : '/';
window.history.pushState({}, '', nextUrl);
}
set({ viewMode });
},
setShowAgyModal: (showAgyModal) => set({ showAgyModal }), setShowAgyModal: (showAgyModal) => set({ showAgyModal }),
setShowCmdPalette: (showCmdPalette) => set({ showCmdPalette }), setShowCmdPalette: (showCmdPalette) => set({ showCmdPalette }),