Files
links/static/dist/jbot.js
T
junvandCopilot a47ea316c7 feat(jbot): UI-configurable API keys + fix Dockerfile
- Add JbotApiConfig Django model (DB singleton) for storing
  Volcengine/OpenRouter credentials set via Settings UI
- New endpoint GET/POST /api/jbot/api-config/ — tokens masked as ***
  on read; only non-placeholder values are updated on write
- views.py: _get_cfg() helper reads DB first, env vars as fallback
- Settings.jsx: new 🔑 API 配置 section with show/hide token fields
  for VOLC App ID, Access Token, OpenRouter API Key, LLM Model, ASR Resource
- api.js: apiGetApiConfig() / apiSaveApiConfig() client helpers
- Dockerfile: COPY jbot/ in both builder + production stages (was missing)
- entrypoint.sh: runs migrate --noinput before gunicorn (auto-creates table)
- k8s/manifest.yaml: remove jbot-credentials secretKeyRef (no Secret needed);
  keep LLM_MODEL/VOLC_TTS_VOICE/VOLC_ASR_RESOURCE as optional env defaults

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00

13 lines
68 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{r as i,j as l,c as nn}from"./client-DS0SU7Jy.js";/**
* react-router v7.15.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/var mt="popstate";function pt(e){return typeof e=="object"&&e!=null&&"pathname"in e&&"search"in e&&"hash"in e&&"state"in e&&"key"in e}function rn(e={}){function t(r,a){var d;let s=(d=a.state)==null?void 0:d.masked,{pathname:o,search:u,hash:c}=s||r.location;return Ke("",{pathname:o,search:u,hash:c},a.state&&a.state.usr||null,a.state&&a.state.key||"default",s?{pathname:r.location.pathname,search:r.location.search,hash:r.location.hash}:void 0)}function n(r,a){return typeof a=="string"?a:be(a)}return sn(t,n,null,e)}function M(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function q(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function an(){return Math.random().toString(36).substring(2,10)}function gt(e,t){return{usr:e.state,key:e.key,idx:t,masked:e.mask?{pathname:e.pathname,search:e.search,hash:e.hash}:void 0}}function Ke(e,t,n=null,r,a){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?ue(t):t,state:n,key:t&&t.key||r||an(),mask:a}}function be({pathname:e="/",search:t="",hash:n=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),n&&n!=="#"&&(e+=n.charAt(0)==="#"?n:"#"+n),e}function ue(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function sn(e,t,n,r={}){let{window:a=document.defaultView,v5Compat:s=!1}=r,o=a.history,u="POP",c=null,d=f();d==null&&(d=0,o.replaceState({...o.state,idx:d},""));function f(){return(o.state||{idx:null}).idx}function h(){u="POP";let m=f(),y=m==null?null:m-d;d=m,c&&c({action:u,location:g.location,delta:y})}function p(m,y){u="PUSH";let w=pt(m)?m:Ke(g.location,m,y);d=f()+1;let x=gt(w,d),N=g.createHref(w.mask||w);try{o.pushState(x,"",N)}catch(I){if(I instanceof DOMException&&I.name==="DataCloneError")throw I;a.location.assign(N)}s&&c&&c({action:u,location:g.location,delta:1})}function b(m,y){u="REPLACE";let w=pt(m)?m:Ke(g.location,m,y);d=f();let x=gt(w,d),N=g.createHref(w.mask||w);o.replaceState(x,"",N),s&&c&&c({action:u,location:g.location,delta:0})}function v(m){return on(m)}let g={get action(){return u},get location(){return e(a,o)},listen(m){if(c)throw new Error("A history only accepts one active listener");return a.addEventListener(mt,h),c=m,()=>{a.removeEventListener(mt,h),c=null}},createHref(m){return t(a,m)},createURL:v,encodeLocation(m){let y=v(m);return{pathname:y.pathname,search:y.search,hash:y.hash}},push:p,replace:b,go(m){return o.go(m)}};return g}function on(e,t=!1){let n="http://localhost";typeof window<"u"&&(n=window.location.origin!=="null"?window.location.origin:window.location.href),M(n,"No window.location.(origin|href) available to create URL");let r=typeof e=="string"?e:be(e);return r=r.replace(/ $/,"%20"),!t&&r.startsWith("//")&&(r=n+r),new URL(r,n)}function Tt(e,t,n="/"){return ln(e,t,n,!1)}function ln(e,t,n,r,a){let s=typeof t=="string"?ue(t):t,o=Z(s.pathname||"/",n);if(o==null)return null;let u=un(e),c=null,d=xn(o);for(let f=0;c==null&&f<u.length;++f)c=bn(u[f],d,r);return c}function un(e){let t=Pt(e);return cn(t),t}function Pt(e,t=[],n=[],r="",a=!1){let s=(o,u,c=a,d)=>{let f={relativePath:d===void 0?o.path||"":d,caseSensitive:o.caseSensitive===!0,childrenIndex:u,route:o};if(f.relativePath.startsWith("/")){if(!f.relativePath.startsWith(r)&&c)return;M(f.relativePath.startsWith(r),`Absolute route path "${f.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),f.relativePath=f.relativePath.slice(r.length)}let h=V([r,f.relativePath]),p=n.concat(f);o.children&&o.children.length>0&&(M(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${h}".`),Pt(o.children,t,p,h,c)),!(o.path==null&&!o.index)&&t.push({path:h,score:yn(h,o.index),routesMeta:p})};return e.forEach((o,u)=>{var c;if(o.path===""||!((c=o.path)!=null&&c.includes("?")))s(o,u);else for(let d of Mt(o.path))s(o,u,!0,d)}),t}function Mt(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,a=n.endsWith("?"),s=n.replace(/\?$/,"");if(r.length===0)return a?[s,""]:[s];let o=Mt(r.join("/")),u=[];return u.push(...o.map(c=>c===""?s:[s,c].join("/"))),a&&u.push(...o),u.map(c=>e.startsWith("/")&&c===""?"/":c)}function cn(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:vn(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}var dn=/^:[\w-]+$/,hn=3,fn=2,mn=1,pn=10,gn=-2,yt=e=>e==="*";function yn(e,t){let n=e.split("/"),r=n.length;return n.some(yt)&&(r+=gn),t&&(r+=fn),n.filter(a=>!yt(a)).reduce((a,s)=>a+(dn.test(s)?hn:s===""?mn:pn),r)}function vn(e,t){return e.length===t.length&&e.slice(0,-1).every((r,a)=>r===t[a])?e[e.length-1]-t[t.length-1]:0}function bn(e,t,n=!1){let{routesMeta:r}=e,a={},s="/",o=[];for(let u=0;u<r.length;++u){let c=r[u],d=u===r.length-1,f=s==="/"?t:t.slice(s.length)||"/",h=Ie({path:c.relativePath,caseSensitive:c.caseSensitive,end:d},f),p=c.route;if(!h&&d&&n&&!r[r.length-1].route.index&&(h=Ie({path:c.relativePath,caseSensitive:c.caseSensitive,end:!1},f)),!h)return null;Object.assign(a,h.params),o.push({params:a,pathname:V([s,h.pathname]),pathnameBase:kn(V([s,h.pathnameBase])),route:p}),h.pathnameBase!=="/"&&(s=V([s,h.pathnameBase]))}return o}function Ie(e,t){typeof e=="string"&&(e={path:e,caseSensitive:!1,end:!0});let[n,r]=wn(e.path,e.caseSensitive,e.end),a=t.match(n);if(!a)return null;let s=a[0],o=s.replace(/(.)\/+$/,"$1"),u=a.slice(1);return{params:r.reduce((d,{paramName:f,isOptional:h},p)=>{if(f==="*"){let v=u[p]||"";o=s.slice(0,s.length-v.length).replace(/(.)\/+$/,"$1")}const b=u[p];return h&&!b?d[f]=void 0:d[f]=(b||"").replace(/%2F/g,"/"),d},{}),pathname:s,pathnameBase:o,pattern:e}}function wn(e,t=!1,n=!0){q(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(o,u,c,d,f)=>{if(r.push({paramName:u,isOptional:c!=null}),c){let h=f.charAt(d+o.length);return h&&h!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(r.push({paramName:"*"}),a+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":e!==""&&e!=="/"&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function xn(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return q(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function Z(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}var En=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function Rn(e,t="/"){let{pathname:n,search:r="",hash:a=""}=typeof e=="string"?ue(e):e,s;return n?(n=At(n),n.startsWith("/")?s=vt(n.substring(1),"/"):s=vt(n,t)):s=t,{pathname:s,search:Cn(r),hash:Nn(a)}}function vt(e,t){let n=Ae(t).split("/");return e.split("/").forEach(a=>{a===".."?n.length>1&&n.pop():a!=="."&&n.push(a)}),n.length>1?n.join("/"):"/"}function He(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`}function Sn(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function It(e){let t=Sn(e);return t.map((n,r)=>r===t.length-1?n.pathname:n.pathnameBase)}function Ze(e,t,n,r=!1){let a;typeof e=="string"?a=ue(e):(a={...e},M(!a.pathname||!a.pathname.includes("?"),He("?","pathname","search",a)),M(!a.pathname||!a.pathname.includes("#"),He("#","pathname","hash",a)),M(!a.search||!a.search.includes("#"),He("#","search","hash",a)));let s=e===""||a.pathname==="",o=s?"/":a.pathname,u;if(o==null)u=n;else{let h=t.length-1;if(!r&&o.startsWith("..")){let p=o.split("/");for(;p[0]==="..";)p.shift(),h-=1;a.pathname=p.join("/")}u=h>=0?t[h]:"/"}let c=Rn(a,u),d=o&&o!=="/"&&o.endsWith("/"),f=(s||o===".")&&n.endsWith("/");return!c.pathname.endsWith("/")&&(d||f)&&(c.pathname+="/"),c}var At=e=>e.replace(/\/\/+/g,"/"),V=e=>At(e.join("/")),Ae=e=>e.replace(/\/+$/,""),kn=e=>Ae(e).replace(/^\/*/,"/"),Cn=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Nn=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e,jn=class{constructor(e,t,n,r=!1){this.status=e,this.statusText=t||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function Tn(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}function Pn(e){let t=e.map(n=>n.route.path).filter(Boolean);return V(t)||"/"}var Lt=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function $t(e,t){let n=e;if(typeof n!="string"||!En.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,a=!1;if(Lt)try{let s=new URL(window.location.href),o=n.startsWith("//")?new URL(s.protocol+n):new URL(n),u=Z(o.pathname,t);o.origin===s.origin&&u!=null?n=u+o.search+o.hash:a=!0}catch{q(!1,`<Link to="${n}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:a,to:n}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var _t=["POST","PUT","PATCH","DELETE"];new Set(_t);var Mn=["GET",..._t];new Set(Mn);var ce=i.createContext(null);ce.displayName="DataRouter";var $e=i.createContext(null);$e.displayName="DataRouterState";var Bt=i.createContext(!1);function In(){return i.useContext(Bt)}var Ft=i.createContext({isTransitioning:!1});Ft.displayName="ViewTransition";var An=i.createContext(new Map);An.displayName="Fetchers";var Ln=i.createContext(null);Ln.displayName="Await";var H=i.createContext(null);H.displayName="Navigation";var we=i.createContext(null);we.displayName="Location";var ee=i.createContext({outlet:null,matches:[],isDataRoute:!1});ee.displayName="Route";var et=i.createContext(null);et.displayName="RouteError";var Ot="REACT_ROUTER_ERROR",$n="REDIRECT",_n="ROUTE_ERROR_RESPONSE";function Bn(e){if(e.startsWith(`${Ot}:${$n}:{`))try{let t=JSON.parse(e.slice(28));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string"&&typeof t.location=="string"&&typeof t.reloadDocument=="boolean"&&typeof t.replace=="boolean")return t}catch{}}function Fn(e){if(e.startsWith(`${Ot}:${_n}:{`))try{let t=JSON.parse(e.slice(40));if(typeof t=="object"&&t&&typeof t.status=="number"&&typeof t.statusText=="string")return new jn(t.status,t.statusText,t.data)}catch{}}function On(e,{relative:t}={}){M(xe(),"useHref() may be used only in the context of a <Router> component.");let{basename:n,navigator:r}=i.useContext(H),{hash:a,pathname:s,search:o}=Re(e,{relative:t}),u=s;return n!=="/"&&(u=s==="/"?n:V([n,s])),r.createHref({pathname:u,search:o,hash:a})}function xe(){return i.useContext(we)!=null}function te(){return M(xe(),"useLocation() may be used only in the context of a <Router> component."),i.useContext(we).location}var Dt="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Ut(e){i.useContext(H).static||i.useLayoutEffect(e)}function Ee(){let{isDataRoute:e}=i.useContext(ee);return e?Qn():Dn()}function Dn(){M(xe(),"useNavigate() may be used only in the context of a <Router> component.");let e=i.useContext(ce),{basename:t,navigator:n}=i.useContext(H),{matches:r}=i.useContext(ee),{pathname:a}=te(),s=JSON.stringify(It(r)),o=i.useRef(!1);return Ut(()=>{o.current=!0}),i.useCallback((c,d={})=>{if(q(o.current,Dt),!o.current)return;if(typeof c=="number"){n.go(c);return}let f=Ze(c,JSON.parse(s),a,d.relative==="path");e==null&&t!=="/"&&(f.pathname=f.pathname==="/"?t:V([t,f.pathname])),(d.replace?n.replace:n.push)(f,d.state,d)},[t,n,s,a,e])}i.createContext(null);function Re(e,{relative:t}={}){let{matches:n}=i.useContext(ee),{pathname:r}=te(),a=JSON.stringify(It(n));return i.useMemo(()=>Ze(e,JSON.parse(a),r,t==="path"),[e,a,r,t])}function Un(e,t){return zt(e,t)}function zt(e,t,n){var m;M(xe(),"useRoutes() may be used only in the context of a <Router> component.");let{navigator:r}=i.useContext(H),{matches:a}=i.useContext(ee),s=a[a.length-1],o=s?s.params:{},u=s?s.pathname:"/",c=s?s.pathnameBase:"/",d=s&&s.route;{let y=d&&d.path||"";Wt(u,!d||y.endsWith("*")||y.endsWith("*?"),`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${u}" (under <Route path="${y}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
Please change the parent <Route path="${y}"> to <Route path="${y==="/"?"*":`${y}/*`}">.`)}let f=te(),h;if(t){let y=typeof t=="string"?ue(t):t;M(c==="/"||((m=y.pathname)==null?void 0:m.startsWith(c)),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${c}" but pathname "${y.pathname}" was given in the \`location\` prop.`),h=y}else h=f;let p=h.pathname||"/",b=p;if(c!=="/"){let y=c.replace(/^\//,"").split("/");b="/"+p.replace(/^\//,"").split("/").slice(y.length).join("/")}let v=n&&n.state.matches.length?n.state.matches.map(y=>Object.assign(y,{route:n.manifest[y.route.id]||y.route})):Tt(e,{pathname:b});q(d||v!=null,`No routes matched location "${h.pathname}${h.search}${h.hash}" `),q(v==null||v[v.length-1].route.element!==void 0||v[v.length-1].route.Component!==void 0||v[v.length-1].route.lazy!==void 0,`Matched leaf route at location "${h.pathname}${h.search}${h.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let g=Jn(v&&v.map(y=>Object.assign({},y,{params:Object.assign({},o,y.params),pathname:V([c,r.encodeLocation?r.encodeLocation(y.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:y.pathname]),pathnameBase:y.pathnameBase==="/"?c:V([c,r.encodeLocation?r.encodeLocation(y.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:y.pathnameBase])})),a,n);return t&&g?i.createElement(we.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...h},navigationType:"POP"}},g):g}function zn(){let e=Xn(),t=Tn(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",a={padding:"0.5rem",backgroundColor:r},s={padding:"2px 4px",backgroundColor:r},o=null;return console.error("Error handled by React Router default ErrorBoundary:",e),o=i.createElement(i.Fragment,null,i.createElement("p",null,"💿 Hey developer 👋"),i.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",i.createElement("code",{style:s},"ErrorBoundary")," or"," ",i.createElement("code",{style:s},"errorElement")," prop on your route.")),i.createElement(i.Fragment,null,i.createElement("h2",null,"Unexpected Application Error!"),i.createElement("h3",{style:{fontStyle:"italic"}},t),n?i.createElement("pre",{style:a},n):null,o)}var Hn=i.createElement(zn,null),Ht=class extends i.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error("React Router caught the following error during render",e)}render(){let e=this.state.error;if(this.context&&typeof e=="object"&&e&&"digest"in e&&typeof e.digest=="string"){const n=Fn(e.digest);n&&(e=n)}let t=e!==void 0?i.createElement(ee.Provider,{value:this.props.routeContext},i.createElement(et.Provider,{value:e,children:this.props.component})):this.props.children;return this.context?i.createElement(Wn,{error:e},t):t}};Ht.contextType=Bt;var We=new WeakMap;function Wn({children:e,error:t}){let{basename:n}=i.useContext(H);if(typeof t=="object"&&t&&"digest"in t&&typeof t.digest=="string"){let r=Bn(t.digest);if(r){let a=We.get(t);if(a)throw a;let s=$t(r.location,n);if(Lt&&!We.get(t))if(s.isExternal||r.reloadDocument)window.location.href=s.absoluteURL||s.to;else{const o=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(s.to,{replace:r.replace}));throw We.set(t,o),o}return i.createElement("meta",{httpEquiv:"refresh",content:`0;url=${s.absoluteURL||s.to}`})}}return e}function Vn({routeContext:e,match:t,children:n}){let r=i.useContext(ce);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),i.createElement(ee.Provider,{value:e},n)}function Jn(e,t=[],n){let r=n==null?void 0:n.state;if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let a=e,s=r==null?void 0:r.errors;if(s!=null){let f=a.findIndex(h=>h.route.id&&(s==null?void 0:s[h.route.id])!==void 0);M(f>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(s).join(",")}`),a=a.slice(0,Math.min(a.length,f+1))}let o=!1,u=-1;if(n&&r){o=r.renderFallback;for(let f=0;f<a.length;f++){let h=a[f];if((h.route.HydrateFallback||h.route.hydrateFallbackElement)&&(u=f),h.route.id){let{loaderData:p,errors:b}=r,v=h.route.loader&&!p.hasOwnProperty(h.route.id)&&(!b||b[h.route.id]===void 0);if(h.route.lazy||v){n.isStatic&&(o=!0),u>=0?a=a.slice(0,u+1):a=[a[0]];break}}}}let c=n==null?void 0:n.onError,d=r&&c?(f,h)=>{var p,b;c(f,{location:r.location,params:((b=(p=r.matches)==null?void 0:p[0])==null?void 0:b.params)??{},pattern:Pn(r.matches),errorInfo:h})}:void 0;return a.reduceRight((f,h,p)=>{let b,v=!1,g=null,m=null;r&&(b=s&&h.route.id?s[h.route.id]:void 0,g=h.route.errorElement||Hn,o&&(u<0&&p===0?(Wt("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),v=!0,m=null):u===p&&(v=!0,m=h.route.hydrateFallbackElement||null)));let y=t.concat(a.slice(0,p+1)),w=()=>{let x;return b?x=g:v?x=m:h.route.Component?x=i.createElement(h.route.Component,null):h.route.element?x=h.route.element:x=f,i.createElement(Vn,{match:h,routeContext:{outlet:f,matches:y,isDataRoute:r!=null},children:x})};return r&&(h.route.ErrorBoundary||h.route.errorElement||p===0)?i.createElement(Ht,{location:r.location,revalidation:r.revalidation,component:g,error:b,children:w(),routeContext:{outlet:null,matches:y,isDataRoute:!0},onError:d}):w()},null)}function tt(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Kn(e){let t=i.useContext(ce);return M(t,tt(e)),t}function qn(e){let t=i.useContext($e);return M(t,tt(e)),t}function Gn(e){let t=i.useContext(ee);return M(t,tt(e)),t}function nt(e){let t=Gn(e),n=t.matches[t.matches.length-1];return M(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}function Yn(){return nt("useRouteId")}function Xn(){var r;let e=i.useContext(et),t=qn("useRouteError"),n=nt("useRouteError");return e!==void 0?e:(r=t.errors)==null?void 0:r[n]}function Qn(){let{router:e}=Kn("useNavigate"),t=nt("useNavigate"),n=i.useRef(!1);return Ut(()=>{n.current=!0}),i.useCallback(async(a,s={})=>{q(n.current,Dt),n.current&&(typeof a=="number"?await e.navigate(a):await e.navigate(a,{fromRouteId:t,...s}))},[e,t])}var bt={};function Wt(e,t,n){!t&&!bt[e]&&(bt[e]=!0,q(!1,n))}i.memo(Zn);function Zn({routes:e,manifest:t,future:n,state:r,isStatic:a,onError:s}){return zt(e,void 0,{manifest:t,state:r,isStatic:a,onError:s})}function Te(e){M(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function er({basename:e="/",children:t=null,location:n,navigationType:r="POP",navigator:a,static:s=!1,useTransitions:o}){M(!xe(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let u=e.replace(/^\/*/,"/"),c=i.useMemo(()=>({basename:u,navigator:a,static:s,useTransitions:o,future:{}}),[u,a,s,o]);typeof n=="string"&&(n=ue(n));let{pathname:d="/",search:f="",hash:h="",state:p=null,key:b="default",mask:v}=n,g=i.useMemo(()=>{let m=Z(d,u);return m==null?null:{location:{pathname:m,search:f,hash:h,state:p,key:b,mask:v},navigationType:r}},[u,d,f,h,p,b,r,v]);return q(g!=null,`<Router basename="${u}"> is not able to match the URL "${d}${f}${h}" because it does not start with the basename, so the <Router> won't render anything.`),g==null?null:i.createElement(H.Provider,{value:c},i.createElement(we.Provider,{children:t,value:g}))}function tr({children:e,location:t}){return Un(qe(e),t)}function qe(e,t=[]){let n=[];return i.Children.forEach(e,(r,a)=>{if(!i.isValidElement(r))return;let s=[...t,a];if(r.type===i.Fragment){n.push.apply(n,qe(r.props.children,s));return}M(r.type===Te,`[${typeof r.type=="string"?r.type:r.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),M(!r.props.index||!r.props.children,"An index route cannot have child routes.");let o={id:r.props.id||s.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(o.children=qe(r.props.children,s)),n.push(o)}),n}var Pe="get",Me="application/x-www-form-urlencoded";function _e(e){return typeof HTMLElement<"u"&&e instanceof HTMLElement}function nr(e){return _e(e)&&e.tagName.toLowerCase()==="button"}function rr(e){return _e(e)&&e.tagName.toLowerCase()==="form"}function ar(e){return _e(e)&&e.tagName.toLowerCase()==="input"}function sr(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function or(e,t){return e.button===0&&(!t||t==="_self")&&!sr(e)}var Ce=null;function ir(){if(Ce===null)try{new FormData(document.createElement("form"),0),Ce=!1}catch{Ce=!0}return Ce}var lr=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Ve(e){return e!=null&&!lr.has(e)?(q(!1,`"${e}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${Me}"`),null):e}function ur(e,t){let n,r,a,s,o;if(rr(e)){let u=e.getAttribute("action");r=u?Z(u,t):null,n=e.getAttribute("method")||Pe,a=Ve(e.getAttribute("enctype"))||Me,s=new FormData(e)}else if(nr(e)||ar(e)&&(e.type==="submit"||e.type==="image")){let u=e.form;if(u==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let c=e.getAttribute("formaction")||u.getAttribute("action");if(r=c?Z(c,t):null,n=e.getAttribute("formmethod")||u.getAttribute("method")||Pe,a=Ve(e.getAttribute("formenctype"))||Ve(u.getAttribute("enctype"))||Me,s=new FormData(u,e),!ir()){let{name:d,type:f,value:h}=e;if(f==="image"){let p=d?`${d}.`:"";s.append(`${p}x`,"0"),s.append(`${p}y`,"0")}else d&&s.append(d,h)}}else{if(_e(e))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');n=Pe,r=null,a=Me,o=e}return s&&a==="text/plain"&&(o=s,s=void 0),{action:r,method:n.toLowerCase(),encType:a,formData:s,body:o}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function rt(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function Vt(e,t,n,r){let a=typeof e=="string"?new URL(e,typeof window>"u"?"server://singlefetch/":window.location.origin):e;return n?a.pathname.endsWith("/")?a.pathname=`${a.pathname}_.${r}`:a.pathname=`${a.pathname}.${r}`:a.pathname==="/"?a.pathname=`_root.${r}`:t&&Z(a.pathname,t)==="/"?a.pathname=`${Ae(t)}/_root.${r}`:a.pathname=`${Ae(a.pathname)}.${r}`,a}async function cr(e,t){if(e.id in t)return t[e.id];try{let n=await import(e.module);return t[e.id]=n,n}catch(n){return console.error(`Error loading route module \`${e.module}\`, reloading page...`),console.error(n),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function dr(e){return e==null?!1:e.href==null?e.rel==="preload"&&typeof e.imageSrcSet=="string"&&typeof e.imageSizes=="string":typeof e.rel=="string"&&typeof e.href=="string"}async function hr(e,t,n){let r=await Promise.all(e.map(async a=>{let s=t.routes[a.route.id];if(s){let o=await cr(s,n);return o.links?o.links():[]}return[]}));return gr(r.flat(1).filter(dr).filter(a=>a.rel==="stylesheet"||a.rel==="preload").map(a=>a.rel==="stylesheet"?{...a,rel:"prefetch",as:"style"}:{...a,rel:"prefetch"}))}function wt(e,t,n,r,a,s){let o=(c,d)=>n[d]?c.route.id!==n[d].route.id:!0,u=(c,d)=>{var f;return n[d].pathname!==c.pathname||((f=n[d].route.path)==null?void 0:f.endsWith("*"))&&n[d].params["*"]!==c.params["*"]};return s==="assets"?t.filter((c,d)=>o(c,d)||u(c,d)):s==="data"?t.filter((c,d)=>{var h;let f=r.routes[c.route.id];if(!f||!f.hasLoader)return!1;if(o(c,d)||u(c,d))return!0;if(c.route.shouldRevalidate){let p=c.route.shouldRevalidate({currentUrl:new URL(a.pathname+a.search+a.hash,window.origin),currentParams:((h=n[0])==null?void 0:h.params)||{},nextUrl:new URL(e,window.origin),nextParams:c.params,defaultShouldRevalidate:!0});if(typeof p=="boolean")return p}return!0}):[]}function fr(e,t,{includeHydrateFallback:n}={}){return mr(e.map(r=>{let a=t.routes[r.route.id];if(!a)return[];let s=[a.module];return a.clientActionModule&&(s=s.concat(a.clientActionModule)),a.clientLoaderModule&&(s=s.concat(a.clientLoaderModule)),n&&a.hydrateFallbackModule&&(s=s.concat(a.hydrateFallbackModule)),a.imports&&(s=s.concat(a.imports)),s}).flat(1))}function mr(e){return[...new Set(e)]}function pr(e){let t={},n=Object.keys(e).sort();for(let r of n)t[r]=e[r];return t}function gr(e,t){let n=new Set;return new Set(t),e.reduce((r,a)=>{let s=JSON.stringify(pr(a));return n.has(s)||(n.add(s),r.push({key:s,link:a})),r},[])}function at(){let e=i.useContext(ce);return rt(e,"You must render this element inside a <DataRouterContext.Provider> element"),e}function yr(){let e=i.useContext($e);return rt(e,"You must render this element inside a <DataRouterStateContext.Provider> element"),e}var st=i.createContext(void 0);st.displayName="FrameworkContext";function ot(){let e=i.useContext(st);return rt(e,"You must render this element inside a <HydratedRouter> element"),e}function vr(e,t){let n=i.useContext(st),[r,a]=i.useState(!1),[s,o]=i.useState(!1),{onFocus:u,onBlur:c,onMouseEnter:d,onMouseLeave:f,onTouchStart:h}=t,p=i.useRef(null);i.useEffect(()=>{if(e==="render"&&o(!0),e==="viewport"){let g=y=>{y.forEach(w=>{o(w.isIntersecting)})},m=new IntersectionObserver(g,{threshold:.5});return p.current&&m.observe(p.current),()=>{m.disconnect()}}},[e]),i.useEffect(()=>{if(r){let g=setTimeout(()=>{o(!0)},100);return()=>{clearTimeout(g)}}},[r]);let b=()=>{a(!0)},v=()=>{a(!1),o(!1)};return n?e!=="intent"?[s,p,{}]:[s,p,{onFocus:me(u,b),onBlur:me(c,v),onMouseEnter:me(d,b),onMouseLeave:me(f,v),onTouchStart:me(h,b)}]:[!1,p,{}]}function me(e,t){return n=>{e&&e(n),n.defaultPrevented||t(n)}}function br({page:e,...t}){let n=In(),{router:r}=at(),a=i.useMemo(()=>Tt(r.routes,e,r.basename),[r.routes,e,r.basename]);return a?n?i.createElement(xr,{page:e,matches:a,...t}):i.createElement(Er,{page:e,matches:a,...t}):null}function wr(e){let{manifest:t,routeModules:n}=ot(),[r,a]=i.useState([]);return i.useEffect(()=>{let s=!1;return hr(e,t,n).then(o=>{s||a(o)}),()=>{s=!0}},[e,t,n]),r}function xr({page:e,matches:t,...n}){let r=te(),{future:a}=ot(),{basename:s}=at(),o=i.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let u=Vt(e,s,a.unstable_trailingSlashAwareDataRequests,"rsc"),c=!1,d=[];for(let f of t)typeof f.route.shouldRevalidate=="function"?c=!0:d.push(f.route.id);return c&&d.length>0&&u.searchParams.set("_routes",d.join(",")),[u.pathname+u.search]},[s,a.unstable_trailingSlashAwareDataRequests,e,r,t]);return i.createElement(i.Fragment,null,o.map(u=>i.createElement("link",{key:u,rel:"prefetch",as:"fetch",href:u,...n})))}function Er({page:e,matches:t,...n}){let r=te(),{future:a,manifest:s,routeModules:o}=ot(),{basename:u}=at(),{loaderData:c,matches:d}=yr(),f=i.useMemo(()=>wt(e,t,d,s,r,"data"),[e,t,d,s,r]),h=i.useMemo(()=>wt(e,t,d,s,r,"assets"),[e,t,d,s,r]),p=i.useMemo(()=>{if(e===r.pathname+r.search+r.hash)return[];let g=new Set,m=!1;if(t.forEach(w=>{var N;let x=s.routes[w.route.id];!x||!x.hasLoader||(!f.some(I=>I.route.id===w.route.id)&&w.route.id in c&&((N=o[w.route.id])!=null&&N.shouldRevalidate)||x.hasClientLoader?m=!0:g.add(w.route.id))}),g.size===0)return[];let y=Vt(e,u,a.unstable_trailingSlashAwareDataRequests,"data");return m&&g.size>0&&y.searchParams.set("_routes",t.filter(w=>g.has(w.route.id)).map(w=>w.route.id).join(",")),[y.pathname+y.search]},[u,a.unstable_trailingSlashAwareDataRequests,c,r,s,f,t,e,o]),b=i.useMemo(()=>fr(h,s),[h,s]),v=wr(h);return i.createElement(i.Fragment,null,p.map(g=>i.createElement("link",{key:g,rel:"prefetch",as:"fetch",href:g,...n})),b.map(g=>i.createElement("link",{key:g,rel:"modulepreload",href:g,...n})),v.map(({key:g,link:m})=>i.createElement("link",{key:g,nonce:n.nonce,...m,crossOrigin:m.crossOrigin??n.crossOrigin})))}function Rr(...e){return t=>{e.forEach(n=>{typeof n=="function"?n(t):n!=null&&(n.current=t)})}}var Sr=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{Sr&&(window.__reactRouterVersion="7.15.1")}catch{}function kr({basename:e,children:t,useTransitions:n,window:r}){let a=i.useRef();a.current==null&&(a.current=rn({window:r,v5Compat:!0}));let s=a.current,[o,u]=i.useState({action:s.action,location:s.location}),c=i.useCallback(d=>{n===!1?u(d):i.startTransition(()=>u(d))},[n]);return i.useLayoutEffect(()=>s.listen(c),[s,c]),i.createElement(er,{basename:e,children:t,location:o.location,navigationType:o.action,navigator:s,useTransitions:n})}var Jt=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Kt=i.forwardRef(function({onClick:t,discover:n="render",prefetch:r="none",relative:a,reloadDocument:s,replace:o,mask:u,state:c,target:d,to:f,preventScrollReset:h,viewTransition:p,defaultShouldRevalidate:b,...v},g){let{basename:m,navigator:y,useTransitions:w}=i.useContext(H),x=typeof f=="string"&&Jt.test(f),N=$t(f,m);f=N.to;let I=On(f,{relative:a}),j=te(),J=null;if(u){let W=Ze(u,[],j.mask?j.mask.pathname:"/",!0);m!=="/"&&(W.pathname=W.pathname==="/"?m:V([m,W.pathname])),J=y.createHref(W)}let[ne,re,he]=vr(r,v),De=Tr(f,{replace:o,mask:u,state:c,target:d,preventScrollReset:h,relative:a,viewTransition:p,defaultShouldRevalidate:b,useTransitions:w});function Ue(W){t&&t(W),W.defaultPrevented||De(W)}let ae=!(N.isExternal||s),se=i.createElement("a",{...v,...he,href:(ae?J:void 0)||N.absoluteURL||I,onClick:ae?Ue:t,ref:Rr(g,re),target:d,"data-discover":!x&&n==="render"?"true":void 0});return ne&&!x?i.createElement(i.Fragment,null,se,i.createElement(br,{page:I})):se});Kt.displayName="Link";var Cr=i.forwardRef(function({"aria-current":t="page",caseSensitive:n=!1,className:r="",end:a=!1,style:s,to:o,viewTransition:u,children:c,...d},f){let h=Re(o,{relative:d.relative}),p=te(),b=i.useContext($e),{navigator:v,basename:g}=i.useContext(H),m=b!=null&&Lr(h)&&u===!0,y=v.encodeLocation?v.encodeLocation(h).pathname:h.pathname,w=p.pathname,x=b&&b.navigation&&b.navigation.location?b.navigation.location.pathname:null;n||(w=w.toLowerCase(),x=x?x.toLowerCase():null,y=y.toLowerCase()),x&&g&&(x=Z(x,g)||x);const N=y!=="/"&&y.endsWith("/")?y.length-1:y.length;let I=w===y||!a&&w.startsWith(y)&&w.charAt(N)==="/",j=x!=null&&(x===y||!a&&x.startsWith(y)&&x.charAt(y.length)==="/"),J={isActive:I,isPending:j,isTransitioning:m},ne=I?t:void 0,re;typeof r=="function"?re=r(J):re=[r,I?"active":null,j?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let he=typeof s=="function"?s(J):s;return i.createElement(Kt,{...d,"aria-current":ne,className:re,ref:f,style:he,to:o,viewTransition:u},typeof c=="function"?c(J):c)});Cr.displayName="NavLink";var Nr=i.forwardRef(({discover:e="render",fetcherKey:t,navigate:n,reloadDocument:r,replace:a,state:s,method:o=Pe,action:u,onSubmit:c,relative:d,preventScrollReset:f,viewTransition:h,defaultShouldRevalidate:p,...b},v)=>{let{useTransitions:g}=i.useContext(H),m=Ir(),y=Ar(u,{relative:d}),w=o.toLowerCase()==="get"?"get":"post",x=typeof u=="string"&&Jt.test(u),N=I=>{if(c&&c(I),I.defaultPrevented)return;I.preventDefault();let j=I.nativeEvent.submitter,J=(j==null?void 0:j.getAttribute("formmethod"))||o,ne=()=>m(j||I.currentTarget,{fetcherKey:t,method:J,navigate:n,replace:a,state:s,relative:d,preventScrollReset:f,viewTransition:h,defaultShouldRevalidate:p});g&&n!==!1?i.startTransition(()=>ne()):ne()};return i.createElement("form",{ref:v,method:w,action:y,onSubmit:r?c:N,...b,"data-discover":!x&&e==="render"?"true":void 0})});Nr.displayName="Form";function jr(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qt(e){let t=i.useContext(ce);return M(t,jr(e)),t}function Tr(e,{target:t,replace:n,mask:r,state:a,preventScrollReset:s,relative:o,viewTransition:u,defaultShouldRevalidate:c,useTransitions:d}={}){let f=Ee(),h=te(),p=Re(e,{relative:o});return i.useCallback(b=>{if(or(b,t)){b.preventDefault();let v=n!==void 0?n:be(h)===be(p),g=()=>f(e,{replace:v,mask:r,state:a,preventScrollReset:s,relative:o,viewTransition:u,defaultShouldRevalidate:c});d?i.startTransition(()=>g()):g()}},[h,f,p,n,r,a,t,e,s,o,u,c,d])}var Pr=0,Mr=()=>`__${String(++Pr)}__`;function Ir(){let{router:e}=qt("useSubmit"),{basename:t}=i.useContext(H),n=Yn(),r=e.fetch,a=e.navigate;return i.useCallback(async(s,o={})=>{let{action:u,method:c,encType:d,formData:f,body:h}=ur(s,t);if(o.navigate===!1){let p=o.fetcherKey||Mr();await r(p,n,o.action||u,{defaultShouldRevalidate:o.defaultShouldRevalidate,preventScrollReset:o.preventScrollReset,formData:f,body:h,formMethod:o.method||c,formEncType:o.encType||d,flushSync:o.flushSync})}else await a(o.action||u,{defaultShouldRevalidate:o.defaultShouldRevalidate,preventScrollReset:o.preventScrollReset,formData:f,body:h,formMethod:o.method||c,formEncType:o.encType||d,replace:o.replace,state:o.state,fromRouteId:n,flushSync:o.flushSync,viewTransition:o.viewTransition})},[r,a,t,n])}function Ar(e,{relative:t}={}){let{basename:n}=i.useContext(H),r=i.useContext(ee);M(r,"useFormAction must be used inside a RouteContext");let[a]=r.matches.slice(-1),s={...Re(e||".",{relative:t})},o=te();if(e==null){s.search=o.search;let u=new URLSearchParams(s.search),c=u.getAll("index");if(c.some(f=>f==="")){u.delete("index"),c.filter(h=>h).forEach(h=>u.append("index",h));let f=u.toString();s.search=f?`?${f}`:""}}return(!e||e===".")&&a.route.index&&(s.search=s.search?s.search.replace(/^\?/,"?index&"):"?index"),n!=="/"&&(s.pathname=s.pathname==="/"?n:V([n,s.pathname])),be(s)}function Lr(e,{relative:t}={}){let n=i.useContext(Ft);M(n!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=qt("useViewTransitionState"),a=Re(e,{relative:t});if(!n.isTransitioning)return!1;let s=Z(n.currentLocation.pathname,r)||n.currentLocation.pathname,o=Z(n.nextLocation.pathname,r)||n.nextLocation.pathname;return Ie(a.pathname,o)!=null||Ie(a.pathname,s)!=null}const $=6,oe=2.6,K=240,X={LE:[10,13],RE:[29,13],MO:[20,27]};function G(e,t,n){const r=[],a=Math.ceil(n);for(let s=-a;s<=a;s++)for(let o=-a;o<=a;o++)s*s+o*o<=n*n&&r.push([e+s,t+o]);return r}function B(e,t,n,r,a,s=5){const o=[],u=new Set;for(let c=r;c<=a;c+=s){const d=c*Math.PI/180,f=Math.round(e+n*Math.cos(d)),h=Math.round(t+n*Math.sin(d)),p=`${f},${h}`;u.has(p)||(u.add(p),o.push([f,h]))}return o}function Y(e,t,n){const r=[];for(let a=Math.round(e-n);a<=Math.round(e+n);a++)r.push([a,Math.round(t)]);return r}function Ge(e,t,n,r){const a=[],s=new Set,o=Math.round(n);for(let u=-o;u<=o;u++){const c=u/o*Math.PI,d=Math.round(t+r*Math.sin(c)),f=`${e+u},${d}`;s.has(f)||(s.add(f),a.push([e+u,d]))}return a}function $r(e,t,n,r){const a=[],s=Math.round(n);for(let o=-s;o<=s;o++){const u=o+s;a.push([e+o,Math.round(t+(u%4<2?r:-r))])}return a}function xt(e,t){return[[-1,-2],[1,-2],[-2,-1],[-1,-1],[0,-1],[1,-1],[2,-1],[-2,0],[-1,0],[0,0],[1,0],[2,0],[-1,1],[0,1],[1,1],[0,2]].map(([n,r])=>[e+n,t+r])}function Et(e,t,n,r){const a=[],s=new Set,o=Math.round(r*2*Math.PI*8);for(let u=0;u<=o;u++){const c=u/o*r*2*Math.PI,d=u/o*n,f=Math.round(e+d*Math.cos(c)),h=Math.round(t+d*Math.sin(c)),p=`${f},${h}`;s.has(p)||(s.add(p),a.push([f,h]))}return a}function Je(e,t){return[[e-1,t-1],[e,t-1],[e+1,t-1],[e,t],[e-1,t+1],[e,t+1],[e+1,t+1]]}function Rt(e,t){return[...B(e,t-1,1.5,200,360,30),[e,t+1],[e,t+3]]}function Q(e,t){return{iris:G(e,t,3),pupil:G(e,t,1.5),hl:[[e-1,t-1]],lash:Y(e,t-3,3)}}function z(e,t){return{iris:G(e,t,4),pupil:G(e,t,2),hl:[[e-1,t-1],[e-2,t-2]],lash:Y(e,t-4,4)}}function le(e,t){return{iris:G(e,t,3).filter(([,r])=>r>=t-1&&r<=t+1),pupil:[[e,t]],lash:Y(e,t-1,2)}}function ie(e,t){return{iris:G(e,t,3).filter(([,r])=>r>=t),pupil:[[e-1,t],[e,t],[e+1,t]],lash:Y(e,t,3)}}function Ne(e,t){return{iris:G(e,t+1,3),pupil:G(e,t+2,1.5),hl:[[e-1,t]],lash:Y(e,t-2,2)}}function je(e,t,n=2){return{iris:G(e,t,3),pupil:G(e+n,t,1.5),hl:[[e+n-1,t-1]],lash:Y(e,t-3,3)}}function _r(e,t){return{wink:B(e,t+1,3,200,340,15)}}function ye(e,t){return B(e,t,5,25,155)}function Ye(e,t){return B(e,t,7,15,165)}function St(e,t){return Y(e,t,5)}function Br(e,t){const n=B(e-2,t,4,30,120,10),r=B(e+3,t+2,2.5,270,360,15),a=new Set(n.map(([s,o])=>`${s},${o}`));return[...n,...r.filter(([s,o])=>!a.has(`${s},${o}`))]}function Fr(e,t){const n=B(e-2,t,2,200,340,20),r=B(e+2,t,2,200,340,20),a=Y(e,t+2,3),s=new Set(n.map(([o,u])=>`${o},${u}`));return[...n,...r.filter(([o,u])=>!s.has(`${o},${u}`)),...a]}function Or(e,t,n){const r=Math.abs(Math.sin(n*1.4));if(r<.25)return B(e,t,4,20,160,8);const a=r<.65?1:2,s=B(e,t-a,3.5,190,350,8),o=B(e,t+a,3,10,170,8),u=new Set(s.map(([c,d])=>`${c},${d}`));return[...s,...o.filter(([c,d])=>!u.has(`${c},${d}`))]}const[R,S]=X.LE,[k,C]=X.RE,[T,P]=X.MO,_="#22CCFF",Dr=[{id:"idle",label:"😐 待机",color:"#00FFEE",category:"interaction",canBlink:!0,leftEye:Q(R,S),rightEye:Q(k,C),mouth:ye(T,P)},{id:"listening",label:"👂 聆听",color:"#44AAFF",category:"interaction",canBlink:!0,leftEye:z(R,S),rightEye:z(k,C),mouth:B(T,P,3,0,180)},{id:"thinking",label:"🤔 思考",color:"#FFCC00",category:"interaction",canBlink:!0,leftEye:le(R,S),rightEye:Q(k,C),mouth:Ge(T,P,5,1),extras:[{dots:Rt(R-1,S-6),color:"#FFCC00",alpha:.75}]},{id:"speaking",label:"💬 说话",color:"#FFFFFF",pupilColor:"#002233",category:"interaction",canBlink:!0,leftEye:Q(R,S),rightEye:Q(k,C),speakingMouth:!0},{id:"happy",label:"😊 开心",color:"#FF44AA",category:"interaction",canBlink:!0,leftEye:{heart:xt(R,S)},rightEye:{heart:xt(k,C)},mouth:Ye(T,P)},{id:"error",label:"😵 错误",color:"#FF4400",category:"interaction",canBlink:!0,leftEye:{spiral:Et(R,S,4,2)},rightEye:{spiral:Et(k,C,4,2)},mouth:$r(T,P,5,1.5)},{id:"confused",label:"😕 困惑",color:"#BB44FF",category:"interaction",canBlink:!0,leftEye:z(R,S),rightEye:le(k,C),mouth:Ge(T,P,5,1.5),extras:[{dots:Rt(k+1,C-6),color:"#BB44FF",alpha:.75}]},{id:"dreaming",label:"💤 做梦",color:"#5566FF",category:"interaction",canBlink:!0,leftEye:ie(R,S),rightEye:ie(k,C),mouth:ye(T,P),extras:[{dots:Je(24,10),color:"#5566FF",alpha:.85,zzzIndex:0},{dots:Je(28,7),color:"#7788FF",alpha:.75,zzzIndex:1},{dots:Je(32,4),color:"#99AAFF",alpha:.65,zzzIndex:2}]}],Gt=[{id:"natural_smile",label:"01 自然微笑",color:_,category:"auto",canBlink:!0,leftEye:Q(R,S),rightEye:Q(k,C),mouth:ye(T,P)},{id:"calm_relax",label:"02 平静放松",color:_,category:"auto",canBlink:!0,leftEye:ie(R,S),rightEye:ie(k,C),mouth:St(T,P)},{id:"curious_observe",label:"03 好奇观察",color:_,category:"auto",canBlink:!0,leftEye:z(R,S),rightEye:z(k,C),mouth:B(T,P,4,30,150,8)},{id:"light_joy",label:"04 轻松愉悦",color:_,category:"auto",canBlink:!0,leftEye:le(R,S),rightEye:le(k,C),mouth:Ye(T,P)},{id:"side_think",label:"05 侧目思考",color:_,category:"auto",canBlink:!0,leftEye:je(R,S,2),rightEye:je(k,C,2),mouth:Ge(T,P,4,1)},{id:"warm_friendly",label:"06 温和友善",color:_,category:"auto",canBlink:!0,leftEye:Ne(R,S),rightEye:Ne(k,C),mouth:ye(T,P)},{id:"quiet_listen",label:"07 静静聆听",color:_,category:"auto",canBlink:!0,leftEye:Q(R,S),rightEye:Q(k,C),mouth:Y(T,P,4)},{id:"focus_gaze",label:"08 专注注视",color:_,category:"auto",canBlink:!0,leftEye:z(R,S),rightEye:z(k,C),mouth:St(T,P)},{id:"quiet_wait",label:"09 安静等待",color:_,category:"auto",canBlink:!0,leftEye:ie(R,S),rightEye:ie(k,C),mouth:B(T,P,4,30,150,8)},{id:"slow_blink",label:"10 慢眨眼",color:_,category:"auto",canBlink:!0,leftEye:le(R,S),rightEye:le(k,C),mouth:ye(T,P)},{id:"look_around",label:"11 左右扫视",color:_,category:"auto",canBlink:!0,leftEye:je(R,S,-2),rightEye:je(k,C,-2),mouth:Y(T,P,4)},{id:"playful",label:"12 小小调皮",color:_,category:"auto",canBlink:!1,leftEye:_r(R,S),rightEye:z(k,C),mouth:Br(T,P)},{id:"joyful_wait",label:"13 愉悦期待",color:_,category:"auto",canBlink:!0,leftEye:z(R,S),rightEye:z(k,C),mouth:Ye(T,P)},{id:"slight_surprise",label:"14 轻微惊喜",color:_,category:"auto",canBlink:!0,leftEye:z(R,S),rightEye:z(k,C),mouth:B(T,P,3,0,180,10)},{id:"gentle_gaze",label:"15 温柔注视",color:_,category:"auto",canBlink:!0,leftEye:Ne(R,S),rightEye:Ne(k,C),mouth:B(T,P,4,30,150,8)},{id:"spaceout",label:"16 发呆放空",color:_,category:"auto",canBlink:!0,leftEye:ie(R,S),rightEye:ie(k,C),mouth:Fr(T,P)}],it=[...Dr,...Gt],pe=Gt.map(e=>e.id),Xe=Object.fromEntries(it.map(e=>[e.id,e]));function D(e,t,n,r,a=null){if(!(!t||t.length===0)){if(e.save(),a==null){e.globalAlpha=Math.max(0,Math.min(1,r)),e.shadowBlur=14,e.shadowColor=n,e.fillStyle=n,e.beginPath();for(const[s,o]of t){const u=s*$+$/2,c=o*$+$/2;e.moveTo(u+oe,c),e.arc(u,c,oe,0,Math.PI*2)}e.fill(),e.shadowBlur=0,e.fillStyle="rgba(255,255,255,0.55)",e.beginPath();for(const[s,o]of t){const u=s*$+$/2,c=o*$+$/2;e.moveTo(u+oe*.38,c),e.arc(u,c,oe*.38,0,Math.PI*2)}e.fill()}else{const s=[],o=[];for(const[c,d]of t){const f=(c*7.3+d*13.1)*.31;(Math.sin(a*1.1+f)>0?s:o).push([c,d])}const u=(c,d,f)=>{if(c.length){e.globalAlpha=Math.max(0,Math.min(1,d)),e.shadowBlur=f,e.shadowColor=n,e.fillStyle=n,e.beginPath();for(const[h,p]of c){const b=h*$+$/2,v=p*$+$/2;e.moveTo(b+oe,v),e.arc(b,v,oe,0,Math.PI*2)}e.fill()}};u(o,r*.68,7),u(s,r,20),e.globalAlpha=Math.max(0,Math.min(1,r*.55)),e.shadowBlur=0,e.fillStyle="rgba(255,255,255,0.55)",e.beginPath();for(const[c,d]of t){const f=c*$+$/2,h=d*$+$/2;e.moveTo(f+oe*.38,h),e.arc(f,h,oe*.38,0,Math.PI*2)}e.fill()}e.restore()}}function ge(e,t,n){return n>=1?e:e.map(([r,a])=>[r,t+(a-t)*n])}function Ur(e,t){if(e===null)return 1;const n=t-e;return n<120?1-n/120:n<220?0:n<340?(n-220)/120:1}function kt(e,t,n,r,a,s,o,u,c){if(!t)return;const d=s||"#001020";t.iris?(D(e,ge(t.iris,r,u),a,o,c),D(e,ge(t.pupil,r,u),d,o,null),t.hl&&D(e,ge(t.hl,r,u),"#ffffff",o*.9,null),t.lash&&D(e,ge(t.lash,r,u),a,o,null)):t.closed!==void 0?(D(e,t.closed,a,o,c),t.lash&&D(e,t.lash,a,o,null)):t.wink?D(e,t.wink,a,o,c):t.heart?D(e,t.heart,a,o,c):t.spiral?D(e,t.spiral,a,o,c):t.star?D(e,t.star,a,o,c):t.pts&&D(e,ge(t.pts,r,u),a,o,c)}function zr({expressionId:e,scale:t=2}){const n=i.useRef(null),r=i.useRef({currentId:e,previousId:null,transitionStart:null,blinkStart:null,nextBlink:performance.now()+2e3+Math.random()*2e3,t:0,raf:null});return i.useEffect(()=>{const a=r.current;e!==a.currentId&&(a.previousId=a.currentId,a.currentId=e,a.transitionStart=performance.now(),a.blinkStart=null)},[e]),i.useEffect(()=>{const s=n.current.getContext("2d"),o=r.current;function u(d,f,h,p){if(!d)return;kt(s,d.leftEye,X.LE[0],X.LE[1],d.color,d.pupilColor,f,h,p),kt(s,d.rightEye,X.RE[0],X.RE[1],d.color,d.pupilColor,f,h,p);const b=d.speakingMouth?Or(X.MO[0],X.MO[1],o.t):d.mouth;if(b&&D(s,b,d.color,f,p),d.extras)for(const v of d.extras){let g=f*v.alpha;if(v.zzzIndex!==void 0){const m=o.t-v.zzzIndex*1.2;g*=.35+.65*(Math.sin(m)*.5+.5)}D(s,v.dots,v.color,g,null)}}function c(){const d=performance.now();o.t+=.04,s.clearRect(0,0,K,K),s.fillStyle="#000",s.fillRect(0,0,K,K);let f=1,h=0;if(o.transitionStart!==null){const g=d-o.transitionStart;if(g>=380)o.transitionStart=null,o.previousId=null;else{const m=g/380,y=m<.5?2*m*m:1-(-2*m+2)**2/2;f=y,h=1-y}}const p=Xe[o.currentId];!o.blinkStart&&o.transitionStart===null&&(p!=null&&p.canBlink)&&d>=o.nextBlink&&(o.blinkStart=d,o.nextBlink=d+2500+Math.random()*2500),o.blinkStart!==null&&d-o.blinkStart>=340&&(o.blinkStart=null);const b=Ur(o.blinkStart,d);o.previousId&&u(Xe[o.previousId],h,1,null);const v=o.transitionStart===null?o.t:null;u(p,f,b,v),s.save(),s.globalAlpha=.04,s.fillStyle="#000";for(let g=0;g<K;g+=2)s.fillRect(0,g+1,K,1);s.restore(),o.raf=requestAnimationFrame(c)}return o.raf=requestAnimationFrame(c),()=>cancelAnimationFrame(o.raf)},[]),l.jsx("canvas",{ref:n,width:K,height:K,style:{width:K*t,height:K*t,imageRendering:"pixelated",borderRadius:14,boxShadow:"0 0 40px rgba(0,255,200,0.2)",display:"block"}})}function Hr({base64:e,mimeType:t="audio/mp3",label:n="🔊"}){const r=e?`data:${t};base64,${e}`:null;return r?l.jsx("audio",{className:"audio-btn",controls:!0,src:r,title:n,preload:"none"}):null}function Wr({role:e,text:t,audioBase64:n,audioMime:r}){const a=e==="user";return l.jsxs("div",{className:`bubble ${a?"bubble-user":"bubble-bot"}`,children:[l.jsx("div",{className:"bubble-label",children:a?"你":"JBOT"}),l.jsx("div",{className:"bubble-text",children:t}),n&&l.jsx(Hr,{base64:n,mimeType:r||"audio/mp3"})]})}function Vr({messages:e,voiceStatus:t}){const n=i.useRef(null),r=Ee();return i.useEffect(()=>{var a;(a=n.current)==null||a.scrollIntoView({behavior:"smooth"})},[e]),l.jsxs("section",{className:"chat-panel",children:[l.jsxs("div",{className:"chat-header",children:[l.jsx("span",{className:"chat-title",children:"对话"}),l.jsx("button",{className:"history-link",onClick:()=>r("/history"),children:"历史记录 →"})]}),t&&l.jsx("div",{className:`voice-status voice-status-${t.type}`,children:t.text}),l.jsxs("div",{className:"chat-messages",children:[e.length===0?l.jsx("p",{className:"chat-empty",children:"按住 Space 开始说话…"}):e.map(a=>l.jsx(Wr,{role:a.role,text:a.text,audioBase64:a.audioBase64,audioMime:a.audioMime},a.id)),l.jsx("div",{ref:n})]})]})}const Be="jbot_memory",Jr=100;function ve(){try{return JSON.parse(localStorage.getItem(Be)||"[]")}catch{return[]}}function Kr(e){const t=ve(),n=e.toLowerCase().trim();if(t.some(a=>a.text.toLowerCase().trim()===n))return null;const r={id:crypto.randomUUID(),text:e.trim(),createdAt:Date.now()};return localStorage.setItem(Be,JSON.stringify([r,...t].slice(0,Jr))),r}function qr(e){localStorage.setItem(Be,JSON.stringify(ve().filter(t=>t.id!==e)))}function Gr(){localStorage.removeItem(Be)}function Yr({memories:e,onMemoriesChange:t}){const[n,r]=i.useState(!1),a=o=>{qr(o),t()},s=()=>{window.confirm("清空所有记忆?")&&(Gr(),t())};return l.jsxs("div",{className:"memory-panel",children:[l.jsxs("button",{className:"memory-toggle",onClick:()=>r(o=>!o),children:[l.jsx("span",{children:"🧠 长期记忆"}),l.jsx("span",{className:"memory-badge",children:e.length}),l.jsx("span",{className:"memory-arrow",children:n?"▲":"▼"})]}),n&&l.jsx("div",{className:"memory-body",children:e.length===0?l.jsx("p",{className:"memory-empty",children:"对话后会自动记住关于你的信息"}):l.jsxs(l.Fragment,{children:[l.jsx("ul",{className:"memory-list",children:e.map(o=>l.jsxs("li",{className:"memory-item",children:[l.jsx("span",{className:"memory-text",children:o.text}),l.jsx("button",{className:"memory-del",onClick:()=>a(o.id),title:"删除",children:"×"})]},o.id))}),l.jsx("button",{className:"memory-clear",onClick:s,children:"清空全部"})]})})]})}const de="/api/jbot";async function Xr(e,t=[],n={}){const r=await fetch(`${de}/chat/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e,memories:t,settings:n})});if(!r.ok){const a=await r.json().catch(()=>({}));throw new Error(a.error||`HTTP ${r.status}`)}return r.json()}async function Qr(e,t="audio/webm"){const n=await fetch(`${de}/asr/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({audioBase64:e,format:t})});if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`HTTP ${n.status}`)}return n.json()}async function Zr(e,t){const n=await fetch(`${de}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:e,voice:t})});if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`HTTP ${n.status}`)}return n.json()}async function ea(e,t){try{const n=await fetch(`${de}/memory/extract/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({userText:e,botText:t})});if(!n.ok)return[];const{facts:r=[]}=await n.json();return r}catch{return[]}}async function ta(){try{const e=await fetch(`${de}/api-config/`);return e.ok?e.json():{}}catch{return{}}}async function na(e){const t=await fetch(`${de}/api-config/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){const n=await t.json().catch(()=>({}));throw new Error(n.error||`HTTP ${t.status}`)}return t.json()}function ra(){const[e,t]=i.useState(!1),n=i.useRef(null),r=i.useRef(null),a=i.useRef(null),s=i.useRef([]),o=i.useRef(""),u=i.useCallback(async()=>{var p;if(e)return;s.current=[],o.current="",(p=n.current)!=null&&p.active||(n.current=await navigator.mediaDevices.getUserMedia({audio:!0}));const d=MediaRecorder.isTypeSupported("audio/webm;codecs=opus")?"audio/webm;codecs=opus":"audio/webm",f=new MediaRecorder(n.current,{mimeType:d});f.ondataavailable=b=>{b.data.size>0&&s.current.push(b.data)},f.start(100),r.current=f;const h=window.SpeechRecognition||window.webkitSpeechRecognition;if(h){const b=new h;b.lang="zh-CN",b.interimResults=!0,b.continuous=!0,b.onresult=v=>{let g="";for(const m of v.results)m.isFinal&&(g+=m[0].transcript);g&&(o.current=g)},b.onerror=v=>{console.warn("SpeechRecognition error:",v.error)},b.start(),a.current=b}t(!0)},[e]),c=i.useCallback(()=>new Promise(d=>{var p,b;t(!1);const f=o.current;try{(p=a.current)==null||p.stop()}catch{}a.current=null,(b=n.current)==null||b.getTracks().forEach(v=>v.stop()),n.current=null;const h=r.current;if(!h)return d({transcript:f,audioBase64:null,mimeType:null});h.onstop=async()=>{const v=h.mimeType;if(s.current.length===0)return d({transcript:f,audioBase64:null,mimeType:v});const g=new Blob(s.current,{type:v}),m=new FileReader;m.onloadend=()=>{d({transcript:f,audioBase64:m.result.split(",")[1],mimeType:v})},m.readAsDataURL(g)},h.stop(),r.current=null}),[]);return{isRecording:e,start:u,stop:c}}const Fe="jbot_history",aa=200;function Le(){try{return JSON.parse(localStorage.getItem(Fe)||"[]")}catch{return[]}}function sa(e){const t=Le(),n=[{id:crypto.randomUUID(),timestamp:Date.now(),...e},...t];return localStorage.setItem(Fe,JSON.stringify(n.slice(0,aa))),n[0]}function oa(){localStorage.removeItem(Fe)}function ia(e){localStorage.setItem(Fe,JSON.stringify(Le().filter(t=>t.id!==e)))}const Yt=[{id:"zh_female_vv_uranus_bigtts",label:"灵儿",lang:"中文",gender:"女",style:"亲切"},{id:"zh_female_tianmei_conversation_bigtts",label:"甜美",lang:"中文",gender:"女",style:"温柔"},{id:"zh_female_linjing_conversation_bigtts",label:"琳静",lang:"中文",gender:"女",style:"知性"},{id:"zh_female_qingxin_conversation_bigtts",label:"清新",lang:"中文",gender:"女",style:"清新"},{id:"zh_male_m191_uranus_bigtts",label:"豪远",lang:"中文",gender:"男",style:"标准"},{id:"zh_male_haoran_conversation_bigtts",label:"豪然",lang:"中文",gender:"男",style:"稳重"},{id:"zh_male_m372_conversation_bigtts",label:"温和男声",lang:"中文",gender:"男",style:"温和"},{id:"en_female_sarah_jupiter_bigtts",label:"Sarah",lang:"EN",gender:"女",style:"Warm"},{id:"en_male_adam_mars_bigtts",label:"Adam",lang:"EN",gender:"男",style:"Clear"}],Xt="zh_female_vv_uranus_bigtts";function la(e){const t=Yt.find(n=>n.id===e);return t?`${t.label} · ${t.lang} ${t.gender}`:e}const lt="jbot_settings",Qe={robotName:"JBOT",robotPersonality:"你是一个友好、简洁、偶尔幽默的AI机器人助手。回复控制在80字以内。",language:"auto",voiceId:Xt,customVoiceId:"",responseMaxWords:80};function Oe(){try{const e=JSON.parse(localStorage.getItem(lt)||"{}");return{...Qe,...e}}catch{return{...Qe}}}function ua(e){const n={...Oe(),...e};return localStorage.setItem(lt,JSON.stringify(n)),n}function ca(){return localStorage.removeItem(lt),{...Qe}}function Ct(e=null){var n;const t=e||Oe();return((n=t.customVoiceId)==null?void 0:n.trim())||t.voiceId||Xt}const da=it.filter(e=>e.category==="interaction"),ha=it.filter(e=>e.category==="auto"),fa={1:"idle",2:"listening",3:"thinking",4:"speaking",5:"happy",6:"error",7:"confused",8:"dreaming",q:"natural_smile",w:"calm_relax",e:"curious_observe",r:"light_joy",t:"side_think",y:"warm_friendly",u:"quiet_listen",i:"focus_gaze"};async function ma(e,t="audio/mp3"){return new Promise(n=>{const r=new Audio(`data:${t};base64,${e}`);r.onended=n,r.onerror=n,r.play().catch(n)})}function pa(e){return new Promise(t=>{if(!window.speechSynthesis)return t();const n=new SpeechSynthesisUtterance(e);n.lang="zh-CN",n.rate=1.05,n.onend=t,n.onerror=t,window.speechSynthesis.speak(n)})}function ga(){var ht;const e=Ee(),[t,n]=i.useState(pe[0]),[r,a]=i.useState(!0),[s,o]=i.useState(1),[u,c]=i.useState(1e4),d=i.useRef(null),f=i.useRef(!0),h=i.useRef(pe[0]),p=i.useRef(null),b=i.useRef(1e4);i.useEffect(()=>{f.current=r},[r]),i.useEffect(()=>{h.current=t},[t]),i.useEffect(()=>{b.current=u},[u]);const v=i.useCallback(()=>{clearTimeout(d.current),d.current=null},[]);p.current=()=>{if(v(),!f.current)return;const E=1+(Math.random()-.5)*.3;d.current=setTimeout(()=>{var L;const U=pe.filter(O=>O!==h.current),A=U[Math.floor(Math.random()*U.length)];n(A),(L=p.current)==null||L.call(p)},b.current*E)};const g=i.useCallback(()=>{var E;return(E=p.current)==null?void 0:E.call(p)},[]);i.useEffect(()=>(r?g():v(),v),[r,g,v]);const m=i.useCallback(E=>{v(),n(E),f.current&&g()},[v,g]),{start:y,stop:w}=ra(),[x,N]=i.useState("idle"),[I,j]=i.useState(null),[J,ne]=i.useState([]),[re,he]=i.useState([]),[De,Ue]=i.useState(()=>ve()),ae=i.useRef("idle"),se=i.useRef(!1),W=i.useCallback(()=>Ue(ve()),[]);i.useEffect(()=>{ae.current=x},[x]);const ut=(E,U,A,L)=>{const O={id:crypto.randomUUID(),role:E,text:U,audioBase64:A,audioMime:L};return ne(ze=>[...ze.slice(-29),O]),O},ct=i.useCallback(async()=>{if(!(se.current||ae.current!=="idle")){se.current=!0,v(),N("acquiring"),j({type:"acquiring",text:"⏳ 麦克风准备中…"});try{await y()}catch(E){console.warn("[PTT] Mic acquisition failed:",E.message),se.current=!1,N("idle"),j({type:"error",text:"⚠ 麦克风不可用"}),setTimeout(()=>j(null),2500),g();return}n("listening"),N("listening"),j({type:"listening",text:"🎙 请讲话 · 松开 Space 结束"})}},[v,y,g]),dt=i.useCallback(async()=>{if(!se.current)return;if(se.current=!1,ae.current==="acquiring"){N("idle"),j(null),g();return}if(ae.current!=="listening")return;N("thinking"),n("thinking"),j({type:"thinking",text:"⚙ 识别中…"});const{transcript:E,audioBase64:U,mimeType:A}=await w();let L="";if(U)try{j({type:"thinking",text:"⚙ 识别中…"});const{text:F}=await Qr(U,A||"audio/webm");L=(F==null?void 0:F.trim())||""}catch(F){console.warn("[ASR] Volcengine failed, using browser transcript:",F.message)}if(L||(L=E.trim()),!L){N("idle"),n("idle"),j(null);return}ut("user",L,U,A),j({type:"thinking",text:"⚙ 思考中…"});let O="";try{const F=ve().map(tn=>tn.text),Se=Oe(),ke=[...re,{role:"user",content:L}],{text:en}=await Xr(ke.slice(-10),F,Se);O=en,he([...ke,{role:"assistant",content:O}].slice(-20))}catch(F){O=`对话出错: ${F.message}`}n("speaking"),N("speaking"),j({type:"speaking",text:"🔊 播放中…"});const ze=Ct();let fe=null,ft=!1;try{const{audioBase64:F}=await Zr(O,ze);fe=F}catch(F){console.warn("[TTS] Volcengine unavailable, using browser TTS:",F.message),ft=!0}ft&&j({type:"speaking",text:"🔊 播放中… (浏览器语音)"}),ut("bot",O,fe,"audio/mp3"),sa({userText:L,botText:O,userAudioBase64:U,userAudioMime:A,botAudioBase64:fe,botAudioMime:"audio/mp3"}),ea(L,O).then(F=>{let Se=!1;F.forEach(ke=>{Kr(ke)&&(Se=!0)}),Se&&W()}).catch(()=>{}),fe?await ma(fe,"audio/mp3"):await pa(O),N("idle"),j(null);const Zt=pe[Math.floor(Math.random()*pe.length)];n(Zt),g()},[w,re,g]);i.useEffect(()=>{function E(A){if(!(A.target.tagName==="INPUT"||A.target.tagName==="TEXTAREA")){if(A.code==="Space"&&!A.repeat){A.preventDefault(),ct();return}if(ae.current==="idle"){const L=fa[A.key.toLowerCase()];L&&m(L)}}}function U(A){A.code==="Space"&&(A.preventDefault(),dt())}return window.addEventListener("keydown",E),window.addEventListener("keyup",U),()=>{window.removeEventListener("keydown",E),window.removeEventListener("keyup",U)}},[ct,dt,m]);const Qt=la(Ct());return l.jsxs("div",{className:"app",children:[l.jsxs("header",{className:"hdr",children:[l.jsxs("h1",{children:["J",l.jsx("span",{className:"accent",children:"BOT"})]}),l.jsx("p",{className:"subtitle",children:"AI Robot Face · 240×240"}),l.jsxs("nav",{className:"hdr-nav",children:[l.jsx("button",{className:"hdr-link",onClick:()=>e("/history"),children:"历史"}),l.jsx("button",{className:"hdr-link",onClick:()=>e("/settings"),children:"设置"})]})]}),l.jsxs("div",{className:"page-body",children:[l.jsxs("aside",{className:"left-col",children:[l.jsxs("section",{className:"screen-wrap",children:[l.jsxs("div",{className:"scale-controls",children:[l.jsx("button",{className:`scale-btn${s===1?" active":""}`,onClick:()=>o(1),children:"1×"}),l.jsx("button",{className:`scale-btn${s===2?" active":""}`,onClick:()=>o(2),children:"2×"})]}),l.jsx("div",{className:"bezel",children:l.jsx(zr,{expressionId:t,scale:s})}),l.jsx("div",{className:"expr-label",children:(ht=Xe[t])==null?void 0:ht.label})]}),l.jsxs("section",{className:"panel",children:[l.jsxs("div",{className:"panel-section",children:[l.jsxs("h3",{children:["交互状态 ",l.jsx("span",{className:"key-hint",children:"1 8"})]}),l.jsx("div",{className:"btn-grid",children:da.map(E=>l.jsx("button",{className:`ebtn${t===E.id?" active":""}`,style:{"--c":E.color},onClick:()=>m(E.id),children:E.label},E.id))})]}),l.jsxs("div",{className:"panel-section",children:[l.jsxs("h3",{children:["微表情 ",l.jsx("span",{className:"key-hint",children:"QI · 自动"})]}),l.jsx("div",{className:"btn-grid btn-grid-4",children:ha.map(E=>l.jsx("button",{className:`ebtn${t===E.id?" active":""}`,style:{"--c":E.color},onClick:()=>m(E.id),children:E.label},E.id))})]}),l.jsxs("label",{className:"toggle",children:[l.jsx("input",{type:"checkbox",checked:r,onChange:E=>a(E.target.checked)}),l.jsx("span",{children:"自动轮换微表情"})]}),l.jsxs("div",{className:"switch-interval",children:[l.jsxs("div",{className:"switch-interval-label",children:["切换间隔 ",l.jsxs("span",{className:"interval-val",children:[Math.round(u/1e3)," 秒"]})]}),l.jsx("input",{type:"range",min:"3",max:"30",step:"1",value:Math.round(u/1e3),onChange:E=>c(Number(E.target.value)*1e3)}),l.jsxs("div",{className:"interval-ticks",children:[l.jsx("span",{children:"3s"}),l.jsx("span",{children:"30s"})]})]}),l.jsxs("p",{className:"hint",children:["按住 ",l.jsx("kbd",{children:"Space"})," 说话 · 松开发送 · ",l.jsx("button",{className:"hist-inline-link",onClick:()=>e("/history"),children:"历史记录"})]}),l.jsxs("p",{className:"hint voice-hint",children:["🔊 音色: ",l.jsx("button",{className:"hist-inline-link",onClick:()=>e("/settings"),children:Qt})]}),l.jsx(Yr,{memories:De,onMemoriesChange:W})]})]}),l.jsx("div",{className:"right-col",children:l.jsx(Vr,{messages:J,voiceStatus:I})})]})]})}function Nt({base64:e,mimeType:t="audio/mp3"}){return e?l.jsx("audio",{className:"hist-audio",controls:!0,src:`data:${t};base64,${e}`,preload:"none"}):null}function ya({entry:e,onDelete:t}){const r=new Date(e.timestamp).toLocaleString("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"});return l.jsxs("article",{className:"hist-entry",children:[l.jsxs("div",{className:"hist-meta",children:[l.jsx("span",{className:"hist-time",children:r}),l.jsx("button",{className:"hist-del",onClick:()=>t(e.id),title:"删除",children:"✕"})]}),l.jsxs("div",{className:"hist-row hist-row-user",children:[l.jsx("div",{className:"hist-role",children:"你"}),l.jsxs("div",{className:"hist-body",children:[l.jsx("p",{className:"hist-text",children:e.userText||"(语音消息)"}),l.jsx(Nt,{base64:e.userAudioBase64,mimeType:e.userAudioMime||"audio/webm"})]})]}),l.jsxs("div",{className:"hist-row hist-row-bot",children:[l.jsx("div",{className:"hist-role",children:"JBOT"}),l.jsxs("div",{className:"hist-body",children:[l.jsx("p",{className:"hist-text",children:e.botText||""}),l.jsx(Nt,{base64:e.botAudioBase64,mimeType:"audio/mp3"})]})]})]})}function va(){const[e,t]=i.useState(()=>Le()),n=Ee(),r=s=>{ia(s),t(Le())},a=()=>{window.confirm("确认清空所有历史记录?")&&(oa(),t([]))};return l.jsxs("div",{className:"hist-page",children:[l.jsxs("div",{className:"hist-toolbar",children:[l.jsx("button",{className:"hist-back",onClick:()=>n("/"),children:"← 返回"}),l.jsx("h1",{className:"hist-heading",children:"历史对话"}),l.jsx("button",{className:"hist-clear",onClick:a,disabled:e.length===0,children:"清空"})]}),e.length===0?l.jsx("p",{className:"hist-empty",children:"暂无历史记录"}):l.jsx("div",{className:"hist-list",children:e.map(s=>l.jsx(ya,{entry:s,onDelete:r},s.id))})]})}const jt={volcAppId:"",volcAccessToken:"",openrouterApiKey:"",llmModel:"deepseek/deepseek-chat",volcTtsVoice:"zh_female_vv_uranus_bigtts",volcAsrResource:"volc.bigasr.sauc.duration"};function ba(){const e=Ee(),[t,n]=i.useState(()=>Oe()),[r,a]=i.useState(jt),[s,o]=i.useState(!0),[u,c]=i.useState(!1),[d,f]=i.useState({});i.useEffect(()=>{ta().then(m=>{m&&!m.error&&a(y=>({...y,...m}))}).finally(()=>o(!1))},[]);const h=(m,y)=>{n(w=>({...w,[m]:y})),c(!1)},p=(m,y)=>{a(w=>({...w,[m]:y})),c(!1)},b=m=>f(y=>({...y,[m]:!y[m]})),v=async()=>{ua(t);try{await na(r),c(!0),setTimeout(()=>c(!1),2e3)}catch(m){alert("API 配置保存失败:"+m.message)}},g=()=>{if(window.confirm("重置所有设置为默认值?")){const m=ca();n(m),a(jt),c(!1)}};return l.jsxs("div",{className:"settings-page",children:[l.jsxs("div",{className:"settings-toolbar",children:[l.jsx("button",{className:"settings-back",onClick:()=>e("/"),children:"← 返回"}),l.jsx("h1",{className:"settings-heading",children:"全局设置"}),l.jsx("button",{className:"settings-save",onClick:v,children:u?"✓ 已保存":"保存"})]}),l.jsxs("div",{className:"settings-body",children:[l.jsxs("section",{className:"settings-section",children:[l.jsx("h2",{className:"settings-section-title",children:"🔑 API 配置"}),l.jsx("p",{className:"settings-hint-block",children:"配置保存在服务器数据库中。Token 字段显示 *** 表示已配置,留空则保持不变。"}),s?l.jsx("p",{className:"settings-hint-block",children:"加载中…"}):l.jsxs(l.Fragment,{children:[l.jsxs("label",{className:"settings-label",children:["Volcengine App ID",l.jsx("input",{className:"settings-input",value:r.volcAppId,onChange:m=>p("volcAppId",m.target.value),placeholder:"填写火山引擎 App ID"})]}),l.jsxs("label",{className:"settings-label",children:["Volcengine Access Token",l.jsxs("div",{className:"settings-secret-row",children:[l.jsx("input",{className:"settings-input",type:d.volcAccessToken?"text":"password",value:r.volcAccessToken,onChange:m=>p("volcAccessToken",m.target.value),placeholder:"留空则保持不变"}),l.jsx("button",{className:"settings-show-btn",onClick:()=>b("volcAccessToken"),children:d.volcAccessToken?"隐藏":"显示"})]})]}),l.jsxs("label",{className:"settings-label",children:["OpenRouter API Key",l.jsxs("div",{className:"settings-secret-row",children:[l.jsx("input",{className:"settings-input",type:d.openrouterApiKey?"text":"password",value:r.openrouterApiKey,onChange:m=>p("openrouterApiKey",m.target.value),placeholder:"留空则保持不变"}),l.jsx("button",{className:"settings-show-btn",onClick:()=>b("openrouterApiKey"),children:d.openrouterApiKey?"隐藏":"显示"})]})]}),l.jsxs("label",{className:"settings-label",children:["LLM 模型",l.jsx("input",{className:"settings-input",value:r.llmModel,onChange:m=>p("llmModel",m.target.value),placeholder:"deepseek/deepseek-chat"}),l.jsx("span",{className:"settings-hint",children:"OpenRouter 模型 ID,例如 openai/gpt-4o"})]}),l.jsxs("label",{className:"settings-label",children:["ASR Resource ID",l.jsx("input",{className:"settings-input",value:r.volcAsrResource,onChange:m=>p("volcAsrResource",m.target.value),placeholder:"volc.bigasr.sauc.duration"})]})]})]}),l.jsxs("section",{className:"settings-section",children:[l.jsx("h2",{className:"settings-section-title",children:"🤖 机器人身份"}),l.jsxs("label",{className:"settings-label",children:["名字",l.jsx("input",{className:"settings-input",value:t.robotName,onChange:m=>h("robotName",m.target.value),placeholder:"JBOT"})]}),l.jsxs("label",{className:"settings-label",children:["人格描述 ",l.jsx("span",{className:"settings-hint",children:"会注入到每轮对话的系统提示"}),l.jsx("textarea",{className:"settings-textarea",rows:3,value:t.robotPersonality,onChange:m=>h("robotPersonality",m.target.value),placeholder:"你是一个友好、简洁、偶尔幽默的AI机器人助手。回复控制在80字以内。"})]})]}),l.jsxs("section",{className:"settings-section",children:[l.jsx("h2",{className:"settings-section-title",children:"👤 用户背景"}),l.jsxs("label",{className:"settings-label",children:["关于你 ",l.jsx("span",{className:"settings-hint",children:"机器人会了解你的背景,更好地回答"}),l.jsx("textarea",{className:"settings-textarea",rows:3,value:t.extraContext||"",onChange:m=>h("extraContext",m.target.value),placeholder:"例如:我叫小明,是一名软件工程师,在上海工作,对AI和嵌入式系统感兴趣。"})]})]}),l.jsxs("section",{className:"settings-section",children:[l.jsx("h2",{className:"settings-section-title",children:"🌐 语言"}),l.jsx("div",{className:"settings-radio-group",children:[{value:"auto",label:"自动(跟随用户语言)"},{value:"zh-CN",label:"中文"},{value:"en-US",label:"English"}].map(m=>l.jsxs("label",{className:"settings-radio-label",children:[l.jsx("input",{type:"radio",name:"language",value:m.value,checked:t.language===m.value,onChange:()=>h("language",m.value)}),m.label]},m.value))})]}),l.jsxs("section",{className:"settings-section",children:[l.jsx("h2",{className:"settings-section-title",children:"🔊 语音音色"}),l.jsx("div",{className:"settings-voice-grid",children:Yt.map(m=>l.jsxs("button",{className:`settings-voice-btn ${!t.customVoiceId&&t.voiceId===m.id?"active":""}`,onClick:()=>{h("voiceId",m.id),h("customVoiceId","")},children:[l.jsx("span",{className:"voice-btn-label",children:m.label}),l.jsxs("span",{className:"voice-btn-meta",children:[m.lang," · ",m.gender," · ",m.style]})]},m.id))}),l.jsxs("label",{className:"settings-label settings-label-mt",children:["自定义音色 ID ",l.jsx("span",{className:"settings-hint",children:"填写后会覆盖上方选择"}),l.jsx("input",{className:"settings-input",value:t.customVoiceId||"",onChange:m=>h("customVoiceId",m.target.value),placeholder:"例如 zh_female_vv_uranus_bigtts"})]}),l.jsxs("p",{className:"settings-hint-block",children:["完整音色列表见"," ",l.jsx("a",{href:"https://www.volcengine.com/docs/6561/1257544",target:"_blank",rel:"noreferrer",children:"火山引擎 TTS 音色文档"})]})]}),l.jsxs("section",{className:"settings-section settings-section-danger",children:[l.jsx("h2",{className:"settings-section-title",children:"⚠️ 重置"}),l.jsx("button",{className:"settings-reset-btn",onClick:g,children:"重置为默认设置"})]})]})]})}nn.createRoot(document.getElementById("jbot-root")).render(l.jsx(i.StrictMode,{children:l.jsx(kr,{basename:"/jbot",children:l.jsxs(tr,{children:[l.jsx(Te,{path:"/",element:l.jsx(ga,{})}),l.jsx(Te,{path:"/history",element:l.jsx(va,{})}),l.jsx(Te,{path:"/settings",element:l.jsx(ba,{})})]})})}));