All checks were successful
check / check (push) Successful in 57s
Major UI overhaul of the embedded web SPA to match traditional IRC client look and feel: Layout changes: - Input bar now spans full width at bottom of window (below user list) - Removed inline join dialog from tab bar (use /join command instead) - Nick prefix shown in input bar (e.g. 'alice>') - Topic bar shows 'Topic:' label with accent color IRC command support: - Added /me command (action messages via meta.action flag) - Added /mode command for channel/user mode changes - Added /quit command for clean disconnect - Added /help command listing all available commands - /part now accepts optional reason message User list improvements: - Parse 353 RPL_NAMREPLY to extract user mode prefixes - Display @nick for ops, +nick for voiced, plain for regular users - Sort users by mode rank: ops first, then voiced, then regular - Ops shown in orange, voiced in green, regular in default color - Extracted UserList into dedicated component Message display: - Messages displayed inline with nick on same line (IRC style) - Action messages (/me) shown as '* nick does something' in purple - System messages prefixed with '***' - Uses IRC vocabulary: 'has parted' instead of 'has left' - Parse 332 RPL_TOPIC for channel topic on join UX improvements: - Command history with up/down arrow keys (100 entries) - Input always visible including on Server tab - Dark theme with monospace font (JetBrains Mono/Fira Code) - Hover highlight on messages - Custom scrollbar styling - Tab type-based styling (server tab bold) closes #50
3 lines
24 KiB
JavaScript
3 lines
24 KiB
JavaScript
var ae,S,He,ht,K,Ae,Re,De,Le,he,de,pe,yt,X={},je=[],vt=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,_e=Array.isArray;function J(e,t){for(var n in t)e[n]=t[n];return e}function ye(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function y(e,t,n){var r,a,s,c={};for(s in t)s=="key"?r=t[s]:s=="ref"?a=t[s]:c[s]=t[s];if(arguments.length>2&&(c.children=arguments.length>3?ae.call(arguments,2):n),typeof e=="function"&&e.defaultProps!=null)for(s in e.defaultProps)c[s]===void 0&&(c[s]=e.defaultProps[s]);return oe(e,c,r,a,null)}function oe(e,t,n,r,a){var s={type:e,props:t,key:n,ref:r,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:a??++He,__i:-1,__u:0};return a==null&&S.vnode!=null&&S.vnode(s),s}function ce(e){return e.children}function re(e,t){this.props=e,this.context=t}function Q(e,t){if(t==null)return e.__?Q(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type=="function"?Q(e):null}function We(e){var t,n;if((e=e.__)!=null&&e.__c!=null){for(e.__e=e.__c.base=null,t=0;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null){e.__e=e.__c.base=n.__e;break}return We(e)}}function Oe(e){(!e.__d&&(e.__d=!0)&&K.push(e)&&!se.__r++||Ae!=S.debounceRendering)&&((Ae=S.debounceRendering)||Re)(se)}function se(){for(var e,t,n,r,a,s,c,u=1;K.length;)K.length>u&&K.sort(De),e=K.shift(),u=K.length,e.__d&&(n=void 0,r=void 0,a=(r=(t=e).__v).__e,s=[],c=[],t.__P&&((n=J({},r)).__v=r.__v+1,S.vnode&&S.vnode(n),ve(t.__P,n,r,t.__n,t.__P.namespaceURI,32&r.__u?[a]:null,s,a??Q(r),!!(32&r.__u),c),n.__v=r.__v,n.__.__k[n.__i]=n,Be(s,n,c),r.__e=r.__=null,n.__e!=a&&We(n)));se.__r=0}function Fe(e,t,n,r,a,s,c,u,v,l,b){var _,m,h,T,A,x,g,k=r&&r.__k||je,O=t.length;for(v=bt(n,t,k,v,O),_=0;_<O;_++)(h=n.__k[_])!=null&&(m=h.__i==-1?X:k[h.__i]||X,h.__i=_,x=ve(e,h,m,a,s,c,u,v,l,b),T=h.__e,h.ref&&m.ref!=h.ref&&(m.ref&&be(m.ref,null,h),b.push(h.ref,h.__c||T,h)),A==null&&T!=null&&(A=T),(g=!!(4&h.__u))||m.__k===h.__k?v=Je(h,v,e,g):typeof h.type=="function"&&x!==void 0?v=x:T&&(v=T.nextSibling),h.__u&=-7);return n.__e=A,v}function bt(e,t,n,r,a){var s,c,u,v,l,b=n.length,_=b,m=0;for(e.__k=new Array(a),s=0;s<a;s++)(c=t[s])!=null&&typeof c!="boolean"&&typeof c!="function"?(typeof c=="string"||typeof c=="number"||typeof c=="bigint"||c.constructor==String?c=e.__k[s]=oe(null,c,null,null,null):_e(c)?c=e.__k[s]=oe(ce,{children:c},null,null,null):c.constructor===void 0&&c.__b>0?c=e.__k[s]=oe(c.type,c.props,c.key,c.ref?c.ref:null,c.__v):e.__k[s]=c,v=s+m,c.__=e,c.__b=e.__b+1,u=null,(l=c.__i=kt(c,n,v,_))!=-1&&(_--,(u=n[l])&&(u.__u|=2)),u==null||u.__v==null?(l==-1&&(a>b?m--:a<b&&m++),typeof c.type!="function"&&(c.__u|=4)):l!=v&&(l==v-1?m--:l==v+1?m++:(l>v?m--:m++,c.__u|=4))):e.__k[s]=null;if(_)for(s=0;s<b;s++)(u=n[s])!=null&&(2&u.__u)==0&&(u.__e==r&&(r=Q(u)),qe(u,u));return r}function Je(e,t,n,r){var a,s;if(typeof e.type=="function"){for(a=e.__k,s=0;a&&s<a.length;s++)a[s]&&(a[s].__=e,t=Je(a[s],t,n,r));return t}e.__e!=t&&(r&&(t&&e.type&&!t.parentNode&&(t=Q(e)),n.insertBefore(e.__e,t||null)),t=e.__e);do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function kt(e,t,n,r){var a,s,c,u=e.key,v=e.type,l=t[n],b=l!=null&&(2&l.__u)==0;if(l===null&&u==null||b&&u==l.key&&v==l.type)return n;if(r>(b?1:0)){for(a=n-1,s=n+1;a>=0||s<t.length;)if((l=t[c=a>=0?a--:s++])!=null&&(2&l.__u)==0&&u==l.key&&v==l.type)return c}return-1}function $e(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||vt.test(t)?n:n+"px"}function ne(e,t,n,r,a){var s,c;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof r=="string"&&(e.style.cssText=r=""),r)for(t in r)n&&t in n||$e(e.style,t,"");if(n)for(t in n)r&&n[t]==r[t]||$e(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")s=t!=(t=t.replace(Le,"$1")),c=t.toLowerCase(),t=c in e||t=="onFocusOut"||t=="onFocusIn"?c.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+s]=n,n?r?n.u=r.u:(n.u=he,e.addEventListener(t,s?pe:de,s)):e.removeEventListener(t,s?pe:de,s);else{if(a=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&n==1?"":n))}}function Ue(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t.t==null)t.t=he++;else if(t.t<n.u)return;return n(S.event?S.event(t):t)}}}function ve(e,t,n,r,a,s,c,u,v,l){var b,_,m,h,T,A,x,g,k,O,j,V,H,te,B,R,q,$=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(v=!!(32&n.__u),s=[u=t.__e=n.__e]),(b=S.__b)&&b(t);e:if(typeof $=="function")try{if(g=t.props,k="prototype"in $&&$.prototype.render,O=(b=$.contextType)&&r[b.__c],j=b?O?O.props.value:b.__:r,n.__c?x=(_=t.__c=n.__c).__=_.__E:(k?t.__c=_=new $(g,j):(t.__c=_=new re(g,j),_.constructor=$,_.render=St),O&&O.sub(_),_.state||(_.state={}),_.__n=r,m=_.__d=!0,_.__h=[],_._sb=[]),k&&_.__s==null&&(_.__s=_.state),k&&$.getDerivedStateFromProps!=null&&(_.__s==_.state&&(_.__s=J({},_.__s)),J(_.__s,$.getDerivedStateFromProps(g,_.__s))),h=_.props,T=_.state,_.__v=t,m)k&&$.getDerivedStateFromProps==null&&_.componentWillMount!=null&&_.componentWillMount(),k&&_.componentDidMount!=null&&_.__h.push(_.componentDidMount);else{if(k&&$.getDerivedStateFromProps==null&&g!==h&&_.componentWillReceiveProps!=null&&_.componentWillReceiveProps(g,j),t.__v==n.__v||!_.__e&&_.shouldComponentUpdate!=null&&_.shouldComponentUpdate(g,_.__s,j)===!1){for(t.__v!=n.__v&&(_.props=g,_.state=_.__s,_.__d=!1),t.__e=n.__e,t.__k=n.__k,t.__k.some(function(W){W&&(W.__=t)}),V=0;V<_._sb.length;V++)_.__h.push(_._sb[V]);_._sb=[],_.__h.length&&c.push(_);break e}_.componentWillUpdate!=null&&_.componentWillUpdate(g,_.__s,j),k&&_.componentDidUpdate!=null&&_.__h.push(function(){_.componentDidUpdate(h,T,A)})}if(_.context=j,_.props=g,_.__P=e,_.__e=!1,H=S.__r,te=0,k){for(_.state=_.__s,_.__d=!1,H&&H(t),b=_.render(_.props,_.state,_.context),B=0;B<_._sb.length;B++)_.__h.push(_._sb[B]);_._sb=[]}else do _.__d=!1,H&&H(t),b=_.render(_.props,_.state,_.context),_.state=_.__s;while(_.__d&&++te<25);_.state=_.__s,_.getChildContext!=null&&(r=J(J({},r),_.getChildContext())),k&&!m&&_.getSnapshotBeforeUpdate!=null&&(A=_.getSnapshotBeforeUpdate(h,T)),R=b,b!=null&&b.type===ce&&b.key==null&&(R=Ve(b.props.children)),u=Fe(e,_e(R)?R:[R],t,n,r,a,s,c,u,v,l),_.base=t.__e,t.__u&=-161,_.__h.length&&c.push(_),x&&(_.__E=_.__=null)}catch(W){if(t.__v=null,v||s!=null)if(W.then){for(t.__u|=v?160:128;u&&u.nodeType==8&&u.nextSibling;)u=u.nextSibling;s[s.indexOf(u)]=null,t.__e=u}else{for(q=s.length;q--;)ye(s[q]);me(t)}else t.__e=n.__e,t.__k=n.__k,W.then||me(t);S.__e(W,t,n)}else s==null&&t.__v==n.__v?(t.__k=n.__k,t.__e=n.__e):u=t.__e=gt(n.__e,t,n,r,a,s,c,v,l);return(b=S.diffed)&&b(t),128&t.__u?void 0:u}function me(e){e&&e.__c&&(e.__c.__e=!0),e&&e.__k&&e.__k.forEach(me)}function Be(e,t,n){for(var r=0;r<n.length;r++)be(n[r],n[++r],n[++r]);S.__c&&S.__c(t,e),e.some(function(a){try{e=a.__h,a.__h=[],e.some(function(s){s.call(a)})}catch(s){S.__e(s,a.__v)}})}function Ve(e){return typeof e!="object"||e==null||e.__b&&e.__b>0?e:_e(e)?e.map(Ve):J({},e)}function gt(e,t,n,r,a,s,c,u,v){var l,b,_,m,h,T,A,x=n.props||X,g=t.props,k=t.type;if(k=="svg"?a="http://www.w3.org/2000/svg":k=="math"?a="http://www.w3.org/1998/Math/MathML":a||(a="http://www.w3.org/1999/xhtml"),s!=null){for(l=0;l<s.length;l++)if((h=s[l])&&"setAttribute"in h==!!k&&(k?h.localName==k:h.nodeType==3)){e=h,s[l]=null;break}}if(e==null){if(k==null)return document.createTextNode(g);e=document.createElementNS(a,k,g.is&&g),u&&(S.__m&&S.__m(t,s),u=!1),s=null}if(k==null)x===g||u&&e.data==g||(e.data=g);else{if(s=s&&ae.call(e.childNodes),!u&&s!=null)for(x={},l=0;l<e.attributes.length;l++)x[(h=e.attributes[l]).name]=h.value;for(l in x)if(h=x[l],l!="children"){if(l=="dangerouslySetInnerHTML")_=h;else if(!(l in g)){if(l=="value"&&"defaultValue"in g||l=="checked"&&"defaultChecked"in g)continue;ne(e,l,null,h,a)}}for(l in g)h=g[l],l=="children"?m=h:l=="dangerouslySetInnerHTML"?b=h:l=="value"?T=h:l=="checked"?A=h:u&&typeof h!="function"||x[l]===h||ne(e,l,h,x[l],a);if(b)u||_&&(b.__html==_.__html||b.__html==e.innerHTML)||(e.innerHTML=b.__html),t.__k=[];else if(_&&(e.innerHTML=""),Fe(t.type=="template"?e.content:e,_e(m)?m:[m],t,n,r,k=="foreignObject"?"http://www.w3.org/1999/xhtml":a,s,c,s?s[0]:n.__k&&Q(n,0),u,v),s!=null)for(l=s.length;l--;)ye(s[l]);u||(l="value",k=="progress"&&T==null?e.removeAttribute("value"):T!=null&&(T!==e[l]||k=="progress"&&!T||k=="option"&&T!=x[l])&&ne(e,l,T,x[l],a),l="checked",A!=null&&A!=e[l]&&ne(e,l,A,x[l],a))}return e}function be(e,t,n){try{if(typeof e=="function"){var r=typeof e.__u=="function";r&&e.__u(),r&&t==null||(e.__u=e(t))}else e.current=t}catch(a){S.__e(a,n)}}function qe(e,t,n){var r,a;if(S.unmount&&S.unmount(e),(r=e.ref)&&(r.current&&r.current!=e.__e||be(r,null,t)),(r=e.__c)!=null){if(r.componentWillUnmount)try{r.componentWillUnmount()}catch(s){S.__e(s,t)}r.base=r.__P=null}if(r=e.__k)for(a=0;a<r.length;a++)r[a]&&qe(r[a],t,n||typeof e.type!="function");n||ye(e.__e),e.__c=e.__=e.__e=void 0}function St(e,t,n){return this.constructor(e,n)}function Ge(e,t,n){var r,a,s,c;t==document&&(t=document.documentElement),S.__&&S.__(e,t),a=(r=typeof n=="function")?null:n&&n.__k||t.__k,s=[],c=[],ve(t,e=(!r&&n||t).__k=y(ce,null,[e]),a||X,X,t.namespaceURI,!r&&n?[n]:a?null:t.firstChild?ae.call(t.childNodes):null,s,!r&&n?n:a?a.__e:t.firstChild,r,c),Be(s,e,c)}ae=je.slice,S={__e:function(e,t,n,r){for(var a,s,c;t=t.__;)if((a=t.__c)&&!a.__)try{if((s=a.constructor)&&s.getDerivedStateFromError!=null&&(a.setState(s.getDerivedStateFromError(e)),c=a.__d),a.componentDidCatch!=null&&(a.componentDidCatch(e,r||{}),c=a.__d),c)return a.__E=a}catch(u){e=u}throw e}},He=0,ht=function(e){return e!=null&&e.constructor===void 0},re.prototype.setState=function(e,t){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=J({},this.state),typeof e=="function"&&(e=e(J({},n),this.props)),e&&J(n,e),e!=null&&this.__v&&(t&&this._sb.push(t),Oe(this))},re.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),Oe(this))},re.prototype.render=ce,K=[],Re=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,De=function(e,t){return e.__v.__b-t.__v.__b},se.__r=0,Le=/(PointerCapture)$|Capture$/i,he=0,de=Ue(!1),pe=Ue(!0),yt=0;var Z,C,ke,Ke,ee=0,nt=[],I=S,ze=I.__b,Qe=I.__r,Ye=I.diffed,Xe=I.__c,Ze=I.unmount,et=I.__;function Se(e,t){I.__h&&I.__h(C,e,ee||t),ee=0;var n=C.__H||(C.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function E(e){return ee=1,wt(st,e)}function wt(e,t,n){var r=Se(Z++,2);if(r.t=e,!r.__c&&(r.__=[n?n(t):st(void 0,t),function(u){var v=r.__N?r.__N[0]:r.__[0],l=r.t(v,u);v!==l&&(r.__N=[l,r.__[1]],r.__c.setState({}))}],r.__c=C,!C.__f)){var a=function(u,v,l){if(!r.__c.__H)return!0;var b=r.__c.__H.__.filter(function(m){return!!m.__c});if(b.every(function(m){return!m.__N}))return!s||s.call(this,u,v,l);var _=r.__c.props!==u;return b.forEach(function(m){if(m.__N){var h=m.__[0];m.__=m.__N,m.__N=void 0,h!==m.__[0]&&(_=!0)}}),s&&s.call(this,u,v,l)||_};C.__f=!0;var s=C.shouldComponentUpdate,c=C.componentWillUpdate;C.componentWillUpdate=function(u,v,l){if(this.__e){var b=s;s=void 0,a(u,v,l),s=b}c&&c.call(this,u,v,l)},C.shouldComponentUpdate=a}return r.__N||r.__}function U(e,t){var n=Se(Z++,3);!I.__s&&rt(n.__H,t)&&(n.__=e,n.u=t,C.__H.__h.push(n))}function L(e){return ee=5,ot(function(){return{current:e}},[])}function ot(e,t){var n=Se(Z++,7);return rt(n.__H,t)&&(n.__=e(),n.__H=t,n.__h=e),n.__}function z(e,t){return ee=8,ot(function(){return e},t)}function Ct(){for(var e;e=nt.shift();)if(e.__P&&e.__H)try{e.__H.__h.forEach(ie),e.__H.__h.forEach(ge),e.__H.__h=[]}catch(t){e.__H.__h=[],I.__e(t,e.__v)}}I.__b=function(e){C=null,ze&&ze(e)},I.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),et&&et(e,t)},I.__r=function(e){Qe&&Qe(e),Z=0;var t=(C=e.__c).__H;t&&(ke===C?(t.__h=[],C.__h=[],t.__.forEach(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(t.__h.forEach(ie),t.__h.forEach(ge),t.__h=[],Z=0)),ke=C},I.diffed=function(e){Ye&&Ye(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(nt.push(t)!==1&&Ke===I.requestAnimationFrame||((Ke=I.requestAnimationFrame)||Tt)(Ct)),t.__H.__.forEach(function(n){n.u&&(n.__H=n.u),n.u=void 0})),ke=C=null},I.__c=function(e,t){t.some(function(n){try{n.__h.forEach(ie),n.__h=n.__h.filter(function(r){return!r.__||ge(r)})}catch(r){t.some(function(a){a.__h&&(a.__h=[])}),t=[],I.__e(r,n.__v)}}),Xe&&Xe(e,t)},I.unmount=function(e){Ze&&Ze(e);var t,n=e.__c;n&&n.__H&&(n.__H.__.forEach(function(r){try{ie(r)}catch(a){t=a}}),n.__H=void 0,t&&I.__e(t,n.__v))};var tt=typeof requestAnimationFrame=="function";function Tt(e){var t,n=function(){clearTimeout(r),tt&&cancelAnimationFrame(t),setTimeout(e)},r=setTimeout(n,35);tt&&(t=requestAnimationFrame(n))}function ie(e){var t=C,n=e.__c;typeof n=="function"&&(e.__c=void 0,n()),C=t}function ge(e){var t=C;e.__c=e.__(),C=t}function rt(e,t){return!e||e.length!==t.length||t.some(function(n,r){return n!==e[r]})}function st(e,t){return typeof t=="function"?t(e):t}var xt="/api/v1",It=15,Pt=3e3,Et=1e4,Mt=100;function M(e,t={}){let n=localStorage.getItem("neoirc_token"),r={"Content-Type":"application/json",...t.headers||{}};n&&(r.Authorization=`Bearer ${n}`);let{signal:a,...s}=t;return fetch(xt+e,{...s,headers:r,signal:a}).then(async c=>{let u=await c.json().catch(()=>null);if(!c.ok)throw{status:c.status,data:u};return u})}function we(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function at(e){let t=0;for(let r=0;r<e.length;r++)t=e.charCodeAt(r)+((t<<5)-t);return`hsl(${Math.abs(t)%360}, 70%, 65%)`}function Nt({onLogin:e}){let[t,n]=E(""),[r,a]=E(""),[s,c]=E(""),[u,v]=E("NeoIRC"),l=L();return U(()=>{M("/server").then(m=>{m.name&&v(m.name),m.motd&&c(m.motd)}).catch(()=>{}),localStorage.getItem("neoirc_token")&&M("/state").then(m=>e(m.nick)).catch(()=>localStorage.removeItem("neoirc_token")),l.current?.focus()},[]),y("div",{class:"login-screen"},y("h1",null,u),s&&y("div",{class:"motd"},s),y("form",{onSubmit:async _=>{_.preventDefault(),a("");try{let m=await M("/session",{method:"POST",body:JSON.stringify({nick:t.trim()})});localStorage.setItem("neoirc_token",m.token),e(m.nick)}catch(m){a(m.data?.error||"Connection failed")}}},y("input",{ref:l,type:"text",placeholder:"Choose a nickname...",value:t,onInput:_=>n(_.target.value),maxLength:32,autoFocus:!0}),y("button",{type:"submit"},"Connect")),r&&y("div",{class:"error"},r))}function At({msg:e}){return e.system?y("div",{class:"message system"},y("span",{class:"timestamp"},we(e.ts)),y("span",{class:"content"},e.text)):e.action?y("div",{class:"message action"},y("span",{class:"timestamp"},we(e.ts)),y("span",{class:"content"},"* ",y("span",{class:"action-nick",style:{color:at(e.from)}},e.from)," ",e.text)):y("div",{class:"message"},y("span",{class:"timestamp"},we(e.ts)),y("span",{class:"nick",style:{color:at(e.from)}},e.from),y("span",{class:"content"},e.text))}function Ot({members:e,userModes:t,onDM:n}){let r=[...e].sort((a,s)=>{let c=t[a.nick]||"",u=t[s.nick]||"",v=c==="o"?0:c==="v"?1:2,l=u==="o"?0:u==="v"?1:2;return v!==l?v-l:a.nick.localeCompare(s.nick)});return y("div",{class:"user-list"},y("div",{class:"user-list-header"},"Users (",e.length,")"),y("div",{class:"user-list-entries"},r.map(a=>{let s=t[a.nick]||"",c=s==="o"?"@":s==="v"?"+":"";return y("div",{class:`user ${s==="o"?"op":s==="v"?"voice":""}`,onClick:()=>n(a.nick),title:a.nick},c,a.nick)})))}function $t(){let[e,t]=E(!1),[n,r]=E(""),[a,s]=E([{type:"server",name:"Server"}]),[c,u]=E(0),[v,l]=E({Server:[]}),[b,_]=E({}),[m,h]=E({}),[T,A]=E({}),[x,g]=E({}),[k,O]=E(""),[j,V]=E(!0),[H,te]=E([]),[B,R]=E(-1),q=L(0),$=L(new Set),W=L(null),Y=L(a),Ce=L(c),le=L(n),Te=L(),xe=L();U(()=>{Y.current=a},[a]),U(()=>{Ce.current=c},[c]),U(()=>{le.current=n},[n]),U(()=>{let o=a.filter(i=>i.type==="channel").map(i=>i.name);localStorage.setItem("neoirc_channels",JSON.stringify(o))},[a]),U(()=>{let o=a[c];o&&g(i=>({...i,[o.name]:0}))},[c,a]);let D=z((o,i)=>{if(i.id&&$.current.has(i.id))return;i.id&&$.current.add(i.id),l(d=>({...d,[o]:[...d[o]||[],i]}));let f=Y.current[Ce.current];(!f||f.name!==o)&&g(d=>({...d,[o]:(d[o]||0)+1}))},[]),P=z((o,i)=>{l(f=>({...f,[o]:[...f[o]||[],{id:"sys-"+Date.now()+"-"+Math.random(),ts:new Date().toISOString(),text:i,system:!0}]}))},[]),G=z(o=>{let i=o.replace("#","");M(`/channels/${i}/members`).then(f=>{_(d=>({...d,[o]:f}))}).catch(()=>{})},[]),Ie=z((o,i)=>{let f=i.trim().split(/\s+/),d={};for(let p of f)p.startsWith("@")?d[p.slice(1)]="o":p.startsWith("+")&&(d[p.slice(1)]="v");h(p=>({...p,[o]:{...p[o]||{},...d}}))},[]),ue=z(o=>{let i=Array.isArray(o.body)?o.body.join(`
|
|
`):"",f={id:o.id,ts:o.ts,from:o.from,to:o.to,command:o.command},d=o.meta&&o.meta.action;switch(o.command){case"PRIVMSG":case"NOTICE":{let p={...f,text:i,system:!1,action:d},w=o.to;if(w&&w.startsWith("#"))D(w,p);else{let N=o.from===le.current?o.to:o.from;s(fe=>fe.find(Ne=>Ne.type==="dm"&&Ne.name===N)?fe:[...fe,{type:"dm",name:N}]),D(N,p)}break}case"JOIN":{let p=`${o.from} has joined ${o.to}`;o.to&&D(o.to,{...f,text:p,system:!0}),o.to&&o.to.startsWith("#")&&G(o.to);break}case"PART":{let p=i?": "+i:"",w=`${o.from} has parted ${o.to}${p}`;o.to&&D(o.to,{...f,text:w,system:!0}),o.to&&o.to.startsWith("#")&&G(o.to);break}case"QUIT":{let p=i?": "+i:"",w=`${o.from} has quit${p}`;Y.current.forEach(N=>{N.type==="channel"&&D(N.name,{...f,text:w,system:!0})});break}case"NICK":{let p=Array.isArray(o.body)?o.body[0]:i,w=`${o.from} is now known as ${p}`;Y.current.forEach(N=>{N.type==="channel"&&D(N.name,{...f,text:w,system:!0})}),o.from===le.current&&p&&r(p),Y.current.forEach(N=>{N.type==="channel"&&G(N.name)});break}case"TOPIC":{let p=`${o.from} set the topic: ${i}`;o.to&&(D(o.to,{...f,text:p,system:!0}),A(w=>({...w,[o.to]:i})));break}case"332":{let p=Array.isArray(o.params)&&o.params.length>0?o.params[0]:o.to;p&&A(w=>({...w,[p]:i}));break}case"353":{let p=Array.isArray(o.params)&&o.params.length>1?o.params[1]:o.to;p&&i&&Ie(p,i);break}case"366":break;case"375":case"372":case"376":D("Server",{...f,text:i,system:!0});break;default:D("Server",{...f,text:i||o.command,system:!0})}},[D,G,Ie]);U(()=>{if(!e)return;let o=!0;return(async()=>{for(;o;)try{let f=new AbortController;W.current=f;let d=await M(`/messages?after=${q.current}&timeout=${It}`,{signal:f.signal});if(!o)break;if(V(!0),d.messages)for(let p of d.messages)ue(p);d.last_id>q.current&&(q.current=d.last_id)}catch(f){if(!o)break;if(f.name==="AbortError")continue;V(!1),await new Promise(d=>setTimeout(d,Pt))}})(),()=>{o=!1,W.current?.abort()}},[e,ue]),U(()=>{if(!e)return;let o=a[c];if(!o||o.type!=="channel")return;G(o.name);let i=setInterval(()=>G(o.name),Et);return()=>clearInterval(i)},[e,c,a,G]),U(()=>{Te.current?.scrollIntoView({behavior:"smooth"})},[v,c]),U(()=>{xe.current?.focus()},[c]),U(()=>{if(!e)return;let o=a[c];!o||o.type!=="channel"||M("/channels").then(i=>{let f=i.find(d=>d.name===o.name);f&&f.topic&&A(d=>({...d,[o.name]:f.topic}))}).catch(()=>{})},[e,c,a]);let _t=z(async o=>{r(o),t(!0),P("Server",`Connected as ${o}`);let i=JSON.parse(localStorage.getItem("neoirc_channels")||"[]");for(let f of i)try{await M("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:f})}),s(d=>d.find(p=>p.type==="channel"&&p.name===f)?d:[...d,{type:"channel",name:f}])}catch{}},[P]),ct=async o=>{if(o){o=o.trim(),o.startsWith("#")||(o="#"+o);try{await M("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:o})}),s(i=>i.find(f=>f.type==="channel"&&f.name===o)?i:[...i,{type:"channel",name:o}]),u(a.length);try{let i=await M(`/history?target=${encodeURIComponent(o)}&limit=50`);if(Array.isArray(i))for(let f of i)ue(f)}catch{}}catch(i){P("Server",`Failed to join ${o}: ${i.data?.error||"error"}`)}}},Pe=async(o,i)=>{try{let f={command:"PART",to:o};i&&(f.body=[i]),await M("/messages",{method:"POST",body:JSON.stringify(f)})}catch{}s(f=>f.filter(d=>!(d.type==="channel"&&d.name===o))),u(0)},it=o=>{let i=a[o];i.type==="channel"?Pe(i.name):i.type==="dm"&&(s(f=>f.filter((d,p)=>p!==o)),c>=o&&u(Math.max(0,c-1)))},Ee=o=>{s(f=>f.find(d=>d.type==="dm"&&d.name===o)?f:[...f,{type:"dm",name:o}]);let i=a.findIndex(f=>f.type==="dm"&&f.name===o);u(i>=0?i:a.length)},lt=async o=>{let i=o.split(" "),f=i[0].toLowerCase(),d=a[c];switch(f){case"/join":i[1]?ct(i[1]):P(d?.name||"Server","Usage: /join #channel");break;case"/part":{if(d?.type==="channel"){let p=i.slice(1).join(" ")||"";Pe(d.name,p)}else P(d?.name||"Server","Not in a channel");break}case"/msg":if(i[1]&&i.slice(2).join(" ")){let p=i[1],w=i.slice(2).join(" ");try{await M("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:p,body:[w]})}),Ee(p)}catch(N){P("Server",`DM failed: ${N.data?.error||"error"}`)}}else P(d?.name||"Server","Usage: /msg <nick> <message>");break;case"/me":if(d&&d.type!=="server"){let p=i.slice(1).join(" ");if(p)try{await M("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:d.name,body:[p],meta:{action:!0}})})}catch(w){P(d.name,`Action failed: ${w.data?.error||"error"}`)}else P(d.name,"Usage: /me <action>")}break;case"/nick":if(i[1])try{await M("/messages",{method:"POST",body:JSON.stringify({command:"NICK",body:[i[1]]})})}catch(p){P("Server",`Nick change failed: ${p.data?.error||"error"}`)}else P(d?.name||"Server","Usage: /nick <newnick>");break;case"/topic":if(d?.type==="channel"){let p=i.slice(1).join(" ");try{await M("/messages",{method:"POST",body:JSON.stringify({command:"TOPIC",to:d.name,body:[p]})})}catch(w){P(d.name,`Topic failed: ${w.data?.error||"error"}`)}}else P(d?.name||"Server","Not in a channel");break;case"/mode":if(d?.type==="channel"&&i.length>=2){let p=i[1].startsWith("#")?i[1]:d.name,w=i[1].startsWith("#")?i.slice(2):i.slice(1);try{await M("/messages",{method:"POST",body:JSON.stringify({command:"MODE",to:p,params:w})})}catch(N){P(d.name,`Mode failed: ${N.data?.error||"error"}`)}}else P(d?.name||"Server","Usage: /mode [#channel] <+/-mode> [nick]");break;case"/quit":{let p=i.slice(1).join(" ");try{let w={command:"QUIT"};p&&(w.body=[p]),await M("/messages",{method:"POST",body:JSON.stringify(w)})}catch{}localStorage.removeItem("neoirc_token"),window.location.reload();break}case"/help":P(d?.name||"Server","Commands: /join #channel, /part [reason], /msg nick message, /me action, /nick newnick, /topic text, /mode [#channel] +/-mode [nick], /quit [reason], /help");break;default:P(d?.name||"Server",`Unknown command: ${f} \u2014 Type /help for available commands`)}},ut=async()=>{let o=k.trim();if(!o)return;te(f=>[o,...f].slice(0,Mt)),R(-1),O("");let i=a[c];if(o.startsWith("/")){await lt(o);return}if(!i||i.type==="server"){P("Server","Cannot send messages to the server tab. Join a channel with /join #channel");return}try{await M("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:i.name,body:[o]})})}catch(f){P(i.name,`Send failed: ${f.data?.error||"error"}`)}},ft=o=>{if(o.key==="Enter")ut();else if(o.key==="ArrowUp"){if(o.preventDefault(),H.length>0){let i=Math.min(B+1,H.length-1);R(i),O(H[i])}}else if(o.key==="ArrowDown")if(o.preventDefault(),B>0){let i=B-1;R(i),O(H[i])}else R(-1),O("")};if(!e)return y(Nt,{onLogin:_t});let F=a[c]||a[0],dt=v[F.name]||[],pt=b[F.name]||[],mt=m[F.name]||{},Me=T[F.name]||"";return y("div",{class:"app"},y("div",{class:"tab-bar"},!j&&y("div",{class:"connection-status"},"\u26A0 Reconnecting..."),a.map((o,i)=>y("div",{class:`tab ${i===c?"active":""} ${o.type}`,onClick:()=>u(i)},y("span",{class:"tab-name"},o.type==="dm"?`\u2192${o.name}`:o.name),x[o.name]>0&&i!==c&&y("span",{class:"unread-badge"},x[o.name]),o.type!=="server"&&y("span",{class:"close-btn",onClick:f=>{f.stopPropagation(),it(i)}},"\xD7")))),F.type==="channel"&&y("div",{class:"topic-bar",title:Me},y("span",{class:"topic-label"},"Topic:")," ",Me||"(no topic set)"),y("div",{class:"content"},y("div",{class:"messages-pane"},y("div",{class:F.type==="server"?"server-messages":"messages"},dt.map(o=>y(At,{msg:o})),y("div",{ref:Te}))),F.type==="channel"&&y(Ot,{members:pt,userModes:mt,onDM:Ee})),y("div",{class:"input-bar"},y("span",{class:"input-nick"},n),y("input",{ref:xe,placeholder:F.type==="server"?"Type /join #channel to get started...":`Message ${F.name}...`,value:k,onInput:o=>O(o.target.value),onKeyDown:ft})))}Ge(y($t,null),document.getElementById("root"));
|