Files
links/static/dist/jbot.js
T
junvandCopilot a87b4643fd feat: port JBOT AI robot face into links as Django app
- Add jbot Django app (jbot/__init__.py, apps.py, urls.py, api_urls.py)
- Python backend (jbot/views.py): TTS 2.0, ASR 1.0 BigASR, LLM/memory via OpenRouter
- React SPA frontend at /jbot/ (static_src/jbot/): RobotFace, ChatPanel, expressions,
  voices, push-to-talk, history, memory, settings pages
- Vite entry jbot: static_src/jbot/main.jsx → dist/jbot.js + dist/jbot.css
- react-router-dom added; BrowserRouter basename=/jbot for SPA routing
- core/settings.py: added jbot to INSTALLED_APPS
- core/urls.py: /jbot/ + /api/jbot/ URL includes
- pyproject.toml: websocket-client>=1.9.0 for BigASR binary WS protocol
- Dockerfile: ffmpeg added to production apt-get for WebM→PCM audio conversion
- k8s/manifest.yaml: VOLC_APP_ID, VOLC_ACCESS_TOKEN, OPENROUTER_API_KEY env vars
  via jbot-credentials Secret; LLM_MODEL, VOLC_TTS_VOICE, VOLC_ASR_RESOURCE defaults

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

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