From fbbe35c9c09c963219a840b93728ac06a5f70c89 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Fri, 6 Feb 2026 00:49:38 +0500 Subject: [PATCH] basic frontend added --- .gitignore | 4 + .../assets/webui/assets/index-BR9M1eFw.js | 17 + .../assets/webui/assets/index-jwVA1VU4.css | 1 + app/src/main/assets/webui/index.html | 14 + .../sh/sar/textpipe/server/TextpipeServer.kt | 9 +- .../textpipe/server/routes/StatusRoutes.kt | 4 - .../sar/textpipe/server/routes/WebUIRoutes.kt | 52 + .../sar/textpipe/service/TextpipeService.kt | 2 +- webui/index.html | 13 + webui/package-lock.json | 1207 +++++++++++++++++ webui/package.json | 1 + webui/src/App.vue | 35 + webui/src/api.js | 42 + webui/src/components/LoginScreen.vue | 59 + webui/src/components/MainApp.vue | 213 +++ webui/src/components/MessageThread.vue | 102 ++ webui/src/main.js | 5 + webui/src/style.css | 590 ++++++++ webui/vite.config.js | 11 + 19 files changed, 2374 insertions(+), 7 deletions(-) create mode 100644 app/src/main/assets/webui/assets/index-BR9M1eFw.js create mode 100644 app/src/main/assets/webui/assets/index-jwVA1VU4.css create mode 100644 app/src/main/assets/webui/index.html create mode 100644 app/src/main/java/sh/sar/textpipe/server/routes/WebUIRoutes.kt create mode 100644 webui/index.html create mode 100644 webui/package-lock.json create mode 100644 webui/package.json create mode 100644 webui/src/App.vue create mode 100644 webui/src/api.js create mode 100644 webui/src/components/LoginScreen.vue create mode 100644 webui/src/components/MainApp.vue create mode 100644 webui/src/components/MessageThread.vue create mode 100644 webui/src/main.js create mode 100644 webui/src/style.css create mode 100644 webui/vite.config.js diff --git a/.gitignore b/.gitignore index aa724b7..e822c2f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ .externalNativeBuild .cxx local.properties + +# WebUI +webui/node_modules +webui/dist diff --git a/app/src/main/assets/webui/assets/index-BR9M1eFw.js b/app/src/main/assets/webui/assets/index-BR9M1eFw.js new file mode 100644 index 0000000..5567307 --- /dev/null +++ b/app/src/main/assets/webui/assets/index-BR9M1eFw.js @@ -0,0 +1,17 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&n(o)}).observe(document,{childList:!0,subtree:!0});function s(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function n(i){if(i.ep)return;i.ep=!0;const r=s(i);fetch(i.href,r)}})();/** +* @vue/shared v3.5.27 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function js(e){const t=Object.create(null);for(const s of e.split(","))t[s]=1;return s=>s in t}const U={},it=[],Ee=()=>{},Wn=()=>!1,ns=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Hs=e=>e.startsWith("onUpdate:"),Q=Object.assign,Us=(e,t)=>{const s=e.indexOf(t);s>-1&&e.splice(s,1)},zi=Object.prototype.hasOwnProperty,j=(e,t)=>zi.call(e,t),R=Array.isArray,rt=e=>is(e)==="[object Map]",kn=e=>is(e)==="[object Set]",$=e=>typeof e=="function",J=e=>typeof e=="string",ke=e=>typeof e=="symbol",k=e=>e!==null&&typeof e=="object",qn=e=>(k(e)||$(e))&&$(e.then)&&$(e.catch),Gn=Object.prototype.toString,is=e=>Gn.call(e),Zi=e=>is(e).slice(8,-1),Jn=e=>is(e)==="[object Object]",Vs=e=>J(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,_t=js(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),rs=e=>{const t=Object.create(null);return s=>t[s]||(t[s]=e(s))},Qi=/-\w/g,Be=rs(e=>e.replace(Qi,t=>t.slice(1).toUpperCase())),er=/\B([A-Z])/g,qe=rs(e=>e.replace(er,"-$1").toLowerCase()),Yn=rs(e=>e.charAt(0).toUpperCase()+e.slice(1)),ms=rs(e=>e?`on${Yn(e)}`:""),Ve=(e,t)=>!Object.is(e,t),Ut=(e,...t)=>{for(let s=0;s{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:s})},Bs=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let hn;const os=()=>hn||(hn=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Ws(e){if(R(e)){const t={};for(let s=0;s{if(s){const n=s.split(sr);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Tt(e){let t="";if(J(e))t=e;else if(R(e))for(let s=0;s!!(e&&e.__v_isRef===!0),fe=e=>J(e)?e:e==null?"":R(e)||k(e)&&(e.toString===Gn||!$(e.toString))?Zn(e)?fe(e.value):JSON.stringify(e,Qn,2):String(e),Qn=(e,t)=>Zn(t)?Qn(e,t.value):rt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((s,[n,i],r)=>(s[_s(n,r)+" =>"]=i,s),{})}:kn(t)?{[`Set(${t.size})`]:[...t.values()].map(s=>_s(s))}:ke(t)?_s(t):k(t)&&!R(t)&&!Jn(t)?String(t):t,_s=(e,t="")=>{var s;return ke(e)?`Symbol(${(s=e.description)!=null?s:t})`:e};/** +* @vue/reactivity v3.5.27 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let ce;class lr{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=ce,!t&&ce&&(this.index=(ce.scopes||(ce.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,s;if(this.scopes)for(t=0,s=this.scopes.length;t0&&--this._on===0&&(ce=this.prevScope,this.prevScope=void 0)}stop(t){if(this._active){this._active=!1;let s,n;for(s=0,n=this.effects.length;s0)return;if(bt){let t=bt;for(bt=void 0;t;){const s=t.next;t.next=void 0,t.flags&=-9,t=s}}let e;for(;vt;){let t=vt;for(vt=void 0;t;){const s=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=s}}if(e)throw e}function ni(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function ii(e){let t,s=e.depsTail,n=s;for(;n;){const i=n.prevDep;n.version===-1?(n===s&&(s=i),Gs(n),fr(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=i}e.deps=t,e.depsTail=s}function Ms(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(ri(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function ri(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Ct)||(e.globalVersion=Ct,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Ms(e))))return;e.flags|=2;const t=e.dep,s=W,n=me;W=e,me=!0;try{ni(e);const i=e.fn(e._value);(t.version===0||Ve(i,e._value))&&(e.flags|=128,e._value=i,t.version++)}catch(i){throw t.version++,i}finally{W=s,me=n,ii(e),e.flags&=-3}}function Gs(e,t=!1){const{dep:s,prevSub:n,nextSub:i}=e;if(n&&(n.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=n,e.nextSub=void 0),s.subs===e&&(s.subs=n,!n&&s.computed)){s.computed.flags&=-5;for(let r=s.computed.deps;r;r=r.nextDep)Gs(r,!0)}!t&&!--s.sc&&s.map&&s.map.delete(s.key)}function fr(e){const{prevDep:t,nextDep:s}=e;t&&(t.nextDep=s,e.prevDep=void 0),s&&(s.prevDep=t,e.nextDep=void 0)}let me=!0;const oi=[];function $e(){oi.push(me),me=!1}function Fe(){const e=oi.pop();me=e===void 0?!0:e}function pn(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const s=W;W=void 0;try{t()}finally{W=s}}}let Ct=0;class ur{constructor(t,s){this.sub=t,this.dep=s,this.version=s.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class Js{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!W||!me||W===this.computed)return;let s=this.activeLink;if(s===void 0||s.sub!==W)s=this.activeLink=new ur(W,this),W.deps?(s.prevDep=W.depsTail,W.depsTail.nextDep=s,W.depsTail=s):W.deps=W.depsTail=s,li(s);else if(s.version===-1&&(s.version=this.version,s.nextDep)){const n=s.nextDep;n.prevDep=s.prevDep,s.prevDep&&(s.prevDep.nextDep=n),s.prevDep=W.depsTail,s.nextDep=void 0,W.depsTail.nextDep=s,W.depsTail=s,W.deps===s&&(W.deps=n)}return s}trigger(t){this.version++,Ct++,this.notify(t)}notify(t){ks();try{for(let s=this.subs;s;s=s.prevSub)s.sub.notify()&&s.sub.dep.notify()}finally{qs()}}}function li(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)li(n)}const s=e.dep.subs;s!==e&&(e.prevSub=s,s&&(s.nextSub=e)),e.dep.subs=e}}const As=new WeakMap,Qe=Symbol(""),Os=Symbol(""),Et=Symbol("");function z(e,t,s){if(me&&W){let n=As.get(e);n||As.set(e,n=new Map);let i=n.get(s);i||(n.set(s,i=new Js),i.map=n,i.key=s),i.track()}}function Pe(e,t,s,n,i,r){const o=As.get(e);if(!o){Ct++;return}const l=f=>{f&&f.trigger()};if(ks(),t==="clear")o.forEach(l);else{const f=R(e),d=f&&Vs(s);if(f&&s==="length"){const a=Number(n);o.forEach((p,w)=>{(w==="length"||w===Et||!ke(w)&&w>=a)&&l(p)})}else switch((s!==void 0||o.has(void 0))&&l(o.get(s)),d&&l(o.get(Et)),t){case"add":f?d&&l(o.get("length")):(l(o.get(Qe)),rt(e)&&l(o.get(Os)));break;case"delete":f||(l(o.get(Qe)),rt(e)&&l(o.get(Os)));break;case"set":rt(e)&&l(o.get(Qe));break}}qs()}function tt(e){const t=N(e);return t===e?t:(z(t,"iterate",Et),ge(e)?t:t.map(_e))}function ls(e){return z(e=N(e),"iterate",Et),e}function je(e,t){return De(e)?ct(et(e)?_e(t):t):_e(t)}const ar={__proto__:null,[Symbol.iterator](){return bs(this,Symbol.iterator,e=>je(this,e))},concat(...e){return tt(this).concat(...e.map(t=>R(t)?tt(t):t))},entries(){return bs(this,"entries",e=>(e[1]=je(this,e[1]),e))},every(e,t){return Ae(this,"every",e,t,void 0,arguments)},filter(e,t){return Ae(this,"filter",e,t,s=>s.map(n=>je(this,n)),arguments)},find(e,t){return Ae(this,"find",e,t,s=>je(this,s),arguments)},findIndex(e,t){return Ae(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return Ae(this,"findLast",e,t,s=>je(this,s),arguments)},findLastIndex(e,t){return Ae(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return Ae(this,"forEach",e,t,void 0,arguments)},includes(...e){return ys(this,"includes",e)},indexOf(...e){return ys(this,"indexOf",e)},join(e){return tt(this).join(e)},lastIndexOf(...e){return ys(this,"lastIndexOf",e)},map(e,t){return Ae(this,"map",e,t,void 0,arguments)},pop(){return pt(this,"pop")},push(...e){return pt(this,"push",e)},reduce(e,...t){return gn(this,"reduce",e,t)},reduceRight(e,...t){return gn(this,"reduceRight",e,t)},shift(){return pt(this,"shift")},some(e,t){return Ae(this,"some",e,t,void 0,arguments)},splice(...e){return pt(this,"splice",e)},toReversed(){return tt(this).toReversed()},toSorted(e){return tt(this).toSorted(e)},toSpliced(...e){return tt(this).toSpliced(...e)},unshift(...e){return pt(this,"unshift",e)},values(){return bs(this,"values",e=>je(this,e))}};function bs(e,t,s){const n=ls(e),i=n[t]();return n!==e&&!ge(e)&&(i._next=i.next,i.next=()=>{const r=i._next();return r.done||(r.value=s(r.value)),r}),i}const dr=Array.prototype;function Ae(e,t,s,n,i,r){const o=ls(e),l=o!==e&&!ge(e),f=o[t];if(f!==dr[t]){const p=f.apply(e,r);return l?_e(p):p}let d=s;o!==e&&(l?d=function(p,w){return s.call(this,je(e,p),w,e)}:s.length>2&&(d=function(p,w){return s.call(this,p,w,e)}));const a=f.call(o,d,n);return l&&i?i(a):a}function gn(e,t,s,n){const i=ls(e);let r=s;return i!==e&&(ge(e)?s.length>3&&(r=function(o,l,f){return s.call(this,o,l,f,e)}):r=function(o,l,f){return s.call(this,o,je(e,l),f,e)}),i[t](r,...n)}function ys(e,t,s){const n=N(e);z(n,"iterate",Et);const i=n[t](...s);return(i===-1||i===!1)&&Zs(s[0])?(s[0]=N(s[0]),n[t](...s)):i}function pt(e,t,s=[]){$e(),ks();const n=N(e)[t].apply(e,s);return qs(),Fe(),n}const hr=js("__proto__,__v_isRef,__isVue"),ci=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ke));function pr(e){ke(e)||(e=String(e));const t=N(this);return z(t,"has",e),t.hasOwnProperty(e)}class fi{constructor(t=!1,s=!1){this._isReadonly=t,this._isShallow=s}get(t,s,n){if(s==="__v_skip")return t.__v_skip;const i=this._isReadonly,r=this._isShallow;if(s==="__v_isReactive")return!i;if(s==="__v_isReadonly")return i;if(s==="__v_isShallow")return r;if(s==="__v_raw")return n===(i?r?Tr:hi:r?di:ai).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const o=R(t);if(!i){let f;if(o&&(f=ar[s]))return f;if(s==="hasOwnProperty")return pr}const l=Reflect.get(t,s,Z(t)?t:n);if((ke(s)?ci.has(s):hr(s))||(i||z(t,"get",s),r))return l;if(Z(l)){const f=o&&Vs(s)?l:l.value;return i&&k(f)?Is(f):f}return k(l)?i?Is(l):Xs(l):l}}class ui extends fi{constructor(t=!1){super(!1,t)}set(t,s,n,i){let r=t[s];const o=R(t)&&Vs(s);if(!this._isShallow){const d=De(r);if(!ge(n)&&!De(n)&&(r=N(r),n=N(n)),!o&&Z(r)&&!Z(n))return d||(r.value=n),!0}const l=o?Number(s)e,Kt=e=>Reflect.getPrototypeOf(e);function br(e,t,s){return function(...n){const i=this.__v_raw,r=N(i),o=rt(r),l=e==="entries"||e===Symbol.iterator&&o,f=e==="keys"&&o,d=i[e](...n),a=s?Ps:t?ct:_e;return!t&&z(r,"iterate",f?Os:Qe),Q(Object.create(d),{next(){const{value:p,done:w}=d.next();return w?{value:p,done:w}:{value:l?[a(p[0]),a(p[1])]:a(p),done:w}}})}}function Nt(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function yr(e,t){const s={get(i){const r=this.__v_raw,o=N(r),l=N(i);e||(Ve(i,l)&&z(o,"get",i),z(o,"get",l));const{has:f}=Kt(o),d=t?Ps:e?ct:_e;if(f.call(o,i))return d(r.get(i));if(f.call(o,l))return d(r.get(l));r!==o&&r.get(i)},get size(){const i=this.__v_raw;return!e&&z(N(i),"iterate",Qe),i.size},has(i){const r=this.__v_raw,o=N(r),l=N(i);return e||(Ve(i,l)&&z(o,"has",i),z(o,"has",l)),i===l?r.has(i):r.has(i)||r.has(l)},forEach(i,r){const o=this,l=o.__v_raw,f=N(l),d=t?Ps:e?ct:_e;return!e&&z(f,"iterate",Qe),l.forEach((a,p)=>i.call(r,d(a),d(p),o))}};return Q(s,e?{add:Nt("add"),set:Nt("set"),delete:Nt("delete"),clear:Nt("clear")}:{add(i){!t&&!ge(i)&&!De(i)&&(i=N(i));const r=N(this);return Kt(r).has.call(r,i)||(r.add(i),Pe(r,"add",i,i)),this},set(i,r){!t&&!ge(r)&&!De(r)&&(r=N(r));const o=N(this),{has:l,get:f}=Kt(o);let d=l.call(o,i);d||(i=N(i),d=l.call(o,i));const a=f.call(o,i);return o.set(i,r),d?Ve(r,a)&&Pe(o,"set",i,r):Pe(o,"add",i,r),this},delete(i){const r=N(this),{has:o,get:l}=Kt(r);let f=o.call(r,i);f||(i=N(i),f=o.call(r,i)),l&&l.call(r,i);const d=r.delete(i);return f&&Pe(r,"delete",i,void 0),d},clear(){const i=N(this),r=i.size!==0,o=i.clear();return r&&Pe(i,"clear",void 0,void 0),o}}),["keys","values","entries",Symbol.iterator].forEach(i=>{s[i]=br(i,e,t)}),s}function Ys(e,t){const s=yr(e,t);return(n,i,r)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?n:Reflect.get(j(s,i)&&i in n?s:n,i,r)}const xr={get:Ys(!1,!1)},wr={get:Ys(!1,!0)},Sr={get:Ys(!0,!1)};const ai=new WeakMap,di=new WeakMap,hi=new WeakMap,Tr=new WeakMap;function Cr(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Er(e){return e.__v_skip||!Object.isExtensible(e)?0:Cr(Zi(e))}function Xs(e){return De(e)?e:zs(e,!1,mr,xr,ai)}function Mr(e){return zs(e,!1,vr,wr,di)}function Is(e){return zs(e,!0,_r,Sr,hi)}function zs(e,t,s,n,i){if(!k(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const r=Er(e);if(r===0)return e;const o=i.get(e);if(o)return o;const l=new Proxy(e,r===2?n:s);return i.set(e,l),l}function et(e){return De(e)?et(e.__v_raw):!!(e&&e.__v_isReactive)}function De(e){return!!(e&&e.__v_isReadonly)}function ge(e){return!!(e&&e.__v_isShallow)}function Zs(e){return e?!!e.__v_raw:!1}function N(e){const t=e&&e.__v_raw;return t?N(t):e}function Ar(e){return!j(e,"__v_skip")&&Object.isExtensible(e)&&Xn(e,"__v_skip",!0),e}const _e=e=>k(e)?Xs(e):e,ct=e=>k(e)?Is(e):e;function Z(e){return e?e.__v_isRef===!0:!1}function ne(e){return Or(e,!1)}function Or(e,t){return Z(e)?e:new Pr(e,t)}class Pr{constructor(t,s){this.dep=new Js,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=s?t:N(t),this._value=s?t:_e(t),this.__v_isShallow=s}get value(){return this.dep.track(),this._value}set value(t){const s=this._rawValue,n=this.__v_isShallow||ge(t)||De(t);t=n?t:N(t),Ve(t,s)&&(this._rawValue=t,this._value=n?t:_e(t),this.dep.trigger())}}function Ir(e){return Z(e)?e.value:e}const Rr={get:(e,t,s)=>t==="__v_raw"?e:Ir(Reflect.get(e,t,s)),set:(e,t,s,n)=>{const i=e[t];return Z(i)&&!Z(s)?(i.value=s,!0):Reflect.set(e,t,s,n)}};function pi(e){return et(e)?e:new Proxy(e,Rr)}class $r{constructor(t,s,n){this.fn=t,this.setter=s,this._value=void 0,this.dep=new Js(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Ct-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!s,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&W!==this)return si(this,!0),!0}get value(){const t=this.dep.track();return ri(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function Fr(e,t,s=!1){let n,i;return $(e)?n=e:(n=e.get,i=e.set),new $r(n,i,s)}const jt={},qt=new WeakMap;let Ze;function Dr(e,t=!1,s=Ze){if(s){let n=qt.get(s);n||qt.set(s,n=[]),n.push(e)}}function Lr(e,t,s=U){const{immediate:n,deep:i,once:r,scheduler:o,augmentJob:l,call:f}=s,d=x=>i?x:ge(x)||i===!1||i===0?Ie(x,1):Ie(x);let a,p,w,T,F=!1,D=!1;if(Z(e)?(p=()=>e.value,F=ge(e)):et(e)?(p=()=>d(e),F=!0):R(e)?(D=!0,F=e.some(x=>et(x)||ge(x)),p=()=>e.map(x=>{if(Z(x))return x.value;if(et(x))return d(x);if($(x))return f?f(x,2):x()})):$(e)?t?p=f?()=>f(e,2):e:p=()=>{if(w){$e();try{w()}finally{Fe()}}const x=Ze;Ze=a;try{return f?f(e,3,[T]):e(T)}finally{Ze=x}}:p=Ee,t&&i){const x=p,q=i===!0?1/0:i;p=()=>Ie(x(),q)}const X=cr(),K=()=>{a.stop(),X&&X.active&&Us(X.effects,a)};if(r&&t){const x=t;t=(...q)=>{x(...q),K()}}let O=D?new Array(e.length).fill(jt):jt;const E=x=>{if(!(!(a.flags&1)||!a.dirty&&!x))if(t){const q=a.run();if(i||F||(D?q.some((Ke,ve)=>Ve(Ke,O[ve])):Ve(q,O))){w&&w();const Ke=Ze;Ze=a;try{const ve=[q,O===jt?void 0:D&&O[0]===jt?[]:O,T];O=q,f?f(t,3,ve):t(...ve)}finally{Ze=Ke}}}else a.run()};return l&&l(E),a=new ei(p),a.scheduler=o?()=>o(E,!1):E,T=x=>Dr(x,!1,a),w=a.onStop=()=>{const x=qt.get(a);if(x){if(f)f(x,4);else for(const q of x)q();qt.delete(a)}},t?n?E(!0):O=a.run():o?o(E.bind(null,!0),!0):a.run(),K.pause=a.pause.bind(a),K.resume=a.resume.bind(a),K.stop=K,K}function Ie(e,t=1/0,s){if(t<=0||!k(e)||e.__v_skip||(s=s||new Map,(s.get(e)||0)>=t))return e;if(s.set(e,t),t--,Z(e))Ie(e.value,t,s);else if(R(e))for(let n=0;n{Ie(n,t,s)});else if(Jn(e)){for(const n in e)Ie(e[n],t,s);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&Ie(e[n],t,s)}return e}/** +* @vue/runtime-core v3.5.27 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Pt(e,t,s,n){try{return n?e(...n):e()}catch(i){cs(i,t,s)}}function Me(e,t,s,n){if($(e)){const i=Pt(e,t,s,n);return i&&qn(i)&&i.catch(r=>{cs(r,t,s)}),i}if(R(e)){const i=[];for(let r=0;r>>1,i=ie[n],r=Mt(i);r=Mt(s)?ie.push(e):ie.splice(Nr(t),0,e),e.flags|=1,_i()}}function _i(){Gt||(Gt=gi.then(bi))}function jr(e){R(e)?ot.push(...e):He&&e.id===-1?He.splice(st+1,0,e):e.flags&1||(ot.push(e),e.flags|=1),_i()}function mn(e,t,s=Te+1){for(;sMt(s)-Mt(n));if(ot.length=0,He){He.push(...t);return}for(He=t,st=0;ste.id==null?e.flags&2?-1:1/0:e.id;function bi(e){try{for(Te=0;Te{n._d&&En(-1);const r=Jt(t);let o;try{o=e(...i)}finally{Jt(r),n._d&&En(1)}return o};return n._n=!0,n._c=!0,n._d=!0,n}function Yt(e,t){if(pe===null)return e;const s=hs(pe),n=e.dirs||(e.dirs=[]);for(let i=0;i1)return s&&$(t)?t.call(n&&n.proxy):t}}const Vr=Symbol.for("v-scx"),Br=()=>Vt(Vr);function Bt(e,t,s){return xi(e,t,s)}function xi(e,t,s=U){const{immediate:n,deep:i,flush:r,once:o}=s,l=Q({},s),f=t&&n||!t&&r!=="post";let d;if(Ot){if(r==="sync"){const T=Br();d=T.__watcherHandles||(T.__watcherHandles=[])}else if(!f){const T=()=>{};return T.stop=Ee,T.resume=Ee,T.pause=Ee,T}}const a=re;l.call=(T,F,D)=>Me(T,a,F,D);let p=!1;r==="post"?l.scheduler=T=>{ae(T,a&&a.suspense)}:r!=="sync"&&(p=!0,l.scheduler=(T,F)=>{F?T():Qs(T)}),l.augmentJob=T=>{t&&(T.flags|=4),p&&(T.flags|=2,a&&(T.id=a.uid,T.i=a))};const w=Lr(e,t,l);return Ot&&(d?d.push(w):f&&w()),w}function Wr(e,t,s){const n=this.proxy,i=J(e)?e.includes(".")?wi(n,e):()=>n[e]:e.bind(n,n);let r;$(t)?r=t:(r=t.handler,s=t);const o=It(this),l=xi(i,r.bind(n),s);return o(),l}function wi(e,t){const s=t.split(".");return()=>{let n=e;for(let i=0;ie.__isTeleport,Gr=Symbol("_leaveCb");function en(e,t){e.shapeFlag&6&&e.component?(e.transition=t,en(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Si(e){e.ids=[e.ids[0]+e.ids[2]+++"-",0,0]}const Xt=new WeakMap;function yt(e,t,s,n,i=!1){if(R(e)){e.forEach((F,D)=>yt(F,t&&(R(t)?t[D]:t),s,n,i));return}if(xt(n)&&!i){n.shapeFlag&512&&n.type.__asyncResolved&&n.component.subTree.component&&yt(e,t,s,n.component.subTree);return}const r=n.shapeFlag&4?hs(n.component):n.el,o=i?null:r,{i:l,r:f}=e,d=t&&t.r,a=l.refs===U?l.refs={}:l.refs,p=l.setupState,w=N(p),T=p===U?Wn:F=>j(w,F);if(d!=null&&d!==f){if(_n(t),J(d))a[d]=null,T(d)&&(p[d]=null);else if(Z(d)){d.value=null;const F=t;F.k&&(a[F.k]=null)}}if($(f))Pt(f,l,12,[o,a]);else{const F=J(f),D=Z(f);if(F||D){const X=()=>{if(e.f){const K=F?T(f)?p[f]:a[f]:f.value;if(i)R(K)&&Us(K,r);else if(R(K))K.includes(r)||K.push(r);else if(F)a[f]=[r],T(f)&&(p[f]=a[f]);else{const O=[r];f.value=O,e.k&&(a[e.k]=O)}}else F?(a[f]=o,T(f)&&(p[f]=o)):D&&(f.value=o,e.k&&(a[e.k]=o))};if(o){const K=()=>{X(),Xt.delete(e)};K.id=-1,Xt.set(e,K),ae(K,s)}else _n(e),X()}}}function _n(e){const t=Xt.get(e);t&&(t.flags|=8,Xt.delete(e))}os().requestIdleCallback;os().cancelIdleCallback;const xt=e=>!!e.type.__asyncLoader,Ti=e=>e.type.__isKeepAlive;function Jr(e,t){Ci(e,"a",t)}function Yr(e,t){Ci(e,"da",t)}function Ci(e,t,s=re){const n=e.__wdc||(e.__wdc=()=>{let i=s;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(fs(t,n,s),s){let i=s.parent;for(;i&&i.parent;)Ti(i.parent.vnode)&&Xr(n,t,s,i),i=i.parent}}function Xr(e,t,s,n){const i=fs(t,e,n,!0);tn(()=>{Us(n[t],i)},s)}function fs(e,t,s=re,n=!1){if(s){const i=s[e]||(s[e]=[]),r=t.__weh||(t.__weh=(...o)=>{$e();const l=It(s),f=Me(t,s,e,o);return l(),Fe(),f});return n?i.unshift(r):i.push(r),r}}const Le=e=>(t,s=re)=>{(!Ot||e==="sp")&&fs(e,(...n)=>t(...n),s)},zr=Le("bm"),us=Le("m"),Zr=Le("bu"),Qr=Le("u"),eo=Le("bum"),tn=Le("um"),to=Le("sp"),so=Le("rtg"),no=Le("rtc");function io(e,t=re){fs("ec",e,t)}const ro=Symbol.for("v-ndc");function Ei(e,t,s,n){let i;const r=s,o=R(e);if(o||J(e)){const l=o&&et(e);let f=!1,d=!1;l&&(f=!ge(e),d=De(e),e=ls(e)),i=new Array(e.length);for(let a=0,p=e.length;at(l,f,void 0,r));else{const l=Object.keys(e);i=new Array(l.length);for(let f=0,d=l.length;fe?qi(e)?hs(e):Rs(e.parent):null,wt=Q(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Rs(e.parent),$root:e=>Rs(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Ai(e),$forceUpdate:e=>e.f||(e.f=()=>{Qs(e.update)}),$nextTick:e=>e.n||(e.n=mi.bind(e.proxy)),$watch:e=>Wr.bind(e)}),xs=(e,t)=>e!==U&&!e.__isScriptSetup&&j(e,t),oo={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:s,setupState:n,data:i,props:r,accessCache:o,type:l,appContext:f}=e;if(t[0]!=="$"){const w=o[t];if(w!==void 0)switch(w){case 1:return n[t];case 2:return i[t];case 4:return s[t];case 3:return r[t]}else{if(xs(n,t))return o[t]=1,n[t];if(i!==U&&j(i,t))return o[t]=2,i[t];if(j(r,t))return o[t]=3,r[t];if(s!==U&&j(s,t))return o[t]=4,s[t];$s&&(o[t]=0)}}const d=wt[t];let a,p;if(d)return t==="$attrs"&&z(e.attrs,"get",""),d(e);if((a=l.__cssModules)&&(a=a[t]))return a;if(s!==U&&j(s,t))return o[t]=4,s[t];if(p=f.config.globalProperties,j(p,t))return p[t]},set({_:e},t,s){const{data:n,setupState:i,ctx:r}=e;return xs(i,t)?(i[t]=s,!0):n!==U&&j(n,t)?(n[t]=s,!0):j(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(r[t]=s,!0)},has({_:{data:e,setupState:t,accessCache:s,ctx:n,appContext:i,props:r,type:o}},l){let f;return!!(s[l]||e!==U&&l[0]!=="$"&&j(e,l)||xs(t,l)||j(r,l)||j(n,l)||j(wt,l)||j(i.config.globalProperties,l)||(f=o.__cssModules)&&f[l])},defineProperty(e,t,s){return s.get!=null?e._.accessCache[t]=0:j(s,"value")&&this.set(e,t,s.value,null),Reflect.defineProperty(e,t,s)}};function vn(e){return R(e)?e.reduce((t,s)=>(t[s]=null,t),{}):e}let $s=!0;function lo(e){const t=Ai(e),s=e.proxy,n=e.ctx;$s=!1,t.beforeCreate&&bn(t.beforeCreate,e,"bc");const{data:i,computed:r,methods:o,watch:l,provide:f,inject:d,created:a,beforeMount:p,mounted:w,beforeUpdate:T,updated:F,activated:D,deactivated:X,beforeDestroy:K,beforeUnmount:O,destroyed:E,unmounted:x,render:q,renderTracked:Ke,renderTriggered:ve,errorCaptured:Ne,serverPrefetch:Rt,expose:Ge,inheritAttrs:ut,components:$t,directives:Ft,filters:ps}=t;if(d&&co(d,n,null),o)for(const G in o){const V=o[G];$(V)&&(n[G]=V.bind(s))}if(i){const G=i.call(s,s);k(G)&&(e.data=Xs(G))}if($s=!0,r)for(const G in r){const V=r[G],Je=$(V)?V.bind(s,s):$(V.get)?V.get.bind(s,s):Ee,Dt=!$(V)&&$(V.set)?V.set.bind(s):Ee,Ye=Ks({get:Je,set:Dt});Object.defineProperty(n,G,{enumerable:!0,configurable:!0,get:()=>Ye.value,set:be=>Ye.value=be})}if(l)for(const G in l)Mi(l[G],n,s,G);if(f){const G=$(f)?f.call(s):f;Reflect.ownKeys(G).forEach(V=>{Ur(V,G[V])})}a&&bn(a,e,"c");function ee(G,V){R(V)?V.forEach(Je=>G(Je.bind(s))):V&&G(V.bind(s))}if(ee(zr,p),ee(us,w),ee(Zr,T),ee(Qr,F),ee(Jr,D),ee(Yr,X),ee(io,Ne),ee(no,Ke),ee(so,ve),ee(eo,O),ee(tn,x),ee(to,Rt),R(Ge))if(Ge.length){const G=e.exposed||(e.exposed={});Ge.forEach(V=>{Object.defineProperty(G,V,{get:()=>s[V],set:Je=>s[V]=Je,enumerable:!0})})}else e.exposed||(e.exposed={});q&&e.render===Ee&&(e.render=q),ut!=null&&(e.inheritAttrs=ut),$t&&(e.components=$t),Ft&&(e.directives=Ft),Rt&&Si(e)}function co(e,t,s=Ee){R(e)&&(e=Fs(e));for(const n in e){const i=e[n];let r;k(i)?"default"in i?r=Vt(i.from||n,i.default,!0):r=Vt(i.from||n):r=Vt(i),Z(r)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>r.value,set:o=>r.value=o}):t[n]=r}}function bn(e,t,s){Me(R(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,s)}function Mi(e,t,s,n){let i=n.includes(".")?wi(s,n):()=>s[n];if(J(e)){const r=t[e];$(r)&&Bt(i,r)}else if($(e))Bt(i,e.bind(s));else if(k(e))if(R(e))e.forEach(r=>Mi(r,t,s,n));else{const r=$(e.handler)?e.handler.bind(s):t[e.handler];$(r)&&Bt(i,r,e)}}function Ai(e){const t=e.type,{mixins:s,extends:n}=t,{mixins:i,optionsCache:r,config:{optionMergeStrategies:o}}=e.appContext,l=r.get(t);let f;return l?f=l:!i.length&&!s&&!n?f=t:(f={},i.length&&i.forEach(d=>zt(f,d,o,!0)),zt(f,t,o)),k(t)&&r.set(t,f),f}function zt(e,t,s,n=!1){const{mixins:i,extends:r}=t;r&&zt(e,r,s,!0),i&&i.forEach(o=>zt(e,o,s,!0));for(const o in t)if(!(n&&o==="expose")){const l=fo[o]||s&&s[o];e[o]=l?l(e[o],t[o]):t[o]}return e}const fo={data:yn,props:xn,emits:xn,methods:mt,computed:mt,beforeCreate:te,created:te,beforeMount:te,mounted:te,beforeUpdate:te,updated:te,beforeDestroy:te,beforeUnmount:te,destroyed:te,unmounted:te,activated:te,deactivated:te,errorCaptured:te,serverPrefetch:te,components:mt,directives:mt,watch:ao,provide:yn,inject:uo};function yn(e,t){return t?e?function(){return Q($(e)?e.call(this,this):e,$(t)?t.call(this,this):t)}:t:e}function uo(e,t){return mt(Fs(e),Fs(t))}function Fs(e){if(R(e)){const t={};for(let s=0;st==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${Be(t)}Modifiers`]||e[`${qe(t)}Modifiers`];function mo(e,t,...s){if(e.isUnmounted)return;const n=e.vnode.props||U;let i=s;const r=t.startsWith("update:"),o=r&&go(n,t.slice(7));o&&(o.trim&&(i=s.map(a=>J(a)?a.trim():a)),o.number&&(i=s.map(Bs)));let l,f=n[l=ms(t)]||n[l=ms(Be(t))];!f&&r&&(f=n[l=ms(qe(t))]),f&&Me(f,e,6,i);const d=n[l+"Once"];if(d){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Me(d,e,6,i)}}const _o=new WeakMap;function Pi(e,t,s=!1){const n=s?_o:t.emitsCache,i=n.get(e);if(i!==void 0)return i;const r=e.emits;let o={},l=!1;if(!$(e)){const f=d=>{const a=Pi(d,t,!0);a&&(l=!0,Q(o,a))};!s&&t.mixins.length&&t.mixins.forEach(f),e.extends&&f(e.extends),e.mixins&&e.mixins.forEach(f)}return!r&&!l?(k(e)&&n.set(e,null),null):(R(r)?r.forEach(f=>o[f]=null):Q(o,r),k(e)&&n.set(e,o),o)}function as(e,t){return!e||!ns(t)?!1:(t=t.slice(2).replace(/Once$/,""),j(e,t[0].toLowerCase()+t.slice(1))||j(e,qe(t))||j(e,t))}function wn(e){const{type:t,vnode:s,proxy:n,withProxy:i,propsOptions:[r],slots:o,attrs:l,emit:f,render:d,renderCache:a,props:p,data:w,setupState:T,ctx:F,inheritAttrs:D}=e,X=Jt(e);let K,O;try{if(s.shapeFlag&4){const x=i||n,q=x;K=Ce(d.call(q,x,a,p,T,w,F)),O=l}else{const x=t;K=Ce(x.length>1?x(p,{attrs:l,slots:o,emit:f}):x(p,null)),O=t.props?l:vo(l)}}catch(x){St.length=0,cs(x,e,1),K=Re(We)}let E=K;if(O&&D!==!1){const x=Object.keys(O),{shapeFlag:q}=E;x.length&&q&7&&(r&&x.some(Hs)&&(O=bo(O,r)),E=ft(E,O,!1,!0))}return s.dirs&&(E=ft(E,null,!1,!0),E.dirs=E.dirs?E.dirs.concat(s.dirs):s.dirs),s.transition&&en(E,s.transition),K=E,Jt(X),K}const vo=e=>{let t;for(const s in e)(s==="class"||s==="style"||ns(s))&&((t||(t={}))[s]=e[s]);return t},bo=(e,t)=>{const s={};for(const n in e)(!Hs(n)||!(n.slice(9)in t))&&(s[n]=e[n]);return s};function yo(e,t,s){const{props:n,children:i,component:r}=e,{props:o,children:l,patchFlag:f}=t,d=r.emitsOptions;if(t.dirs||t.transition)return!0;if(s&&f>=0){if(f&1024)return!0;if(f&16)return n?Sn(n,o,d):!!o;if(f&8){const a=t.dynamicProps;for(let p=0;pObject.create(Ii),$i=e=>Object.getPrototypeOf(e)===Ii;function wo(e,t,s,n=!1){const i={},r=Ri();e.propsDefaults=Object.create(null),Fi(e,t,i,r);for(const o in e.propsOptions[0])o in i||(i[o]=void 0);s?e.props=n?i:Mr(i):e.type.props?e.props=i:e.props=r,e.attrs=r}function So(e,t,s,n){const{props:i,attrs:r,vnode:{patchFlag:o}}=e,l=N(i),[f]=e.propsOptions;let d=!1;if((n||o>0)&&!(o&16)){if(o&8){const a=e.vnode.dynamicProps;for(let p=0;p{f=!0;const[w,T]=Di(p,t,!0);Q(o,w),T&&l.push(...T)};!s&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}if(!r&&!f)return k(e)&&n.set(e,it),it;if(R(r))for(let a=0;ae==="_"||e==="_ctx"||e==="$stable",nn=e=>R(e)?e.map(Ce):[Ce(e)],Co=(e,t,s)=>{if(t._n)return t;const n=Hr((...i)=>nn(t(...i)),s);return n._c=!1,n},Li=(e,t,s)=>{const n=e._ctx;for(const i in e){if(sn(i))continue;const r=e[i];if($(r))t[i]=Co(i,r,n);else if(r!=null){const o=nn(r);t[i]=()=>o}}},Ki=(e,t)=>{const s=nn(t);e.slots.default=()=>s},Ni=(e,t,s)=>{for(const n in t)(s||!sn(n))&&(e[n]=t[n])},Eo=(e,t,s)=>{const n=e.slots=Ri();if(e.vnode.shapeFlag&32){const i=t._;i?(Ni(n,t,s),s&&Xn(n,"_",i,!0)):Li(t,n)}else t&&Ki(e,t)},Mo=(e,t,s)=>{const{vnode:n,slots:i}=e;let r=!0,o=U;if(n.shapeFlag&32){const l=t._;l?s&&l===1?r=!1:Ni(i,t,s):(r=!t.$stable,Li(t,i)),o=t}else t&&(Ki(e,t),o={default:1});if(r)for(const l in i)!sn(l)&&o[l]==null&&delete i[l]},ae=Ro;function Ao(e){return Oo(e)}function Oo(e,t){const s=os();s.__VUE__=!0;const{insert:n,remove:i,patchProp:r,createElement:o,createText:l,createComment:f,setText:d,setElementText:a,parentNode:p,nextSibling:w,setScopeId:T=Ee,insertStaticContent:F}=e,D=(c,u,h,v=null,g=null,m=null,S=void 0,y=null,b=!!u.dynamicChildren)=>{if(c===u)return;c&&!gt(c,u)&&(v=Lt(c),be(c,g,m,!0),c=null),u.patchFlag===-2&&(b=!1,u.dynamicChildren=null);const{type:_,ref:P,shapeFlag:C}=u;switch(_){case ds:X(c,u,h,v);break;case We:K(c,u,h,v);break;case Ss:c==null&&O(u,h,v,S);break;case de:$t(c,u,h,v,g,m,S,y,b);break;default:C&1?q(c,u,h,v,g,m,S,y,b):C&6?Ft(c,u,h,v,g,m,S,y,b):(C&64||C&128)&&_.process(c,u,h,v,g,m,S,y,b,dt)}P!=null&&g?yt(P,c&&c.ref,m,u||c,!u):P==null&&c&&c.ref!=null&&yt(c.ref,null,m,c,!0)},X=(c,u,h,v)=>{if(c==null)n(u.el=l(u.children),h,v);else{const g=u.el=c.el;u.children!==c.children&&d(g,u.children)}},K=(c,u,h,v)=>{c==null?n(u.el=f(u.children||""),h,v):u.el=c.el},O=(c,u,h,v)=>{[c.el,c.anchor]=F(c.children,u,h,v,c.el,c.anchor)},E=({el:c,anchor:u},h,v)=>{let g;for(;c&&c!==u;)g=w(c),n(c,h,v),c=g;n(u,h,v)},x=({el:c,anchor:u})=>{let h;for(;c&&c!==u;)h=w(c),i(c),c=h;i(u)},q=(c,u,h,v,g,m,S,y,b)=>{if(u.type==="svg"?S="svg":u.type==="math"&&(S="mathml"),c==null)Ke(u,h,v,g,m,S,y,b);else{const _=c.el&&c.el._isVueCE?c.el:null;try{_&&_._beginPatch(),Rt(c,u,g,m,S,y,b)}finally{_&&_._endPatch()}}},Ke=(c,u,h,v,g,m,S,y)=>{let b,_;const{props:P,shapeFlag:C,transition:A,dirs:I}=c;if(b=c.el=o(c.type,m,P&&P.is,P),C&8?a(b,c.children):C&16&&Ne(c.children,b,null,v,g,ws(c,m),S,y),I&&Xe(c,null,v,"created"),ve(b,c,c.scopeId,S,v),P){for(const B in P)B!=="value"&&!_t(B)&&r(b,B,null,P[B],m,v);"value"in P&&r(b,"value",null,P.value,m),(_=P.onVnodeBeforeMount)&&Se(_,v,c)}I&&Xe(c,null,v,"beforeMount");const L=Po(g,A);L&&A.beforeEnter(b),n(b,u,h),((_=P&&P.onVnodeMounted)||L||I)&&ae(()=>{_&&Se(_,v,c),L&&A.enter(b),I&&Xe(c,null,v,"mounted")},g)},ve=(c,u,h,v,g)=>{if(h&&T(c,h),v)for(let m=0;m{for(let _=b;_{const y=u.el=c.el;let{patchFlag:b,dynamicChildren:_,dirs:P}=u;b|=c.patchFlag&16;const C=c.props||U,A=u.props||U;let I;if(h&&ze(h,!1),(I=A.onVnodeBeforeUpdate)&&Se(I,h,u,c),P&&Xe(u,c,h,"beforeUpdate"),h&&ze(h,!0),(C.innerHTML&&A.innerHTML==null||C.textContent&&A.textContent==null)&&a(y,""),_?Ge(c.dynamicChildren,_,y,h,v,ws(u,g),m):S||V(c,u,y,null,h,v,ws(u,g),m,!1),b>0){if(b&16)ut(y,C,A,h,g);else if(b&2&&C.class!==A.class&&r(y,"class",null,A.class,g),b&4&&r(y,"style",C.style,A.style,g),b&8){const L=u.dynamicProps;for(let B=0;B{I&&Se(I,h,u,c),P&&Xe(u,c,h,"updated")},v)},Ge=(c,u,h,v,g,m,S)=>{for(let y=0;y{if(u!==h){if(u!==U)for(const m in u)!_t(m)&&!(m in h)&&r(c,m,u[m],null,g,v);for(const m in h){if(_t(m))continue;const S=h[m],y=u[m];S!==y&&m!=="value"&&r(c,m,y,S,g,v)}"value"in h&&r(c,"value",u.value,h.value,g)}},$t=(c,u,h,v,g,m,S,y,b)=>{const _=u.el=c?c.el:l(""),P=u.anchor=c?c.anchor:l("");let{patchFlag:C,dynamicChildren:A,slotScopeIds:I}=u;I&&(y=y?y.concat(I):I),c==null?(n(_,h,v),n(P,h,v),Ne(u.children||[],h,P,g,m,S,y,b)):C>0&&C&64&&A&&c.dynamicChildren&&c.dynamicChildren.length===A.length?(Ge(c.dynamicChildren,A,h,g,m,S,y),(u.key!=null||g&&u===g.subTree)&&ji(c,u,!0)):V(c,u,h,P,g,m,S,y,b)},Ft=(c,u,h,v,g,m,S,y,b)=>{u.slotScopeIds=y,c==null?u.shapeFlag&512?g.ctx.activate(u,h,v,S,b):ps(u,h,v,g,m,S,b):ln(c,u,b)},ps=(c,u,h,v,g,m,S)=>{const y=c.component=Ho(c,v,g);if(Ti(c)&&(y.ctx.renderer=dt),Vo(y,!1,S),y.asyncDep){if(g&&g.registerDep(y,ee,S),!c.el){const b=y.subTree=Re(We);K(null,b,u,h),c.placeholder=b.el}}else ee(y,c,u,h,g,m,S)},ln=(c,u,h)=>{const v=u.component=c.component;if(yo(c,u,h))if(v.asyncDep&&!v.asyncResolved){G(v,u,h);return}else v.next=u,v.update();else u.el=c.el,v.vnode=u},ee=(c,u,h,v,g,m,S)=>{const y=()=>{if(c.isMounted){let{next:C,bu:A,u:I,parent:L,vnode:B}=c;{const xe=Hi(c);if(xe){C&&(C.el=B.el,G(c,C,S)),xe.asyncDep.then(()=>{c.isUnmounted||y()});return}}let H=C,oe;ze(c,!1),C?(C.el=B.el,G(c,C,S)):C=B,A&&Ut(A),(oe=C.props&&C.props.onVnodeBeforeUpdate)&&Se(oe,L,C,B),ze(c,!0);const le=wn(c),ye=c.subTree;c.subTree=le,D(ye,le,p(ye.el),Lt(ye),c,g,m),C.el=le.el,H===null&&xo(c,le.el),I&&ae(I,g),(oe=C.props&&C.props.onVnodeUpdated)&&ae(()=>Se(oe,L,C,B),g)}else{let C;const{el:A,props:I}=u,{bm:L,m:B,parent:H,root:oe,type:le}=c,ye=xt(u);ze(c,!1),L&&Ut(L),!ye&&(C=I&&I.onVnodeBeforeMount)&&Se(C,H,u),ze(c,!0);{oe.ce&&oe.ce._def.shadowRoot!==!1&&oe.ce._injectChildStyle(le);const xe=c.subTree=wn(c);D(null,xe,h,v,c,g,m),u.el=xe.el}if(B&&ae(B,g),!ye&&(C=I&&I.onVnodeMounted)){const xe=u;ae(()=>Se(C,H,xe),g)}(u.shapeFlag&256||H&&xt(H.vnode)&&H.vnode.shapeFlag&256)&&c.a&&ae(c.a,g),c.isMounted=!0,u=h=v=null}};c.scope.on();const b=c.effect=new ei(y);c.scope.off();const _=c.update=b.run.bind(b),P=c.job=b.runIfDirty.bind(b);P.i=c,P.id=c.uid,b.scheduler=()=>Qs(P),ze(c,!0),_()},G=(c,u,h)=>{u.component=c;const v=c.vnode.props;c.vnode=u,c.next=null,So(c,u.props,v,h),Mo(c,u.children,h),$e(),mn(c),Fe()},V=(c,u,h,v,g,m,S,y,b=!1)=>{const _=c&&c.children,P=c?c.shapeFlag:0,C=u.children,{patchFlag:A,shapeFlag:I}=u;if(A>0){if(A&128){Dt(_,C,h,v,g,m,S,y,b);return}else if(A&256){Je(_,C,h,v,g,m,S,y,b);return}}I&8?(P&16&&at(_,g,m),C!==_&&a(h,C)):P&16?I&16?Dt(_,C,h,v,g,m,S,y,b):at(_,g,m,!0):(P&8&&a(h,""),I&16&&Ne(C,h,v,g,m,S,y,b))},Je=(c,u,h,v,g,m,S,y,b)=>{c=c||it,u=u||it;const _=c.length,P=u.length,C=Math.min(_,P);let A;for(A=0;AP?at(c,g,m,!0,!1,C):Ne(u,h,v,g,m,S,y,b,C)},Dt=(c,u,h,v,g,m,S,y,b)=>{let _=0;const P=u.length;let C=c.length-1,A=P-1;for(;_<=C&&_<=A;){const I=c[_],L=u[_]=b?Ue(u[_]):Ce(u[_]);if(gt(I,L))D(I,L,h,null,g,m,S,y,b);else break;_++}for(;_<=C&&_<=A;){const I=c[C],L=u[A]=b?Ue(u[A]):Ce(u[A]);if(gt(I,L))D(I,L,h,null,g,m,S,y,b);else break;C--,A--}if(_>C){if(_<=A){const I=A+1,L=IA)for(;_<=C;)be(c[_],g,m,!0),_++;else{const I=_,L=_,B=new Map;for(_=L;_<=A;_++){const ue=u[_]=b?Ue(u[_]):Ce(u[_]);ue.key!=null&&B.set(ue.key,_)}let H,oe=0;const le=A-L+1;let ye=!1,xe=0;const ht=new Array(le);for(_=0;_=le){be(ue,g,m,!0);continue}let we;if(ue.key!=null)we=B.get(ue.key);else for(H=L;H<=A;H++)if(ht[H-L]===0&>(ue,u[H])){we=H;break}we===void 0?be(ue,g,m,!0):(ht[we-L]=_+1,we>=xe?xe=we:ye=!0,D(ue,u[we],h,null,g,m,S,y,b),oe++)}const un=ye?Io(ht):it;for(H=un.length-1,_=le-1;_>=0;_--){const ue=L+_,we=u[ue],an=u[ue+1],dn=ue+1{const{el:m,type:S,transition:y,children:b,shapeFlag:_}=c;if(_&6){Ye(c.component.subTree,u,h,v);return}if(_&128){c.suspense.move(u,h,v);return}if(_&64){S.move(c,u,h,dt);return}if(S===de){n(m,u,h);for(let C=0;Cy.enter(m),g);else{const{leave:C,delayLeave:A,afterLeave:I}=y,L=()=>{c.ctx.isUnmounted?i(m):n(m,u,h)},B=()=>{m._isLeaving&&m[Gr](!0),C(m,()=>{L(),I&&I()})};A?A(m,L,B):B()}else n(m,u,h)},be=(c,u,h,v=!1,g=!1)=>{const{type:m,props:S,ref:y,children:b,dynamicChildren:_,shapeFlag:P,patchFlag:C,dirs:A,cacheIndex:I}=c;if(C===-2&&(g=!1),y!=null&&($e(),yt(y,null,h,c,!0),Fe()),I!=null&&(u.renderCache[I]=void 0),P&256){u.ctx.deactivate(c);return}const L=P&1&&A,B=!xt(c);let H;if(B&&(H=S&&S.onVnodeBeforeUnmount)&&Se(H,u,c),P&6)Xi(c.component,h,v);else{if(P&128){c.suspense.unmount(h,v);return}L&&Xe(c,null,u,"beforeUnmount"),P&64?c.type.remove(c,u,h,dt,v):_&&!_.hasOnce&&(m!==de||C>0&&C&64)?at(_,u,h,!1,!0):(m===de&&C&384||!g&&P&16)&&at(b,u,h),v&&cn(c)}(B&&(H=S&&S.onVnodeUnmounted)||L)&&ae(()=>{H&&Se(H,u,c),L&&Xe(c,null,u,"unmounted")},h)},cn=c=>{const{type:u,el:h,anchor:v,transition:g}=c;if(u===de){Yi(h,v);return}if(u===Ss){x(c);return}const m=()=>{i(h),g&&!g.persisted&&g.afterLeave&&g.afterLeave()};if(c.shapeFlag&1&&g&&!g.persisted){const{leave:S,delayLeave:y}=g,b=()=>S(h,m);y?y(c.el,m,b):b()}else m()},Yi=(c,u)=>{let h;for(;c!==u;)h=w(c),i(c),c=h;i(u)},Xi=(c,u,h)=>{const{bum:v,scope:g,job:m,subTree:S,um:y,m:b,a:_}=c;Cn(b),Cn(_),v&&Ut(v),g.stop(),m&&(m.flags|=8,be(S,c,u,h)),y&&ae(y,u),ae(()=>{c.isUnmounted=!0},u)},at=(c,u,h,v=!1,g=!1,m=0)=>{for(let S=m;S{if(c.shapeFlag&6)return Lt(c.component.subTree);if(c.shapeFlag&128)return c.suspense.next();const u=w(c.anchor||c.el),h=u&&u[kr];return h?w(h):u};let gs=!1;const fn=(c,u,h)=>{let v;c==null?u._vnode&&(be(u._vnode,null,null,!0),v=u._vnode.component):D(u._vnode||null,c,u,null,null,null,h),u._vnode=c,gs||(gs=!0,mn(v),vi(),gs=!1)},dt={p:D,um:be,m:Ye,r:cn,mt:ps,mc:Ne,pc:V,pbc:Ge,n:Lt,o:e};return{render:fn,hydrate:void 0,createApp:po(fn)}}function ws({type:e,props:t},s){return s==="svg"&&e==="foreignObject"||s==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:s}function ze({effect:e,job:t},s){s?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Po(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function ji(e,t,s=!1){const n=e.children,i=t.children;if(R(n)&&R(i))for(let r=0;r>1,e[s[l]]0&&(t[n]=s[r-1]),s[r]=n)}}for(r=s.length,o=s[r-1];r-- >0;)s[r]=o,o=t[o];return s}function Hi(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Hi(t)}function Cn(e){if(e)for(let t=0;te.__isSuspense;function Ro(e,t){t&&t.pendingBranch?R(e)?t.effects.push(...e):t.effects.push(e):jr(e)}const de=Symbol.for("v-fgt"),ds=Symbol.for("v-txt"),We=Symbol.for("v-cmt"),Ss=Symbol.for("v-stc"),St=[];let he=null;function Y(e=!1){St.push(he=e?null:[])}function $o(){St.pop(),he=St[St.length-1]||null}let At=1;function En(e,t=!1){At+=e,e<0&&he&&t&&(he.hasOnce=!0)}function Bi(e){return e.dynamicChildren=At>0?he||it:null,$o(),At>0&&he&&he.push(e),e}function se(e,t,s,n,i,r){return Bi(M(e,t,s,n,i,r,!0))}function Zt(e,t,s,n,i){return Bi(Re(e,t,s,n,i,!0))}function Wi(e){return e?e.__v_isVNode===!0:!1}function gt(e,t){return e.type===t.type&&e.key===t.key}const ki=({key:e})=>e??null,Wt=({ref:e,ref_key:t,ref_for:s})=>(typeof e=="number"&&(e=""+e),e!=null?J(e)||Z(e)||$(e)?{i:pe,r:e,k:t,f:!!s}:e:null);function M(e,t=null,s=null,n=0,i=null,r=e===de?0:1,o=!1,l=!1){const f={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ki(t),ref:t&&Wt(t),scopeId:yi,slotScopeIds:null,children:s,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:r,patchFlag:n,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:pe};return l?(rn(f,s),r&128&&e.normalize(f)):s&&(f.shapeFlag|=J(s)?8:16),At>0&&!o&&he&&(f.patchFlag>0||r&6)&&f.patchFlag!==32&&he.push(f),f}const Re=Fo;function Fo(e,t=null,s=null,n=0,i=null,r=!1){if((!e||e===ro)&&(e=We),Wi(e)){const l=ft(e,t,!0);return s&&rn(l,s),At>0&&!r&&he&&(l.shapeFlag&6?he[he.indexOf(e)]=l:he.push(l)),l.patchFlag=-2,l}if(qo(e)&&(e=e.__vccOpts),t){t=Do(t);let{class:l,style:f}=t;l&&!J(l)&&(t.class=Tt(l)),k(f)&&(Zs(f)&&!R(f)&&(f=Q({},f)),t.style=Ws(f))}const o=J(e)?1:Vi(e)?128:qr(e)?64:k(e)?4:$(e)?2:0;return M(e,t,s,n,i,o,r,!0)}function Do(e){return e?Zs(e)||$i(e)?Q({},e):e:null}function ft(e,t,s=!1,n=!1){const{props:i,ref:r,patchFlag:o,children:l,transition:f}=e,d=t?Ko(i||{},t):i,a={__v_isVNode:!0,__v_skip:!0,type:e.type,props:d,key:d&&ki(d),ref:t&&t.ref?s&&r?R(r)?r.concat(Wt(t)):[r,Wt(t)]:Wt(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==de?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:f,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&ft(e.ssContent),ssFallback:e.ssFallback&&ft(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return f&&n&&en(a,f.clone(a)),a}function Lo(e=" ",t=0){return Re(ds,null,e,t)}function Qt(e="",t=!1){return t?(Y(),Zt(We,null,e)):Re(We,null,e)}function Ce(e){return e==null||typeof e=="boolean"?Re(We):R(e)?Re(de,null,e.slice()):Wi(e)?Ue(e):Re(ds,null,String(e))}function Ue(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:ft(e)}function rn(e,t){let s=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(R(t))s=16;else if(typeof t=="object")if(n&65){const i=t.default;i&&(i._c&&(i._d=!1),rn(e,i()),i._c&&(i._d=!0));return}else{s=32;const i=t._;!i&&!$i(t)?t._ctx=pe:i===3&&pe&&(pe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else $(t)?(t={default:t,_ctx:pe},s=32):(t=String(t),n&64?(s=16,t=[Lo(t)]):s=8);e.children=t,e.shapeFlag|=s}function Ko(...e){const t={};for(let s=0;sre||pe;let es,Ls;{const e=os(),t=(s,n)=>{let i;return(i=e[s])||(i=e[s]=[]),i.push(n),r=>{i.length>1?i.forEach(o=>o(r)):i[0](r)}};es=t("__VUE_INSTANCE_SETTERS__",s=>re=s),Ls=t("__VUE_SSR_SETTERS__",s=>Ot=s)}const It=e=>{const t=re;return es(e),e.scope.on(),()=>{e.scope.off(),es(t)}},Mn=()=>{re&&re.scope.off(),es(null)};function qi(e){return e.vnode.shapeFlag&4}let Ot=!1;function Vo(e,t=!1,s=!1){t&&Ls(t);const{props:n,children:i}=e.vnode,r=qi(e);wo(e,n,r,t),Eo(e,i,s||t);const o=r?Bo(e,t):void 0;return t&&Ls(!1),o}function Bo(e,t){const s=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,oo);const{setup:n}=s;if(n){$e();const i=e.setupContext=n.length>1?ko(e):null,r=It(e),o=Pt(n,e,0,[e.props,i]),l=qn(o);if(Fe(),r(),(l||e.sp)&&!xt(e)&&Si(e),l){if(o.then(Mn,Mn),t)return o.then(f=>{An(e,f)}).catch(f=>{cs(f,e,0)});e.asyncDep=o}else An(e,o)}else Gi(e)}function An(e,t,s){$(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:k(t)&&(e.setupState=pi(t)),Gi(e)}function Gi(e,t,s){const n=e.type;e.render||(e.render=n.render||Ee);{const i=It(e);$e();try{lo(e)}finally{Fe(),i()}}}const Wo={get(e,t){return z(e,"get",""),e[t]}};function ko(e){const t=s=>{e.exposed=s||{}};return{attrs:new Proxy(e.attrs,Wo),slots:e.slots,emit:e.emit,expose:t}}function hs(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(pi(Ar(e.exposed)),{get(t,s){if(s in t)return t[s];if(s in wt)return wt[s](e)},has(t,s){return s in t||s in wt}})):e.proxy}function qo(e){return $(e)&&"__vccOpts"in e}const Ks=(e,t)=>Fr(e,t,Ot),Go="3.5.27";/** +* @vue/runtime-dom v3.5.27 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Ns;const On=typeof window<"u"&&window.trustedTypes;if(On)try{Ns=On.createPolicy("vue",{createHTML:e=>e})}catch{}const Ji=Ns?e=>Ns.createHTML(e):e=>e,Jo="http://www.w3.org/2000/svg",Yo="http://www.w3.org/1998/Math/MathML",Oe=typeof document<"u"?document:null,Pn=Oe&&Oe.createElement("template"),Xo={insert:(e,t,s)=>{t.insertBefore(e,s||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,s,n)=>{const i=t==="svg"?Oe.createElementNS(Jo,e):t==="mathml"?Oe.createElementNS(Yo,e):s?Oe.createElement(e,{is:s}):Oe.createElement(e);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>Oe.createTextNode(e),createComment:e=>Oe.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Oe.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,s,n,i,r){const o=s?s.previousSibling:t.lastChild;if(i&&(i===r||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),s),!(i===r||!(i=i.nextSibling)););else{Pn.innerHTML=Ji(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const l=Pn.content;if(n==="svg"||n==="mathml"){const f=l.firstChild;for(;f.firstChild;)l.appendChild(f.firstChild);l.removeChild(f)}t.insertBefore(l,s)}return[o?o.nextSibling:t.firstChild,s?s.previousSibling:t.lastChild]}},zo=Symbol("_vtc");function Zo(e,t,s){const n=e[zo];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):s?e.setAttribute("class",t):e.className=t}const In=Symbol("_vod"),Qo=Symbol("_vsh"),el=Symbol(""),tl=/(?:^|;)\s*display\s*:/;function sl(e,t,s){const n=e.style,i=J(s);let r=!1;if(s&&!i){if(t)if(J(t))for(const o of t.split(";")){const l=o.slice(0,o.indexOf(":")).trim();s[l]==null&&kt(n,l,"")}else for(const o in t)s[o]==null&&kt(n,o,"");for(const o in s)o==="display"&&(r=!0),kt(n,o,s[o])}else if(i){if(t!==s){const o=n[el];o&&(s+=";"+o),n.cssText=s,r=tl.test(s)}}else t&&e.removeAttribute("style");In in e&&(e[In]=r?n.display:"",e[Qo]&&(n.display="none"))}const Rn=/\s*!important$/;function kt(e,t,s){if(R(s))s.forEach(n=>kt(e,t,n));else if(s==null&&(s=""),t.startsWith("--"))e.setProperty(t,s);else{const n=nl(e,t);Rn.test(s)?e.setProperty(qe(n),s.replace(Rn,""),"important"):e[n]=s}}const $n=["Webkit","Moz","ms"],Ts={};function nl(e,t){const s=Ts[t];if(s)return s;let n=Be(t);if(n!=="filter"&&n in e)return Ts[t]=n;n=Yn(n);for(let i=0;i<$n.length;i++){const r=$n[i]+n;if(r in e)return Ts[t]=r}return t}const Fn="http://www.w3.org/1999/xlink";function Dn(e,t,s,n,i,r=or(t)){n&&t.startsWith("xlink:")?s==null?e.removeAttributeNS(Fn,t.slice(6,t.length)):e.setAttributeNS(Fn,t,s):s==null||r&&!zn(s)?e.removeAttribute(t):e.setAttribute(t,r?"":ke(s)?String(s):s)}function Ln(e,t,s,n,i){if(t==="innerHTML"||t==="textContent"){s!=null&&(e[t]=t==="innerHTML"?Ji(s):s);return}const r=e.tagName;if(t==="value"&&r!=="PROGRESS"&&!r.includes("-")){const l=r==="OPTION"?e.getAttribute("value")||"":e.value,f=s==null?e.type==="checkbox"?"on":"":String(s);(l!==f||!("_value"in e))&&(e.value=f),s==null&&e.removeAttribute(t),e._value=s;return}let o=!1;if(s===""||s==null){const l=typeof e[t];l==="boolean"?s=zn(s):s==null&&l==="string"?(s="",o=!0):l==="number"&&(s=0,o=!0)}try{e[t]=s}catch{}o&&e.removeAttribute(i||t)}function nt(e,t,s,n){e.addEventListener(t,s,n)}function il(e,t,s,n){e.removeEventListener(t,s,n)}const Kn=Symbol("_vei");function rl(e,t,s,n,i=null){const r=e[Kn]||(e[Kn]={}),o=r[t];if(n&&o)o.value=n;else{const[l,f]=ol(t);if(n){const d=r[t]=fl(n,i);nt(e,l,d,f)}else o&&(il(e,l,o,f),r[t]=void 0)}}const Nn=/(?:Once|Passive|Capture)$/;function ol(e){let t;if(Nn.test(e)){t={};let n;for(;n=e.match(Nn);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[e[2]===":"?e.slice(3):qe(e.slice(2)),t]}let Cs=0;const ll=Promise.resolve(),cl=()=>Cs||(ll.then(()=>Cs=0),Cs=Date.now());function fl(e,t){const s=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=s.attached)return;Me(ul(n,s.value),t,5,[n])};return s.value=e,s.attached=cl(),s}function ul(e,t){if(R(t)){const s=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{s.call(e),e._stopped=!0},t.map(n=>i=>!i._stopped&&n&&n(i))}else return t}const jn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,al=(e,t,s,n,i,r)=>{const o=i==="svg";t==="class"?Zo(e,n,o):t==="style"?sl(e,s,n):ns(t)?Hs(t)||rl(e,t,s,n,r):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):dl(e,t,n,o))?(Ln(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Dn(e,t,n,o,r,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!J(n))?Ln(e,Be(t),n,r,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Dn(e,t,n,o))};function dl(e,t,s,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&jn(t)&&$(s));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return jn(t)&&J(s)?!1:t in e}const Hn=e=>{const t=e.props["onUpdate:modelValue"]||!1;return R(t)?s=>Ut(t,s):t};function hl(e){e.target.composing=!0}function Un(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Es=Symbol("_assign");function Vn(e,t,s){return t&&(e=e.trim()),s&&(e=Bs(e)),e}const ts={created(e,{modifiers:{lazy:t,trim:s,number:n}},i){e[Es]=Hn(i);const r=n||i.props&&i.props.type==="number";nt(e,t?"change":"input",o=>{o.target.composing||e[Es](Vn(e.value,s,r))}),(s||r)&&nt(e,"change",()=>{e.value=Vn(e.value,s,r)}),t||(nt(e,"compositionstart",hl),nt(e,"compositionend",Un),nt(e,"change",Un))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:s,modifiers:{lazy:n,trim:i,number:r}},o){if(e[Es]=Hn(o),e.composing)return;const l=(r||e.type==="number")&&!/^0\d/.test(e.value)?Bs(e.value):e.value,f=t??"";l!==f&&(document.activeElement===e&&e.type!=="range"&&(n&&t===s||i&&e.value.trim()===f)||(e.value=f))}},pl=["ctrl","shift","alt","meta"],gl={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>pl.some(s=>e[`${s}Key`]&&!t.includes(s))},on=(e,t)=>{const s=e._withMods||(e._withMods={}),n=t.join(".");return s[n]||(s[n]=(i,...r)=>{for(let o=0;o{const s=e._withKeys||(e._withKeys={}),n=t.join(".");return s[n]||(s[n]=i=>{if(!("key"in i))return;const r=qe(i.key);if(t.some(o=>o===r||ml[o]===r))return e(i)})},vl=Q({patchProp:al},Xo);let Bn;function bl(){return Bn||(Bn=Ao(vl))}const yl=(...e)=>{const t=bl().createApp(...e),{mount:s}=t;return t.mount=n=>{const i=wl(n);if(!i)return;const r=t._component;!$(r)&&!r.render&&!r.template&&(r.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const o=s(i,!1,xl(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),o},t};function xl(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function wl(e){return J(e)?document.querySelector(e):e}const Ht=window.location.origin,ss={async getStatus(){const e=await fetch(`${Ht}/api/status`);if(!e.ok)throw new Error(`HTTP ${e.status}`);return e.json()},async getMessages(e){const t=await fetch(`${Ht}/api/sms/messages`,{headers:{"X-API-Key":e}});if(!t.ok)throw new Error(`HTTP ${t.status}`);return t.json()},async getMessageStatus(e,t){const s=await fetch(`${Ht}/api/sms/status/${t}`,{headers:{"X-API-Key":e}});if(!s.ok)throw new Error(`HTTP ${s.status}`);return s.json()},async sendMessage(e,t,s){const n=await fetch(`${Ht}/api/sms/send`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e},body:JSON.stringify({to:t,text:s})});if(!n.ok)throw new Error(`HTTP ${n.status}`);return n.json()}},Sl={class:"login-container"},Tl={class:"login-box"},Cl={class:"input-group"},El=["disabled"],Ml=["disabled"],Al={key:0,class:"error-message"},Ol={__name:"LoginScreen",emits:["login"],setup(e,{emit:t}){const s=t,n=ne(""),i=ne(!1),r=ne(""),o=async()=>{if(n.value){i.value=!0,r.value="";try{await ss.getMessages(n.value),s("login",n.value)}catch(l){l.message.includes("401")||l.message.includes("Unauthorized")?r.value="Invalid API key":r.value="Connection failed. Check server address."}finally{i.value=!1}}};return(l,f)=>(Y(),se("div",Sl,[M("div",Tl,[f[1]||(f[1]=M("div",{class:"login-logo"},"📱",-1)),f[2]||(f[2]=M("h1",{class:"login-title"},"Textpipe",-1)),f[3]||(f[3]=M("p",{class:"login-subtitle"},"SMS Gateway",-1)),M("form",{onSubmit:on(o,["prevent"])},[M("div",Cl,[Yt(M("input",{"onUpdate:modelValue":f[0]||(f[0]=d=>n.value=d),type:"text",placeholder:"Enter your API Key",autocomplete:"off",disabled:i.value},null,8,El),[[ts,n.value]])]),M("button",{type:"submit",class:"btn btn-primary",disabled:i.value||!n.value},fe(i.value?"Verifying...":"Login"),9,Ml)],32),r.value?(Y(),se("p",Al,fe(r.value),1)):Qt("",!0)])]))}},Pl={class:"message-thread"},Il={class:"thread-header"},Rl={class:"thread-info"},$l={class:"thread-name"},Fl={class:"thread-number"},Dl={class:"message-text"},Ll={class:"message-time"},Kl={class:"compose-container"},Nl=["onKeydown"],jl=["disabled"],Hl={__name:"MessageThread",props:["conversation","messages","apiKey"],emits:["back","messageSent"],setup(e,{emit:t}){const s=e,n=t,i=ne(""),r=ne(!1),o=ne(null);us(()=>{l()}),Bt(()=>s.messages.length,()=>{mi(l)});const l=()=>{o.value&&(o.value.scrollTop=o.value.scrollHeight)},f=p=>new Date(p).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),d=p=>{switch(p){case"pending":return"○ Sending...";case"sent":return"✓ Sent";case"delivered":return"✓✓ Delivered";case"failed":return"✕ Failed";default:return p}},a=async()=>{const p=i.value.trim();if(!(!p||r.value)){r.value=!0;try{await ss.sendMessage(s.apiKey,s.conversation.address,p),i.value="",n("messageSent")}catch(w){alert("Failed to send: "+w.message)}finally{r.value=!1}}};return(p,w)=>(Y(),se("div",Pl,[M("div",Il,[M("button",{class:"btn-back",onClick:w[0]||(w[0]=T=>p.$emit("back"))},"←"),M("div",Rl,[M("div",$l,fe(e.conversation.address),1),M("div",Fl,fe(e.messages.length)+" messages",1)])]),M("div",{class:"messages-container",ref_key:"messagesContainer",ref:o},[(Y(!0),se(de,null,Ei(e.messages,T=>(Y(),se("div",{key:T.id,class:Tt(["message",T.type==="sent"?"message-sent":"message-received"])},[M("div",Dl,fe(T.text),1),M("div",Ll,fe(f(T.timestamp)),1),T.type==="sent"?(Y(),se("div",{key:0,class:Tt(["message-status",T.status])},fe(d(T.status)),3)):Qt("",!0)],2))),128))],512),M("div",Kl,[Yt(M("textarea",{"onUpdate:modelValue":w[1]||(w[1]=T=>i.value=T),class:"compose-input",placeholder:"Type a message...",rows:"1",onKeydown:_l(on(a,["exact","prevent"]),["enter"])},null,40,Nl),[[ts,i.value]]),M("button",{class:"btn-send",disabled:!i.value.trim()||r.value,onClick:a}," ➤ ",8,jl)])]))}},Ul={class:"app-container"},Vl={class:"header"},Bl={style:{flex:"1",display:"flex","flex-direction":"column",overflow:"hidden"}},Wl={class:"conversation-list"},kl={key:0,class:"loading"},ql={key:1,class:"empty-state"},Gl=["onClick"],Jl={class:"conversation-avatar"},Yl={class:"conversation-info"},Xl={class:"conversation-name"},zl={class:"conversation-preview"},Zl={class:"conversation-meta"},Ql={class:"conversation-time"},ec={key:0,class:"conversation-badge"},tc={class:"modal"},sc={class:"modal-header"},nc={class:"modal-body"},ic={class:"form-group"},rc={class:"form-group"},oc={class:"modal-footer"},lc=["disabled"],cc={__name:"MainApp",props:["apiKey"],emits:["logout"],setup(e,{emit:t}){const s=e,n=ne([]),i=ne(!0),r=ne(null),o=ne(!1),l=ne(""),f=ne(""),d=ne(!1);let a=null;const p=Ks(()=>{const O={};return n.value.forEach(E=>{const x=E.address;O[x]||(O[x]={address:x,messages:[],lastMessage:"",timestamp:0,unread:0}),O[x].messages.push(E),E.timestamp>O[x].timestamp&&(O[x].timestamp=E.timestamp,O[x].lastMessage=E.text.substring(0,50)+(E.text.length>50?"...":""))}),Object.values(O).sort((E,x)=>x.timestamp-E.timestamp)}),w=Ks(()=>r.value?n.value.filter(O=>O.address===r.value.address).sort((O,E)=>O.timestamp-E.timestamp):[]);us(()=>{T(),a=setInterval(T,5e3)}),tn(()=>{a&&clearInterval(a)});const T=async()=>{try{const O=await ss.getMessages(s.apiKey);n.value=O.messages||[]}catch(O){console.error("Failed to load messages:",O)}finally{i.value=!1}},F=O=>{r.value=O},D=O=>O.charAt(0).toUpperCase(),X=O=>{const E=new Date(O),q=new Date-E;return q<864e5?E.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}):q<6048e5?E.toLocaleDateString([],{weekday:"short"}):E.toLocaleDateString([],{month:"short",day:"numeric"})},K=async()=>{if(!(!l.value||!f.value)){d.value=!0;try{await ss.sendMessage(s.apiKey,l.value,f.value),o.value=!1,l.value="",f.value="",await T()}catch(O){alert("Failed to send message: "+O.message)}finally{d.value=!1}}};return(O,E)=>(Y(),se("div",Ul,[M("header",Vl,[E[7]||(E[7]=M("div",{class:"header-left"},[M("h1",{class:"header-title"},"Textpipe"),M("div",{class:"header-status"},[M("span",{class:"status-dot"}),M("span",null,"Connected")])],-1)),M("button",{class:"btn-logout",onClick:E[0]||(E[0]=x=>O.$emit("logout"))},"Logout")]),M("div",Bl,[r.value?(Y(),Zt(Hl,{key:0,conversation:r.value,messages:w.value,apiKey:e.apiKey,onBack:E[1]||(E[1]=x=>r.value=null),onMessageSent:T},null,8,["conversation","messages","apiKey"])):(Y(),se(de,{key:1},[M("div",Wl,[i.value?(Y(),se("div",kl,[...E[8]||(E[8]=[M("div",{class:"spinner"},null,-1)])])):p.value.length===0?(Y(),se("div",ql,[...E[9]||(E[9]=[M("div",{class:"empty-state-icon"},"💬",-1),M("h2",{class:"empty-state-title"},"No messages yet",-1),M("p",{class:"empty-state-text"},"Start a new conversation",-1)])])):(Y(!0),se(de,{key:2},Ei(p.value,x=>(Y(),se("div",{key:x.address,class:"conversation-item",onClick:q=>F(x)},[M("div",Jl,fe(D(x.address)),1),M("div",Yl,[M("div",Xl,fe(x.address),1),M("div",zl,fe(x.lastMessage),1)]),M("div",Zl,[M("div",Ql,fe(X(x.timestamp)),1),x.unread?(Y(),se("span",ec,fe(x.unread),1)):Qt("",!0)])],8,Gl))),128))]),M("button",{class:"fab",onClick:E[2]||(E[2]=x=>o.value=!0)},"+")],64))]),o.value?(Y(),se("div",{key:0,class:"modal-overlay",onClick:E[6]||(E[6]=on(x=>o.value=!1,["self"]))},[M("div",tc,[M("div",sc,[E[10]||(E[10]=M("h2",{class:"modal-title"},"New Message",-1)),M("button",{class:"btn-close",onClick:E[3]||(E[3]=x=>o.value=!1)},"×")]),M("div",nc,[M("div",ic,[E[11]||(E[11]=M("label",null,"To",-1)),Yt(M("input",{"onUpdate:modelValue":E[4]||(E[4]=x=>l.value=x),type:"tel",placeholder:"Phone number"},null,512),[[ts,l.value]])]),M("div",rc,[E[12]||(E[12]=M("label",null,"Message",-1)),Yt(M("textarea",{"onUpdate:modelValue":E[5]||(E[5]=x=>f.value=x),placeholder:"Type your message...",rows:"4"},null,512),[[ts,f.value]])])]),M("div",oc,[M("button",{class:"btn btn-primary",disabled:!l.value||!f.value||d.value,onClick:K},fe(d.value?"Sending...":"Send"),9,lc)])])])):Qt("",!0)]))}},fc={id:"app"},uc={__name:"App",setup(e){const t=ne(!1),s=ne("");us(()=>{const r=localStorage.getItem("textpipe_api_key");r&&(s.value=r,t.value=!0)});const n=r=>{s.value=r,localStorage.setItem("textpipe_api_key",r),t.value=!0},i=()=>{s.value="",localStorage.removeItem("textpipe_api_key"),t.value=!1};return(r,o)=>(Y(),se("div",fc,[t.value?(Y(),Zt(cc,{key:1,apiKey:s.value,onLogout:i},null,8,["apiKey"])):(Y(),Zt(Ol,{key:0,onLogin:n}))]))}};yl(uc).mount("#app"); diff --git a/app/src/main/assets/webui/assets/index-jwVA1VU4.css b/app/src/main/assets/webui/assets/index-jwVA1VU4.css new file mode 100644 index 0000000..9cea169 --- /dev/null +++ b/app/src/main/assets/webui/assets/index-jwVA1VU4.css @@ -0,0 +1 @@ +*{margin:0;padding:0;box-sizing:border-box}:root{--primary: #FFA31A;--primary-dark: #CC8215;--bg-dark: #1B1B1B;--surface: #292929;--surface-light: #363636;--text-white: #FFFFFF;--text-gray: #B3B3B3;--text-dark: #8C8C8C;--success: #4CAF50;--error: #F44336;--sent: #2196F3;--received: #4CAF50}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;background-color:var(--bg-dark);color:var(--text-white);min-height:100vh}#app{min-height:100vh;display:flex;flex-direction:column}.login-container{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:20px}.login-box{background:var(--surface);border-radius:16px;padding:40px;width:100%;max-width:400px;text-align:center}.login-logo{font-size:48px;margin-bottom:16px}.login-title{font-size:24px;font-weight:600;margin-bottom:8px}.login-subtitle{color:var(--text-gray);margin-bottom:32px}.input-group{margin-bottom:20px}.input-group input{width:100%;padding:16px;border:2px solid var(--surface-light);border-radius:12px;background:var(--bg-dark);color:var(--text-white);font-size:16px;outline:none;transition:border-color .2s}.input-group input:focus{border-color:var(--primary)}.input-group input::placeholder{color:var(--text-dark)}.btn{width:100%;padding:16px;border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s}.btn-primary{background:var(--primary);color:var(--bg-dark)}.btn-primary:hover{background:var(--primary-dark)}.btn-primary:disabled{opacity:.5;cursor:not-allowed}.error-message{color:var(--error);margin-top:16px;font-size:14px}.app-container{display:flex;flex-direction:column;height:100vh}.header{background:var(--surface);padding:16px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--surface-light)}.header-left{display:flex;align-items:center;gap:12px}.header-title{font-size:20px;font-weight:600}.header-status{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--text-gray)}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--success)}.btn-logout{background:transparent;border:1px solid var(--surface-light);color:var(--text-gray);padding:8px 16px;border-radius:8px;font-size:14px;cursor:pointer;transition:all .2s}.btn-logout:hover{border-color:var(--error);color:var(--error)}.conversation-list{flex:1;overflow-y:auto;padding:8px}.conversation-item{display:flex;align-items:center;padding:16px;border-radius:12px;cursor:pointer;transition:background .2s;margin-bottom:4px}.conversation-item:hover{background:var(--surface)}.conversation-item.active{background:var(--surface-light)}.conversation-avatar{width:48px;height:48px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:600;color:var(--bg-dark);margin-right:12px;flex-shrink:0}.conversation-info{flex:1;min-width:0}.conversation-name{font-weight:500;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.conversation-preview{color:var(--text-gray);font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.conversation-meta{text-align:right;flex-shrink:0}.conversation-time{font-size:12px;color:var(--text-dark);margin-bottom:4px}.conversation-badge{background:var(--primary);color:var(--bg-dark);font-size:12px;font-weight:600;padding:2px 8px;border-radius:10px;display:inline-block}.message-thread{display:flex;flex-direction:column;height:100%}.thread-header{background:var(--surface);padding:16px 20px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--surface-light)}.btn-back{background:none;border:none;color:var(--text-white);font-size:24px;cursor:pointer;padding:4px}.thread-info{flex:1}.thread-name{font-weight:600;font-size:18px}.thread-number{color:var(--text-gray);font-size:14px}.messages-container{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:8px}.message{max-width:75%;padding:12px 16px;border-radius:18px;font-size:15px;line-height:1.4;position:relative}.message-sent{background:var(--sent);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}.message-received{background:var(--surface-light);color:var(--text-white);align-self:flex-start;border-bottom-left-radius:4px}.message-time{font-size:11px;color:#ffffffb3;margin-top:4px;text-align:right}.message-status{font-size:11px;margin-top:2px;text-align:right}.message-status.pending{color:var(--text-dark)}.message-status.sent{color:var(--text-gray)}.message-status.delivered{color:var(--success)}.message-status.failed{color:var(--error)}.compose-container{background:var(--surface);padding:16px 20px;display:flex;align-items:flex-end;gap:12px;border-top:1px solid var(--surface-light)}.compose-input{flex:1;background:var(--bg-dark);border:none;border-radius:24px;padding:12px 20px;color:var(--text-white);font-size:16px;resize:none;outline:none;max-height:120px;font-family:inherit}.compose-input::placeholder{color:var(--text-dark)}.btn-send{width:48px;height:48px;border-radius:50%;background:var(--primary);border:none;color:var(--bg-dark);font-size:20px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s;flex-shrink:0}.btn-send:hover{background:var(--primary-dark)}.btn-send:disabled{opacity:.5;cursor:not-allowed}.fab{position:fixed;bottom:24px;right:24px;width:56px;height:56px;border-radius:50%;background:var(--primary);border:none;color:var(--bg-dark);font-size:28px;cursor:pointer;box-shadow:0 4px 12px #0000004d;transition:transform .2s,background .2s}.fab:hover{background:var(--primary-dark);transform:scale(1.05)}.modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#000000b3;display:flex;align-items:center;justify-content:center;padding:20px;z-index:100}.modal{background:var(--surface);border-radius:16px;width:100%;max-width:500px;max-height:90vh;overflow:hidden;display:flex;flex-direction:column}.modal-header{padding:20px;border-bottom:1px solid var(--surface-light);display:flex;align-items:center;justify-content:space-between}.modal-title{font-size:20px;font-weight:600}.btn-close{background:none;border:none;color:var(--text-gray);font-size:24px;cursor:pointer}.modal-body{padding:20px;flex:1;overflow-y:auto}.modal-footer{padding:20px;border-top:1px solid var(--surface-light)}.form-group{margin-bottom:20px}.form-group label{display:block;margin-bottom:8px;color:var(--text-gray);font-size:14px}.form-group input,.form-group textarea{width:100%;padding:14px 16px;border:2px solid var(--surface-light);border-radius:12px;background:var(--bg-dark);color:var(--text-white);font-size:16px;outline:none;transition:border-color .2s;font-family:inherit}.form-group input:focus,.form-group textarea:focus{border-color:var(--primary)}.form-group textarea{resize:vertical;min-height:100px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:40px;text-align:center}.empty-state-icon{font-size:64px;margin-bottom:16px}.empty-state-title{font-size:20px;font-weight:600;margin-bottom:8px}.empty-state-text{color:var(--text-gray)}.loading{display:flex;align-items:center;justify-content:center;padding:40px}.spinner{width:40px;height:40px;border:3px solid var(--surface-light);border-top-color:var(--primary);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media (max-width: 600px){.login-box{padding:30px 20px}.message{max-width:85%}} diff --git a/app/src/main/assets/webui/index.html b/app/src/main/assets/webui/index.html new file mode 100644 index 0000000..25bc362 --- /dev/null +++ b/app/src/main/assets/webui/index.html @@ -0,0 +1,14 @@ + + + + + + + Textpipe SMS Gateway + + + + +
+ + diff --git a/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt b/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt index 0bfd407..9f8c936 100644 --- a/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt +++ b/app/src/main/java/sh/sar/textpipe/server/TextpipeServer.kt @@ -1,5 +1,6 @@ package sh.sar.textpipe.server +import android.content.Context import android.util.Log import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* @@ -13,18 +14,18 @@ import io.ktor.server.response.* import io.ktor.server.routing.* import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.delay -import kotlinx.coroutines.withTimeoutOrNull import kotlinx.serialization.json.Json import sh.sar.textpipe.data.model.ErrorResponse import sh.sar.textpipe.data.repository.SmsRepository import sh.sar.textpipe.server.auth.configureAuth import sh.sar.textpipe.server.routes.smsRoutes import sh.sar.textpipe.server.routes.statusRoutes +import sh.sar.textpipe.server.routes.webUIRoutes import sh.sar.textpipe.sim.SimManager -import java.net.BindException import java.net.ServerSocket class TextpipeServer( + private val context: Context, private val smsRepository: SmsRepository, private val simManager: SimManager ) { @@ -143,6 +144,10 @@ class TextpipeServer( private fun Application.configureRouting() { routing { + // Web UI routes (served from assets) + webUIRoutes(context) + + // API routes statusRoutes(simManager, { startTime }, { currentPort }) smsRoutes(smsRepository) } diff --git a/app/src/main/java/sh/sar/textpipe/server/routes/StatusRoutes.kt b/app/src/main/java/sh/sar/textpipe/server/routes/StatusRoutes.kt index 5b292c4..f367506 100644 --- a/app/src/main/java/sh/sar/textpipe/server/routes/StatusRoutes.kt +++ b/app/src/main/java/sh/sar/textpipe/server/routes/StatusRoutes.kt @@ -6,10 +6,6 @@ import sh.sar.textpipe.data.model.StatusResponse import sh.sar.textpipe.sim.SimManager fun Route.statusRoutes(simManager: SimManager, getStartTime: () -> Long, getPort: () -> Int) { - get("/") { - call.respondText("Textpipe SMS Gateway API") - } - get("/api/status") { val uptime = System.currentTimeMillis() - getStartTime() val response = StatusResponse( diff --git a/app/src/main/java/sh/sar/textpipe/server/routes/WebUIRoutes.kt b/app/src/main/java/sh/sar/textpipe/server/routes/WebUIRoutes.kt new file mode 100644 index 0000000..4ea2493 --- /dev/null +++ b/app/src/main/java/sh/sar/textpipe/server/routes/WebUIRoutes.kt @@ -0,0 +1,52 @@ +package sh.sar.textpipe.server.routes + +import android.content.Context +import io.ktor.http.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import java.io.IOException + +fun Route.webUIRoutes(context: Context) { + get("/") { + serveAsset(context, call, "index.html", ContentType.Text.Html) + } + + get("/assets/{file}") { + val fileName = call.parameters["file"] ?: return@get + val contentType = getContentType(fileName) + serveAsset(context, call, "assets/$fileName", contentType) + } + + get("/favicon.svg") { + serveAsset(context, call, "favicon.svg", ContentType.Image.SVG) + } +} + +private suspend fun serveAsset( + context: Context, + call: io.ktor.server.application.ApplicationCall, + path: String, + contentType: ContentType +) { + try { + val inputStream = context.assets.open("webui/$path") + val bytes = inputStream.readBytes() + inputStream.close() + call.respondBytes(bytes, contentType) + } catch (e: IOException) { + call.respond(HttpStatusCode.NotFound, "File not found: $path") + } +} + +private fun getContentType(fileName: String): ContentType { + return when { + fileName.endsWith(".js") -> ContentType.Application.JavaScript + fileName.endsWith(".css") -> ContentType.Text.CSS + fileName.endsWith(".html") -> ContentType.Text.Html + fileName.endsWith(".svg") -> ContentType.Image.SVG + fileName.endsWith(".png") -> ContentType.Image.PNG + fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") -> ContentType.Image.JPEG + fileName.endsWith(".json") -> ContentType.Application.Json + else -> ContentType.Application.OctetStream + } +} diff --git a/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt b/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt index fa6df48..62326b4 100644 --- a/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt +++ b/app/src/main/java/sh/sar/textpipe/service/TextpipeService.kt @@ -101,7 +101,7 @@ class TextpipeService : Service() { smsSender.register() smsRepository = SmsRepository(dao, smsSender, simManager) - textpipeServer = TextpipeServer(smsRepository, simManager) + textpipeServer = TextpipeServer(this, smsRepository, simManager) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { diff --git a/webui/index.html b/webui/index.html new file mode 100644 index 0000000..9b7d5f9 --- /dev/null +++ b/webui/index.html @@ -0,0 +1,13 @@ + + + + + + + Textpipe SMS Gateway + + +
+ + + diff --git a/webui/package-lock.json b/webui/package-lock.json new file mode 100644 index 0000000..74dc82e --- /dev/null +++ b/webui/package-lock.json @@ -0,0 +1,1207 @@ +{ + "name": "textpipe-webui", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "textpipe-webui", + "version": "1.0.0", + "dependencies": { + "vue": "^3.4.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz", + "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.27", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz", + "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz", + "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.27", + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz", + "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.27.tgz", + "integrity": "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.27" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.27.tgz", + "integrity": "sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.27.tgz", + "integrity": "sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.27", + "@vue/runtime-core": "3.5.27", + "@vue/shared": "3.5.27", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.27.tgz", + "integrity": "sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27" + }, + "peerDependencies": { + "vue": "3.5.27" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz", + "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", + "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-sfc": "3.5.27", + "@vue/runtime-dom": "3.5.27", + "@vue/server-renderer": "3.5.27", + "@vue/shared": "3.5.27" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/webui/package.json b/webui/package.json new file mode 100644 index 0000000..3e69c71 --- /dev/null +++ b/webui/package.json @@ -0,0 +1 @@ +{"name":"textpipe-webui","version":"1.0.0","private":true,"type":"module","scripts":{"dev":"vite","build":"vite build","preview":"vite preview"},"dependencies":{"vue":"^3.4.0"},"devDependencies":{"@vitejs/plugin-vue":"^5.0.0","vite":"^5.0.0"}} diff --git a/webui/src/App.vue b/webui/src/App.vue new file mode 100644 index 0000000..2032c83 --- /dev/null +++ b/webui/src/App.vue @@ -0,0 +1,35 @@ + + + diff --git a/webui/src/api.js b/webui/src/api.js new file mode 100644 index 0000000..ffe0981 --- /dev/null +++ b/webui/src/api.js @@ -0,0 +1,42 @@ +const API_BASE = window.location.origin + +export const api = { + async getStatus() { + const res = await fetch(`${API_BASE}/api/status`) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }, + + async getMessages(apiKey) { + const res = await fetch(`${API_BASE}/api/sms/messages`, { + headers: { + 'X-API-Key': apiKey + } + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }, + + async getMessageStatus(apiKey, id) { + const res = await fetch(`${API_BASE}/api/sms/status/${id}`, { + headers: { + 'X-API-Key': apiKey + } + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + }, + + async sendMessage(apiKey, to, text) { + const res = await fetch(`${API_BASE}/api/sms/send`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': apiKey + }, + body: JSON.stringify({ to, text }) + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return res.json() + } +} diff --git a/webui/src/components/LoginScreen.vue b/webui/src/components/LoginScreen.vue new file mode 100644 index 0000000..7cb13e4 --- /dev/null +++ b/webui/src/components/LoginScreen.vue @@ -0,0 +1,59 @@ + + + diff --git a/webui/src/components/MainApp.vue b/webui/src/components/MainApp.vue new file mode 100644 index 0000000..6b63069 --- /dev/null +++ b/webui/src/components/MainApp.vue @@ -0,0 +1,213 @@ + + + diff --git a/webui/src/components/MessageThread.vue b/webui/src/components/MessageThread.vue new file mode 100644 index 0000000..0dd7771 --- /dev/null +++ b/webui/src/components/MessageThread.vue @@ -0,0 +1,102 @@ + + + diff --git a/webui/src/main.js b/webui/src/main.js new file mode 100644 index 0000000..fe5bae3 --- /dev/null +++ b/webui/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './style.css' + +createApp(App).mount('#app') diff --git a/webui/src/style.css b/webui/src/style.css new file mode 100644 index 0000000..87f63cc --- /dev/null +++ b/webui/src/style.css @@ -0,0 +1,590 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --primary: #FFA31A; + --primary-dark: #CC8215; + --bg-dark: #1B1B1B; + --surface: #292929; + --surface-light: #363636; + --text-white: #FFFFFF; + --text-gray: #B3B3B3; + --text-dark: #8C8C8C; + --success: #4CAF50; + --error: #F44336; + --sent: #2196F3; + --received: #4CAF50; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; + background-color: var(--bg-dark); + color: var(--text-white); + min-height: 100vh; +} + +#app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Login Screen */ +.login-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 20px; +} + +.login-box { + background: var(--surface); + border-radius: 16px; + padding: 40px; + width: 100%; + max-width: 400px; + text-align: center; +} + +.login-logo { + font-size: 48px; + margin-bottom: 16px; +} + +.login-title { + font-size: 24px; + font-weight: 600; + margin-bottom: 8px; +} + +.login-subtitle { + color: var(--text-gray); + margin-bottom: 32px; +} + +.input-group { + margin-bottom: 20px; +} + +.input-group input { + width: 100%; + padding: 16px; + border: 2px solid var(--surface-light); + border-radius: 12px; + background: var(--bg-dark); + color: var(--text-white); + font-size: 16px; + outline: none; + transition: border-color 0.2s; +} + +.input-group input:focus { + border-color: var(--primary); +} + +.input-group input::placeholder { + color: var(--text-dark); +} + +.btn { + width: 100%; + padding: 16px; + border: none; + border-radius: 12px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.btn-primary { + background: var(--primary); + color: var(--bg-dark); +} + +.btn-primary:hover { + background: var(--primary-dark); +} + +.btn-primary:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.error-message { + color: var(--error); + margin-top: 16px; + font-size: 14px; +} + +/* Main App Layout */ +.app-container { + display: flex; + flex-direction: column; + height: 100vh; +} + +/* Header */ +.header { + background: var(--surface); + padding: 16px 20px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--surface-light); +} + +.header-left { + display: flex; + align-items: center; + gap: 12px; +} + +.header-title { + font-size: 20px; + font-weight: 600; +} + +.header-status { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: var(--text-gray); +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--success); +} + +.btn-logout { + background: transparent; + border: 1px solid var(--surface-light); + color: var(--text-gray); + padding: 8px 16px; + border-radius: 8px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s; +} + +.btn-logout:hover { + border-color: var(--error); + color: var(--error); +} + +/* Conversation List */ +.conversation-list { + flex: 1; + overflow-y: auto; + padding: 8px; +} + +.conversation-item { + display: flex; + align-items: center; + padding: 16px; + border-radius: 12px; + cursor: pointer; + transition: background 0.2s; + margin-bottom: 4px; +} + +.conversation-item:hover { + background: var(--surface); +} + +.conversation-item.active { + background: var(--surface-light); +} + +.conversation-avatar { + width: 48px; + height: 48px; + border-radius: 50%; + background: var(--primary); + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + font-weight: 600; + color: var(--bg-dark); + margin-right: 12px; + flex-shrink: 0; +} + +.conversation-info { + flex: 1; + min-width: 0; +} + +.conversation-name { + font-weight: 500; + margin-bottom: 4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.conversation-preview { + color: var(--text-gray); + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.conversation-meta { + text-align: right; + flex-shrink: 0; +} + +.conversation-time { + font-size: 12px; + color: var(--text-dark); + margin-bottom: 4px; +} + +.conversation-badge { + background: var(--primary); + color: var(--bg-dark); + font-size: 12px; + font-weight: 600; + padding: 2px 8px; + border-radius: 10px; + display: inline-block; +} + +/* Message Thread */ +.message-thread { + display: flex; + flex-direction: column; + height: 100%; +} + +.thread-header { + background: var(--surface); + padding: 16px 20px; + display: flex; + align-items: center; + gap: 12px; + border-bottom: 1px solid var(--surface-light); +} + +.btn-back { + background: none; + border: none; + color: var(--text-white); + font-size: 24px; + cursor: pointer; + padding: 4px; +} + +.thread-info { + flex: 1; +} + +.thread-name { + font-weight: 600; + font-size: 18px; +} + +.thread-number { + color: var(--text-gray); + font-size: 14px; +} + +/* Messages */ +.messages-container { + flex: 1; + overflow-y: auto; + padding: 20px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.message { + max-width: 75%; + padding: 12px 16px; + border-radius: 18px; + font-size: 15px; + line-height: 1.4; + position: relative; +} + +.message-sent { + background: var(--sent); + color: white; + align-self: flex-end; + border-bottom-right-radius: 4px; +} + +.message-received { + background: var(--surface-light); + color: var(--text-white); + align-self: flex-start; + border-bottom-left-radius: 4px; +} + +.message-time { + font-size: 11px; + color: rgba(255, 255, 255, 0.7); + margin-top: 4px; + text-align: right; +} + +.message-status { + font-size: 11px; + margin-top: 2px; + text-align: right; +} + +.message-status.pending { + color: var(--text-dark); +} + +.message-status.sent { + color: var(--text-gray); +} + +.message-status.delivered { + color: var(--success); +} + +.message-status.failed { + color: var(--error); +} + +/* Compose */ +.compose-container { + background: var(--surface); + padding: 16px 20px; + display: flex; + align-items: flex-end; + gap: 12px; + border-top: 1px solid var(--surface-light); +} + +.compose-input { + flex: 1; + background: var(--bg-dark); + border: none; + border-radius: 24px; + padding: 12px 20px; + color: var(--text-white); + font-size: 16px; + resize: none; + outline: none; + max-height: 120px; + font-family: inherit; +} + +.compose-input::placeholder { + color: var(--text-dark); +} + +.btn-send { + width: 48px; + height: 48px; + border-radius: 50%; + background: var(--primary); + border: none; + color: var(--bg-dark); + font-size: 20px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.2s; + flex-shrink: 0; +} + +.btn-send:hover { + background: var(--primary-dark); +} + +.btn-send:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* New Message FAB */ +.fab { + position: fixed; + bottom: 24px; + right: 24px; + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--primary); + border: none; + color: var(--bg-dark); + font-size: 28px; + cursor: pointer; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + transition: transform 0.2s, background 0.2s; +} + +.fab:hover { + background: var(--primary-dark); + transform: scale(1.05); +} + +/* New Message Modal */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + z-index: 100; +} + +.modal { + background: var(--surface); + border-radius: 16px; + width: 100%; + max-width: 500px; + max-height: 90vh; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.modal-header { + padding: 20px; + border-bottom: 1px solid var(--surface-light); + display: flex; + align-items: center; + justify-content: space-between; +} + +.modal-title { + font-size: 20px; + font-weight: 600; +} + +.btn-close { + background: none; + border: none; + color: var(--text-gray); + font-size: 24px; + cursor: pointer; +} + +.modal-body { + padding: 20px; + flex: 1; + overflow-y: auto; +} + +.modal-footer { + padding: 20px; + border-top: 1px solid var(--surface-light); +} + +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 8px; + color: var(--text-gray); + font-size: 14px; +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 14px 16px; + border: 2px solid var(--surface-light); + border-radius: 12px; + background: var(--bg-dark); + color: var(--text-white); + font-size: 16px; + outline: none; + transition: border-color 0.2s; + font-family: inherit; +} + +.form-group input:focus, +.form-group textarea:focus { + border-color: var(--primary); +} + +.form-group textarea { + resize: vertical; + min-height: 100px; +} + +/* Empty State */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + padding: 40px; + text-align: center; +} + +.empty-state-icon { + font-size: 64px; + margin-bottom: 16px; +} + +.empty-state-title { + font-size: 20px; + font-weight: 600; + margin-bottom: 8px; +} + +.empty-state-text { + color: var(--text-gray); +} + +/* Loading */ +.loading { + display: flex; + align-items: center; + justify-content: center; + padding: 40px; +} + +.spinner { + width: 40px; + height: 40px; + border: 3px solid var(--surface-light); + border-top-color: var(--primary); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Responsive */ +@media (max-width: 600px) { + .login-box { + padding: 30px 20px; + } + + .message { + max-width: 85%; + } +} diff --git a/webui/vite.config.js b/webui/vite.config.js new file mode 100644 index 0000000..e188816 --- /dev/null +++ b/webui/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + base: './', + build: { + outDir: '../app/src/main/assets/webui', + emptyOutDir: true + } +})