Files
chat/web/dist/app.js
user 5047cf6098
All checks were successful
check / check (push) Successful in 1m48s
Redesign SPA to look like a proper IRC client
- Tabs for channels, queries, and server window with unread indicators
- Persistent input line at bottom with IRC-style prompt [nick] #channel >
- Full IRC command support: /join /part /msg /me /nick /topic /mode /quit /help
- User list on right side with @ops, +voiced, and regular user prefixes
- Topic bar at top of channel windows
- Messages displayed inline: [HH:MM:SS] <nick> message (same line)
- /me actions rendered as: * nick does something
- IRC vocabulary: 'parted' not 'left', 'has changed the topic' not 'set topic'
- Input history with up/down arrow keys
- Parse RPL_NAMREPLY (353) for mode prefixes in user list
- Parse RPL_TOPIC (332) for topic updates from server
- CTCP ACTION support for /me command
- Dark theme with monospace font, classic IRC aesthetic

closes #50
2026-03-07 06:18:41 -08:00

3 lines
25 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
var ie,w,Ue,pt,K,Ee,He,Re,De,he,de,pe,mt,Z={},Le=[],ht=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,ce=Array.isArray;function W(e,t){for(var n in t)e[n]=t[n];return e}function ye(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function p(e,t,n){var o,a,s,c={};for(s in t)s=="key"?o=t[s]:s=="ref"?a=t[s]:c[s]=t[s];if(arguments.length>2&&(c.children=arguments.length>3?ie.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,o,a,null)}function oe(e,t,n,o,a){var s={type:e,props:t,key:n,ref:o,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:a??++Ue,__i:-1,__u:0};return a==null&&w.vnode!=null&&w.vnode(s),s}function _e(e){return e.children}function se(e,t){this.props=e,this.context=t}function Y(e,t){if(t==null)return e.__?Y(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"?Y(e):null}function Fe(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 Fe(e)}}function Me(e){(!e.__d&&(e.__d=!0)&&K.push(e)&&!ae.__r++||Ee!=w.debounceRendering)&&((Ee=w.debounceRendering)||He)(ae)}function ae(){for(var e,t,n,o,a,s,c,_=1;K.length;)K.length>_&&K.sort(Re),e=K.shift(),_=K.length,e.__d&&(n=void 0,o=void 0,a=(o=(t=e).__v).__e,s=[],c=[],t.__P&&((n=W({},o)).__v=o.__v+1,w.vnode&&w.vnode(n),ve(t.__P,n,o,t.__n,t.__P.namespaceURI,32&o.__u?[a]:null,s,a??Y(o),!!(32&o.__u),c),n.__v=o.__v,n.__.__k[n.__i]=n,Je(s,n,c),o.__e=o.__=null,n.__e!=a&&Fe(n)));ae.__r=0}function We(e,t,n,o,a,s,c,_,h,u,b){var i,m,y,C,M,I,k,g=o&&o.__k||Le,U=t.length;for(h=yt(n,t,g,h,U),i=0;i<U;i++)(y=n.__k[i])!=null&&(m=y.__i==-1?Z:g[y.__i]||Z,y.__i=i,I=ve(e,y,m,a,s,c,_,h,u,b),C=y.__e,y.ref&&m.ref!=y.ref&&(m.ref&&be(m.ref,null,y),b.push(y.ref,y.__c||C,y)),M==null&&C!=null&&(M=C),(k=!!(4&y.__u))||m.__k===y.__k?h=je(y,h,e,k):typeof y.type=="function"&&I!==void 0?h=I:C&&(h=C.nextSibling),y.__u&=-7);return n.__e=M,h}function yt(e,t,n,o,a){var s,c,_,h,u,b=n.length,i=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):ce(c)?c=e.__k[s]=oe(_e,{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,h=s+m,c.__=e,c.__b=e.__b+1,_=null,(u=c.__i=vt(c,n,h,i))!=-1&&(i--,(_=n[u])&&(_.__u|=2)),_==null||_.__v==null?(u==-1&&(a>b?m--:a<b&&m++),typeof c.type!="function"&&(c.__u|=4)):u!=h&&(u==h-1?m--:u==h+1?m++:(u>h?m--:m++,c.__u|=4))):e.__k[s]=null;if(i)for(s=0;s<b;s++)(_=n[s])!=null&&(2&_.__u)==0&&(_.__e==o&&(o=Y(_)),Be(_,_));return o}function je(e,t,n,o){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,o));return t}e.__e!=t&&(o&&(t&&e.type&&!t.parentNode&&(t=Y(e)),n.insertBefore(e.__e,t||null)),t=e.__e);do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function vt(e,t,n,o){var a,s,c,_=e.key,h=e.type,u=t[n],b=u!=null&&(2&u.__u)==0;if(u===null&&_==null||b&&_==u.key&&h==u.type)return n;if(o>(b?1:0)){for(a=n-1,s=n+1;a>=0||s<t.length;)if((u=t[c=a>=0?a--:s++])!=null&&(2&u.__u)==0&&_==u.key&&h==u.type)return c}return-1}function Oe(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||ht.test(t)?n:n+"px"}function re(e,t,n,o,a){var s,c;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof o=="string"&&(e.style.cssText=o=""),o)for(t in o)n&&t in n||Oe(e.style,t,"");if(n)for(t in n)o&&n[t]==o[t]||Oe(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")s=t!=(t=t.replace(De,"$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?o?n.u=o.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 $e(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(w.event?w.event(t):t)}}}function ve(e,t,n,o,a,s,c,_,h,u){var b,i,m,y,C,M,I,k,g,U,O,z,R,B,j,J,Q,P=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(h=!!(32&n.__u),s=[_=t.__e=n.__e]),(b=w.__b)&&b(t);e:if(typeof P=="function")try{if(k=t.props,g="prototype"in P&&P.prototype.render,U=(b=P.contextType)&&o[b.__c],O=b?U?U.props.value:b.__:o,n.__c?I=(i=t.__c=n.__c).__=i.__E:(g?t.__c=i=new P(k,O):(t.__c=i=new se(k,O),i.constructor=P,i.render=kt),U&&U.sub(i),i.state||(i.state={}),i.__n=o,m=i.__d=!0,i.__h=[],i._sb=[]),g&&i.__s==null&&(i.__s=i.state),g&&P.getDerivedStateFromProps!=null&&(i.__s==i.state&&(i.__s=W({},i.__s)),W(i.__s,P.getDerivedStateFromProps(k,i.__s))),y=i.props,C=i.state,i.__v=t,m)g&&P.getDerivedStateFromProps==null&&i.componentWillMount!=null&&i.componentWillMount(),g&&i.componentDidMount!=null&&i.__h.push(i.componentDidMount);else{if(g&&P.getDerivedStateFromProps==null&&k!==y&&i.componentWillReceiveProps!=null&&i.componentWillReceiveProps(k,O),t.__v==n.__v||!i.__e&&i.shouldComponentUpdate!=null&&i.shouldComponentUpdate(k,i.__s,O)===!1){for(t.__v!=n.__v&&(i.props=k,i.state=i.__s,i.__d=!1),t.__e=n.__e,t.__k=n.__k,t.__k.some(function(F){F&&(F.__=t)}),z=0;z<i._sb.length;z++)i.__h.push(i._sb[z]);i._sb=[],i.__h.length&&c.push(i);break e}i.componentWillUpdate!=null&&i.componentWillUpdate(k,i.__s,O),g&&i.componentDidUpdate!=null&&i.__h.push(function(){i.componentDidUpdate(y,C,M)})}if(i.context=O,i.props=k,i.__P=e,i.__e=!1,R=w.__r,B=0,g){for(i.state=i.__s,i.__d=!1,R&&R(t),b=i.render(i.props,i.state,i.context),j=0;j<i._sb.length;j++)i.__h.push(i._sb[j]);i._sb=[]}else do i.__d=!1,R&&R(t),b=i.render(i.props,i.state,i.context),i.state=i.__s;while(i.__d&&++B<25);i.state=i.__s,i.getChildContext!=null&&(o=W(W({},o),i.getChildContext())),g&&!m&&i.getSnapshotBeforeUpdate!=null&&(M=i.getSnapshotBeforeUpdate(y,C)),J=b,b!=null&&b.type===_e&&b.key==null&&(J=Ve(b.props.children)),_=We(e,ce(J)?J:[J],t,n,o,a,s,c,_,h,u),i.base=t.__e,t.__u&=-161,i.__h.length&&c.push(i),I&&(i.__E=i.__=null)}catch(F){if(t.__v=null,h||s!=null)if(F.then){for(t.__u|=h?160:128;_&&_.nodeType==8&&_.nextSibling;)_=_.nextSibling;s[s.indexOf(_)]=null,t.__e=_}else{for(Q=s.length;Q--;)ye(s[Q]);me(t)}else t.__e=n.__e,t.__k=n.__k,F.then||me(t);w.__e(F,t,n)}else s==null&&t.__v==n.__v?(t.__k=n.__k,t.__e=n.__e):_=t.__e=bt(n.__e,t,n,o,a,s,c,h,u);return(b=w.diffed)&&b(t),128&t.__u?void 0:_}function me(e){e&&e.__c&&(e.__c.__e=!0),e&&e.__k&&e.__k.forEach(me)}function Je(e,t,n){for(var o=0;o<n.length;o++)be(n[o],n[++o],n[++o]);w.__c&&w.__c(t,e),e.some(function(a){try{e=a.__h,a.__h=[],e.some(function(s){s.call(a)})}catch(s){w.__e(s,a.__v)}})}function Ve(e){return typeof e!="object"||e==null||e.__b&&e.__b>0?e:ce(e)?e.map(Ve):W({},e)}function bt(e,t,n,o,a,s,c,_,h){var u,b,i,m,y,C,M,I=n.props||Z,k=t.props,g=t.type;if(g=="svg"?a="http://www.w3.org/2000/svg":g=="math"?a="http://www.w3.org/1998/Math/MathML":a||(a="http://www.w3.org/1999/xhtml"),s!=null){for(u=0;u<s.length;u++)if((y=s[u])&&"setAttribute"in y==!!g&&(g?y.localName==g:y.nodeType==3)){e=y,s[u]=null;break}}if(e==null){if(g==null)return document.createTextNode(k);e=document.createElementNS(a,g,k.is&&k),_&&(w.__m&&w.__m(t,s),_=!1),s=null}if(g==null)I===k||_&&e.data==k||(e.data=k);else{if(s=s&&ie.call(e.childNodes),!_&&s!=null)for(I={},u=0;u<e.attributes.length;u++)I[(y=e.attributes[u]).name]=y.value;for(u in I)if(y=I[u],u!="children"){if(u=="dangerouslySetInnerHTML")i=y;else if(!(u in k)){if(u=="value"&&"defaultValue"in k||u=="checked"&&"defaultChecked"in k)continue;re(e,u,null,y,a)}}for(u in k)y=k[u],u=="children"?m=y:u=="dangerouslySetInnerHTML"?b=y:u=="value"?C=y:u=="checked"?M=y:_&&typeof y!="function"||I[u]===y||re(e,u,y,I[u],a);if(b)_||i&&(b.__html==i.__html||b.__html==e.innerHTML)||(e.innerHTML=b.__html),t.__k=[];else if(i&&(e.innerHTML=""),We(t.type=="template"?e.content:e,ce(m)?m:[m],t,n,o,g=="foreignObject"?"http://www.w3.org/1999/xhtml":a,s,c,s?s[0]:n.__k&&Y(n,0),_,h),s!=null)for(u=s.length;u--;)ye(s[u]);_||(u="value",g=="progress"&&C==null?e.removeAttribute("value"):C!=null&&(C!==e[u]||g=="progress"&&!C||g=="option"&&C!=I[u])&&re(e,u,C,I[u],a),u="checked",M!=null&&M!=e[u]&&re(e,u,M,I[u],a))}return e}function be(e,t,n){try{if(typeof e=="function"){var o=typeof e.__u=="function";o&&e.__u(),o&&t==null||(e.__u=e(t))}else e.current=t}catch(a){w.__e(a,n)}}function Be(e,t,n){var o,a;if(w.unmount&&w.unmount(e),(o=e.ref)&&(o.current&&o.current!=e.__e||be(o,null,t)),(o=e.__c)!=null){if(o.componentWillUnmount)try{o.componentWillUnmount()}catch(s){w.__e(s,t)}o.base=o.__P=null}if(o=e.__k)for(a=0;a<o.length;a++)o[a]&&Be(o[a],t,n||typeof e.type!="function");n||ye(e.__e),e.__c=e.__=e.__e=void 0}function kt(e,t,n){return this.constructor(e,n)}function qe(e,t,n){var o,a,s,c;t==document&&(t=document.documentElement),w.__&&w.__(e,t),a=(o=typeof n=="function")?null:n&&n.__k||t.__k,s=[],c=[],ve(t,e=(!o&&n||t).__k=p(_e,null,[e]),a||Z,Z,t.namespaceURI,!o&&n?[n]:a?null:t.firstChild?ie.call(t.childNodes):null,s,!o&&n?n:a?a.__e:t.firstChild,o,c),Je(s,e,c)}ie=Le.slice,w={__e:function(e,t,n,o){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,o||{}),c=a.__d),c)return a.__E=a}catch(_){e=_}throw e}},Ue=0,pt=function(e){return e!=null&&e.constructor===void 0},se.prototype.setState=function(e,t){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=W({},this.state),typeof e=="function"&&(e=e(W({},n),this.props)),e&&W(n,e),e!=null&&this.__v&&(t&&this._sb.push(t),Me(this))},se.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),Me(this))},se.prototype.render=_e,K=[],He=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,Re=function(e,t){return e.__v.__b-t.__v.__b},ae.__r=0,De=/(PointerCapture)$|Capture$/i,he=0,de=$e(!1),pe=$e(!0),mt=0;var ee,x,ke,Ge,te=0,tt=[],A=w,Ke=A.__b,ze=A.__r,Qe=A.diffed,Ye=A.__c,Xe=A.unmount,Ze=A.__;function Se(e,t){A.__h&&A.__h(x,e,te||t),te=0;var n=x.__H||(x.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function N(e){return te=1,gt(ot,e)}function gt(e,t,n){var o=Se(ee++,2);if(o.t=e,!o.__c&&(o.__=[n?n(t):ot(void 0,t),function(_){var h=o.__N?o.__N[0]:o.__[0],u=o.t(h,_);h!==u&&(o.__N=[u,o.__[1]],o.__c.setState({}))}],o.__c=x,!x.__f)){var a=function(_,h,u){if(!o.__c.__H)return!0;var b=o.__c.__H.__.filter(function(m){return!!m.__c});if(b.every(function(m){return!m.__N}))return!s||s.call(this,_,h,u);var i=o.__c.props!==_;return b.forEach(function(m){if(m.__N){var y=m.__[0];m.__=m.__N,m.__N=void 0,y!==m.__[0]&&(i=!0)}}),s&&s.call(this,_,h,u)||i};x.__f=!0;var s=x.shouldComponentUpdate,c=x.componentWillUpdate;x.componentWillUpdate=function(_,h,u){if(this.__e){var b=s;s=void 0,a(_,h,u),s=b}c&&c.call(this,_,h,u)},x.shouldComponentUpdate=a}return o.__N||o.__}function $(e,t){var n=Se(ee++,3);!A.__s&&rt(n.__H,t)&&(n.__=e,n.u=t,x.__H.__h.push(n))}function L(e){return te=5,nt(function(){return{current:e}},[])}function nt(e,t){var n=Se(ee++,7);return rt(n.__H,t)&&(n.__=e(),n.__H=t,n.__h=e),n.__}function X(e,t){return te=8,nt(function(){return e},t)}function St(){for(var e;e=tt.shift();)if(e.__P&&e.__H)try{e.__H.__h.forEach(le),e.__H.__h.forEach(ge),e.__H.__h=[]}catch(t){e.__H.__h=[],A.__e(t,e.__v)}}A.__b=function(e){x=null,Ke&&Ke(e)},A.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Ze&&Ze(e,t)},A.__r=function(e){ze&&ze(e),ee=0;var t=(x=e.__c).__H;t&&(ke===x?(t.__h=[],x.__h=[],t.__.forEach(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(t.__h.forEach(le),t.__h.forEach(ge),t.__h=[],ee=0)),ke=x},A.diffed=function(e){Qe&&Qe(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(tt.push(t)!==1&&Ge===A.requestAnimationFrame||((Ge=A.requestAnimationFrame)||wt)(St)),t.__H.__.forEach(function(n){n.u&&(n.__H=n.u),n.u=void 0})),ke=x=null},A.__c=function(e,t){t.some(function(n){try{n.__h.forEach(le),n.__h=n.__h.filter(function(o){return!o.__||ge(o)})}catch(o){t.some(function(a){a.__h&&(a.__h=[])}),t=[],A.__e(o,n.__v)}}),Ye&&Ye(e,t)},A.unmount=function(e){Xe&&Xe(e);var t,n=e.__c;n&&n.__H&&(n.__H.__.forEach(function(o){try{le(o)}catch(a){t=a}}),n.__H=void 0,t&&A.__e(t,n.__v))};var et=typeof requestAnimationFrame=="function";function wt(e){var t,n=function(){clearTimeout(o),et&&cancelAnimationFrame(t),setTimeout(e)},o=setTimeout(n,35);et&&(t=requestAnimationFrame(n))}function le(e){var t=x,n=e.__c;typeof n=="function"&&(e.__c=void 0,n()),x=t}function ge(e){var t=x;e.__c=e.__(),x=t}function rt(e,t){return!e||e.length!==t.length||t.some(function(n,o){return n!==e[o]})}function ot(e,t){return typeof t=="function"?t(e):t}var Ct="/api/v1",Tt=15,xt=3e3,It=1e4,Ce="ACTION ",Te="";function E(e,t={}){let n=localStorage.getItem("neoirc_token"),o={"Content-Type":"application/json",...t.headers||{}};n&&(o.Authorization=`Bearer ${n}`);let{signal:a,...s}=t;return fetch(Ct+e,{...s,headers:o,signal:a}).then(async c=>{let _=await c.json().catch(()=>null);if(!c.ok)throw{status:c.status,data:_};return _})}function At(e){return new Date(e).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})}function we(e){let t=0;for(let o=0;o<e.length;o++)t=e.charCodeAt(o)+((t<<5)-t);return`hsl(${Math.abs(t)%360}, 60%, 65%)`}function Pt(e){return e.startsWith(Ce)&&e.endsWith(Te)}function Nt(e){return e.slice(Ce.length,-Te.length)}function Et({onLogin:e}){let[t,n]=N(""),[o,a]=N(""),[s,c]=N(""),[_,h]=N("NeoIRC"),u=L();return $(()=>{E("/server").then(m=>{m.name&&h(m.name),m.motd&&c(m.motd)}).catch(()=>{}),localStorage.getItem("neoirc_token")&&E("/state").then(m=>e(m.nick)).catch(()=>localStorage.removeItem("neoirc_token")),u.current?.focus()},[]),p("div",{class:"login-screen"},p("div",{class:"login-box"},p("h1",null,_),s&&p("pre",{class:"motd"},s),p("form",{onSubmit:async i=>{i.preventDefault(),a("");try{let m=await E("/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")}}},p("label",null,"Nickname:"),p("input",{ref:u,type:"text",placeholder:"Enter nickname",value:t,onInput:i=>n(i.target.value),maxLength:32,autoFocus:!0}),p("button",{type:"submit"},"Connect")),o&&p("div",{class:"error"},o)))}function Mt({msg:e,myNick:t}){let n=At(e.ts);return e.system?p("div",{class:"message system-message"},p("span",{class:"timestamp"},"[",n,"]"),p("span",{class:"system-text"}," * ",e.text)):e.isAction?p("div",{class:"message action-message"},p("span",{class:"timestamp"},"[",n,"]"),p("span",{class:"action-text"}," ","* ",p("span",{style:{color:we(e.from)}},e.from)," ",e.text)):p("div",{class:"message"},p("span",{class:"timestamp"},"[",n,"]")," ",p("span",{class:"nick",style:{color:we(e.from)}},"<",e.from,">")," ",p("span",{class:"content"},e.text))}function Ot({members:e,onNickClick:t}){let n=[],o=[],a=[];for(let _ of e){let h=_.mode||"";h==="o"?n.push(_):h==="v"?o.push(_):a.push(_)}let s=(_,h)=>_.nick.toLowerCase().localeCompare(h.nick.toLowerCase());n.sort(s),o.sort(s),a.sort(s);let c=(_,h)=>p("div",{class:"nick-entry",onClick:()=>t(_.nick),title:_.nick},p("span",{class:"nick-prefix"},h),p("span",{class:"nick-name",style:{color:we(_.nick)}},_.nick));return p("div",{class:"user-list"},p("div",{class:"user-list-header"},e.length," user",e.length!==1?"s":""),p("div",{class:"user-list-entries"},n.map(_=>c(_,"@")),o.map(_=>c(_,"+")),a.map(_=>c(_,""))))}function $t(){let[e,t]=N(!1),[n,o]=N(""),[a,s]=N([{type:"server",name:"Server"}]),[c,_]=N(0),[h,u]=N({Server:[]}),[b,i]=N({}),[m,y]=N({}),[C,M]=N({}),[I,k]=N(""),[g,U]=N(!0),[O,z]=N([]),[R,B]=N(-1),j=L(0),J=L(new Set),Q=L(null),P=L(a),F=L(c),ne=L(n),xe=L(),Ie=L();$(()=>{P.current=a},[a]),$(()=>{F.current=c},[c]),$(()=>{ne.current=n},[n]),$(()=>{let r=a.filter(l=>l.type==="channel").map(l=>l.name);localStorage.setItem("neoirc_channels",JSON.stringify(r))},[a]),$(()=>{let r=a[c];r&&M(l=>({...l,[r.name]:0}))},[c,a]);let D=X((r,l)=>{if(l.id&&J.current.has(l.id))return;l.id&&J.current.add(l.id),u(f=>({...f,[r]:[...f[r]||[],l]}));let d=P.current[F.current];(!d||d.name!==r)&&M(f=>({...f,[r]:(f[r]||0)+1}))},[]),T=X((r,l)=>{u(d=>({...d,[r]:[...d[r]||[],{id:"sys-"+Date.now()+"-"+Math.random(),ts:new Date().toISOString(),text:l,system:!0}]}))},[]),q=X(r=>{let l=r.replace("#","");E(`/channels/${l}/members`).then(d=>{i(f=>({...f,[r]:d}))}).catch(()=>{})},[]),ue=X(r=>{let l=Array.isArray(r.body)?r.body.join(`
`):"",d={id:r.id,ts:r.ts,from:r.from,to:r.to,command:r.command};switch(r.command){case"PRIVMSG":case"NOTICE":{let f=l,v=!1;Pt(f)&&(f=Nt(f),v=!0);let S={...d,text:f,system:!1,isAction:v},G=r.to;if(G&&G.startsWith("#"))D(G,S);else{let H=r.from===ne.current?r.to:r.from;s(fe=>fe.find(Ne=>Ne.type==="dm"&&Ne.name===H)?fe:[...fe,{type:"dm",name:H}]),D(H,S)}break}case"JOIN":{let f=`${r.from} has joined ${r.to}`;r.to&&D(r.to,{...d,text:f,system:!0}),r.to&&r.to.startsWith("#")&&q(r.to);break}case"PART":{let f=l?" ("+l+")":"",v=`${r.from} has parted ${r.to}${f}`;r.to&&D(r.to,{...d,text:v,system:!0}),r.to&&r.to.startsWith("#")&&q(r.to);break}case"QUIT":{let f=l?" ("+l+")":"",v=`${r.from} has quit${f}`;P.current.forEach(S=>{S.type==="channel"&&D(S.name,{...d,text:v,system:!0})});break}case"NICK":{let f=Array.isArray(r.body)?r.body[0]:l,v=`${r.from} is now known as ${f}`;P.current.forEach(S=>{S.type==="channel"&&D(S.name,{...d,text:v,system:!0})}),r.from===ne.current&&f&&o(f),P.current.forEach(S=>{S.type==="channel"&&q(S.name)});break}case"TOPIC":{let f=`${r.from} has changed the topic to: ${l}`;r.to&&(D(r.to,{...d,text:f,system:!0}),y(v=>({...v,[r.to]:l})));break}case"353":{if(Array.isArray(r.params)&&r.params.length>=2&&r.body){let f=r.params[1],G=(Array.isArray(r.body)?r.body[0]:String(r.body)).split(/\s+/).filter(Boolean).map(H=>H.startsWith("@")?{nick:H.slice(1),mode:"o"}:H.startsWith("+")?{nick:H.slice(1),mode:"v"}:{nick:H,mode:""});i(H=>({...H,[f]:G}))}break}case"332":{if(Array.isArray(r.params)&&r.params.length>=1){let f=r.params[0],v=Array.isArray(r.body)?r.body[0]:l;v&&y(S=>({...S,[f]:v}))}break}case"375":case"372":case"376":D("Server",{...d,text:l,system:!0});break;default:l&&D("Server",{...d,text:l,system:!0})}},[D,q]);$(()=>{if(!e)return;let r=!0;return(async()=>{for(;r;)try{let d=new AbortController;Q.current=d;let f=await E(`/messages?after=${j.current}&timeout=${Tt}`,{signal:d.signal});if(!r)break;if(U(!0),f.messages)for(let v of f.messages)ue(v);f.last_id>j.current&&(j.current=f.last_id)}catch(d){if(!r)break;if(d.name==="AbortError")continue;U(!1),await new Promise(f=>setTimeout(f,xt))}})(),()=>{r=!1,Q.current?.abort()}},[e,ue]),$(()=>{if(!e)return;let r=a[c];if(!r||r.type!=="channel")return;q(r.name);let l=setInterval(()=>q(r.name),It);return()=>clearInterval(l)},[e,c,a,q]),$(()=>{xe.current?.scrollIntoView({behavior:"smooth"})},[h,c]),$(()=>{Ie.current?.focus()},[c]),$(()=>{if(!e)return;let r=a[c];!r||r.type!=="channel"||E("/channels").then(l=>{let d=l.find(f=>f.name===r.name);d&&d.topic&&y(f=>({...f,[r.name]:d.topic}))}).catch(()=>{})},[e,c,a]);let st=X(async r=>{o(r),t(!0),T("Server",`Connected as ${r}`);let l=JSON.parse(localStorage.getItem("neoirc_channels")||"[]");for(let d of l)try{await E("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:d})}),s(f=>f.find(v=>v.type==="channel"&&v.name===d)?f:[...f,{type:"channel",name:d}])}catch{}},[T]),at=async r=>{if(r){r=r.trim(),r.startsWith("#")||(r="#"+r);try{await E("/messages",{method:"POST",body:JSON.stringify({command:"JOIN",to:r})}),s(d=>d.find(f=>f.type==="channel"&&f.name===r)?d:[...d,{type:"channel",name:r}]);let l=P.current.length;_(l);try{let d=await E(`/history?target=${encodeURIComponent(r)}&limit=50`);if(Array.isArray(d))for(let f of d)ue(f)}catch{}}catch(l){T("Server",`Failed to join ${r}: ${l.data?.error||"error"}`)}}},Ae=async(r,l)=>{try{await E("/messages",{method:"POST",body:JSON.stringify(l?{command:"PART",to:r,body:[l]}:{command:"PART",to:r})})}catch{}s(d=>d.filter(f=>!(f.type==="channel"&&f.name===r))),_(0)},it=r=>{let l=a[r];l.type==="channel"?Ae(l.name):l.type==="dm"&&(s(d=>d.filter((f,v)=>v!==r)),c>=r&&_(Math.max(0,c-1)))},Pe=r=>{if(r===ne.current)return;s(d=>d.find(f=>f.type==="dm"&&f.name===r)?d:[...d,{type:"dm",name:r}]);let l=a.findIndex(d=>d.type==="dm"&&d.name===r);_(l>=0?l:a.length)},ct=async r=>{let l=r.split(" "),d=l[0].toLowerCase(),f=a[c];switch(d){case"/join":{l[1]?at(l[1]):T("Server","Usage: /join #channel");break}case"/part":{if(f.type==="channel"){let v=l.slice(1).join(" ")||void 0;Ae(f.name,v)}else T("Server","You are not in a channel");break}case"/msg":{if(l[1]&&l.slice(2).join(" ")){let v=l[1],S=l.slice(2).join(" ");try{await E("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:v,body:[S]})}),Pe(v)}catch(G){T("Server",`Message failed: ${G.data?.error||"error"}`)}}else T("Server","Usage: /msg <nick> <message>");break}case"/me":{if(f.type==="server"){T("Server","Cannot use /me in server window");break}let v=l.slice(1).join(" ");if(v)try{await E("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:f.name,body:[Ce+v+Te]})})}catch(S){T(f.name,`Action failed: ${S.data?.error||"error"}`)}else T("Server","Usage: /me <action>");break}case"/nick":{if(l[1])try{await E("/messages",{method:"POST",body:JSON.stringify({command:"NICK",body:[l[1]]})})}catch(v){T("Server",`Nick change failed: ${v.data?.error||"error"}`)}else T("Server","Usage: /nick <newnick>");break}case"/topic":{if(f.type!=="channel"){T("Server","You are not in a channel");break}let v=l.slice(1).join(" ");if(v)try{await E("/messages",{method:"POST",body:JSON.stringify({command:"TOPIC",to:f.name,body:[v]})})}catch(S){T("Server",`Topic change failed: ${S.data?.error||"error"}`)}else T("Server",`Current topic for ${f.name}: ${m[f.name]||"(none)"}`);break}case"/mode":{if(f.type!=="channel"){T("Server","You are not in a channel");break}let v=l.slice(1);if(v.length>0)try{await E("/messages",{method:"POST",body:JSON.stringify({command:"MODE",to:f.name,params:v})})}catch(S){T("Server",`Mode change failed: ${S.data?.error||"error"}`)}else T("Server","Usage: /mode <+/-mode> [params]");break}case"/quit":{let v=l.slice(1).join(" ")||void 0;try{await E("/messages",{method:"POST",body:JSON.stringify(v?{command:"QUIT",body:[v]}:{command:"QUIT"})})}catch{}localStorage.removeItem("neoirc_token"),localStorage.removeItem("neoirc_channels"),window.location.reload();break}case"/help":{let v=["Available commands:"," /join #channel \u2014 Join a channel"," /part [reason] \u2014 Part the current channel"," /msg nick message \u2014 Send a private message"," /me action \u2014 Send an action"," /nick newnick \u2014 Change your nickname"," /topic [text] \u2014 View or set channel topic"," /mode +/-flags \u2014 Set channel modes"," /quit [reason] \u2014 Disconnect from server"," /help \u2014 Show this help"];for(let S of v)T("Server",S);break}default:T("Server",`Unknown command: ${d}`)}},_t=async()=>{let r=I.trim();if(!r)return;z(d=>{let f=[...d,r];return f.length>100&&f.shift(),f}),B(-1),k("");let l=a[c];if(l){if(r.startsWith("/")){await ct(r);return}if(l.type==="server"){T("Server","Cannot send messages to the server window. Use /join #channel first.");return}try{await E("/messages",{method:"POST",body:JSON.stringify({command:"PRIVMSG",to:l.name,body:[r]})})}catch(d){T(l.name,`Send failed: ${d.data?.error||"error"}`)}}},lt=r=>{if(r.key==="Enter")_t();else if(r.key==="ArrowUp"){if(r.preventDefault(),O.length>0){let l=R===-1?O.length-1:Math.max(0,R-1);B(l),k(O[l])}}else if(r.key==="ArrowDown"&&(r.preventDefault(),R>=0)){let l=R+1;l>=O.length?(B(-1),k("")):(B(l),k(O[l]))}};if(!e)return p(Et,{onLogin:st});let V=a[c]||a[0],ut=h[V.name]||[],ft=b[V.name]||[],dt=m[V.name]||"";return p("div",{class:"irc-app"},p("div",{class:"tab-bar"},p("div",{class:"tabs"},a.map((r,l)=>p("div",{class:`tab ${l===c?"active":""} ${C[r.name]>0&&l!==c?"has-unread":""}`,onClick:()=>_(l),key:r.name},p("span",{class:"tab-label"},(r.type==="dm",r.name)),C[r.name]>0&&l!==c&&p("span",{class:"unread-count"},"(",C[r.name],")"),r.type!=="server"&&p("span",{class:"tab-close",onClick:d=>{d.stopPropagation(),it(l)}},"\xD7")))),p("div",{class:"status-area"},!g&&p("span",{class:"status-warn"},"\u25CF Reconnecting"),p("span",{class:"status-nick"},n))),V.type==="channel"&&p("div",{class:"topic-bar"},p("span",{class:"topic-label"},"Topic:")," ",p("span",{class:"topic-text"},dt||"(no topic set)")),p("div",{class:"main-area"},p("div",{class:"messages-panel"},p("div",{class:"messages-scroll"},ut.map(r=>p(Mt,{msg:r,myNick:n,key:r.id})),p("div",{ref:xe}))),V.type==="channel"&&p(Ot,{members:ft,onNickClick:Pe})),p("div",{class:"input-line"},p("span",{class:"input-prompt"},"[",n,"]",V.type!=="server"?` ${V.name}`:""," >"),p("input",{ref:Ie,type:"text",value:I,onInput:r=>k(r.target.value),onKeyDown:lt,placeholder:V.type==="server"?"Type /help for commands":"",spellCheck:!1,autoComplete:"off"})))}qe(p($t,null),document.getElementById("root"));