tmux-weblink 2.2.10 → 2.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,40 @@
1
- var dn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function qe(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function de(e){return e.replace(dn,"")}var Be;function un(){return Be||(document.querySelector("link[data-tmux-web-xterm-css]")?Be=Promise.resolve():(Be=new Promise(e=>{let t=document.createElement("link");t.rel="stylesheet",t.href="/assets/xterm.css",t.setAttribute("data-tmux-web-xterm-css",""),t.addEventListener("load",()=>e(),{once:!0}),t.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(t)}),Be))}var pn=class Pt{terminal;fitAddon;container;constructor(t,n,s){this.container=t,this.terminal=n,this.fitAddon=s,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,n,s){let[{Terminal:l},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),un()]),c=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,smoothScrollDuration:120,theme:s});return new Pt(t,c,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(t){return new Promise(n=>this.terminal.write(t,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(t,n=!1){this.terminal.scrollToLine(Math.max(0,t),n)}scrollLines(t){let n=this.terminal.buffer?.active;this.terminal.scrollLines(t)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let t=mn(this.container);if(t.width<=0||t.height<=0)return!1;let n=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!n?.width||!n?.height)return this.fitAddon.fit(),!0;let s=n.width,l=n.height,r=this.terminal.element?.querySelector(".xterm-viewport"),c=r?Math.max(0,r.offsetWidth-r.clientWidth):0,o=Math.max(2,Math.floor((t.width-c)/s)),u=Math.max(2,Math.floor(t.height/l));return(o!==this.terminal.cols||u!==this.terminal.rows)&&this.terminal.resize(o,u),!0}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,n){let s=this.terminal.buffer.active,[l,r]=t<=n?[t,n]:[n,t],c=s.viewportY,o=[];for(let u=l;u<=r;u++){let p=s.getLine(c+u);o.push(p?p.translateToString(!0):"")}return o.join(`
2
- `)}selectRows(t,n){let s=this.terminal.buffer.active,[l,r]=t<=n?[t,n]:[n,t];this.terminal.selectLines(s.viewportY+l,s.viewportY+r)}selectCells(t,n,s,l){let r=this.terminal.buffer.active,c=Math.min(n,l),o=Math.max(n,l);if(c===o){let u=Math.min(t,s),p=Math.max(t,s);this.terminal.select(u,r.viewportY+c,p-u+1)}else this.terminal.selectLines(r.viewportY+c,r.viewportY+o)}getCellsText(t,n,s,l){let r=this.terminal.buffer.active,c=Math.min(n,l),o=Math.max(n,l),u=n<l?t:n>l?s:Math.min(t,s),p=n<l?s:n>l?t:Math.max(t,s),k=r.viewportY,L=[];for(let H=c;H<=o;H++){let B=r.getLine(k+H);if(!B){L.push("");continue}let P=H===c?u:0,We=H===o?p:this.terminal.cols-1,Z="";for(let S=P;S<=We;S++){let U=B.getCell(S);U&&(Z+=U.getChars())}L.push(Z.replace(/\s+$/,""))}return L.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function mn(e){let t=e.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(t.width,n.width),height:Math.max(0,Math.min(t.height,n.height-Math.max(0,t.top)))}:t}function fn(e,t,n){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let s=await pn.create(e,n.scrollback,n.theme),l,r=0,c,o=null,u=0,p=0,k=.2,L=10,H=.95,B=.04;function P(){u&&(cancelAnimationFrame(u),u=0),p=0}function We(a){if(P(),Math.abs(a)<k)return;p=Math.max(-L,Math.min(L,a));let i=()=>{if(z){u=0;return}let d=e.querySelector(".xterm-rows > div"),m=d?d.getBoundingClientRect().height:16,b=m>0?Math.round(p*16/m):p>0?1:-1;b===0&&(b=p>0?1:-1);let A=s.viewportY()-b,K=s.baseY(),ce=Math.max(0,Math.min(A,K));if(s.scrollToLine(ce,!0),p*=H,ce!==A){u=0;return}if(Math.abs(p)<B){u=0;return}u=requestAnimationFrame(i)};u=requestAnimationFrame(i)}let Z=0,S="connecting",U=0,ne=!1,Ee=[],G="",z=!1,se=1e3,Rt=location.protocol==="https:"?"wss:":"ws:",jt=n.wsBase||"/ws",zt=Rt+"//"+location.host+jt+"/"+encodeURIComponent(t),_t="/api/session/"+encodeURIComponent(t)+"/upload",Wt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Ot(){return Ee.join("")+G}async function Ut(a,i=0){let d=a?s.viewportY():0,m=de(Ot());if(s.reset(),!m){a||s.scrollToBottom();return}await s.write(m),a?s.scrollToLine(i+d):s.scrollToBottom()}function Y(a){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(a))}function Yt(a){let i;try{i=JSON.parse(a)}catch{S==="live"&&(G+=a,s.write(de(a)));return}if(i.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?Y({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(i.type==="attach_failed"&&typeof i.message=="string"){Ce=!0,S="dead",s.reset(),s.write(`\r
4
- \x1B[31m`+de(i.message)+`\x1B[0m\r
5
- `).then(()=>{ke();try{l?.close()}catch{}});return}if(i.type==="auth.ok"){i.token&&localStorage.setItem("tmux-web-token",i.token),se=1e3,q(),Y({type:"resize",cols:s.cols,rows:s.rows}),T();return}if(i.type==="auth.failed"){if(i.permanentLock||i.retryAfterMs&&i.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(i.message);return}return}if(i.type==="snapshot"&&typeof i.data=="string"){let d=qe(de(i.data));Ee=[d],G="",U=typeof i.lines=="number"?i.lines:n.terminal.initialLines,S="live",s.reset(),q(),Y({type:"resize",cols:s.cols,rows:s.rows}),s.write(d).then(()=>s.scrollToBottom());return}if(i.type==="window_changed"&&typeof i.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(i.activeIndex)&&(d.searchParams.set("window",String(i.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:i.windows}));return}if(i.type==="history"&&typeof i.data=="string"){i.lines>0&&i.data?(Ee.unshift(qe(i.data)),U+=i.lines,Ut(!0,i.lines).finally(()=>{ne=!1})):ne=!1;return}if(i.type==="data"&&typeof i.data=="string"){let d=qe(de(i.data));if(S==="connecting"){S="live",G=d,s.write(d);return}S==="live"&&(G+=d,s.write(d))}}let Ce=!1;function st(){if(Ce)return;let a=zt,i=localStorage.getItem("tmux-web-token");i&&(a+=(a.includes("?")?"&":"?")+"token="+encodeURIComponent(i)),a+=(a.includes("?")?"&":"?")+"cols="+s.cols+"&rows="+s.rows,l=new WebSocket(a),l.onopen=()=>{S="connecting",U=0,ne=!1,Ee=[],G="",s.reset(),se=1e3},l.onmessage=d=>{typeof d.data=="string"&&Yt(d.data)},l.onclose=()=>{z||(S="connecting",!Ce&&setTimeout(()=>{se=Math.min(se*2,1e4),!z&&!Ce&&st()},se))},l.onerror=()=>l?.close()}function xe(a){Y({type:"input",data:a})}function q(){return s.fit()}function ke(){e.classList.remove("terminal-pending")}function T(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),q(),r=requestAnimationFrame(()=>{r=0,q()&&ke()}),c=setTimeout(()=>{q()&&ke()},120)}function qt(){T(),setTimeout(T,50),setTimeout(T,150),setTimeout(T,300)}function at(a){return typeof a=="string"&&a.startsWith("image/")}function it(a){return a?.files?.length?a.files[0]??null:null}function Kt(a){if(!a?.items)return null;for(let i=0;i<a.items.length;i++){let d=a.items[i];if(d?.kind==="file"&&at(d.type))return d.getAsFile()}return null}async function Vt(a){let i=new FormData,d=a instanceof File&&a.name?a.name:"upload";i.append("file",a,d);let m=localStorage.getItem("tmux-web-token"),b={};m&&(b.Authorization="Bearer "+m);let A=await fetch(_t,{method:"POST",body:i,headers:b});if(!A.ok){let ce="upload failed";try{let pt=await A.json();pt.error&&(ce=pt.error)}catch{}throw new Error(ce)}let K=await A.json();if(!K.path)throw new Error("no path in response");return K.path}async function Oe(a){try{let i=await Vt(a);xe(i)}catch{}}s.onData(a=>xe(a)),s.onResize(({cols:a,rows:i})=>Y({type:"resize",cols:a,rows:i})),s.onScroll(()=>{S!=="live"||ne||!s.isNearScrollbackTop()||(ne=!0,Y({type:"load_history",before:U}))});let ae=!1,ie=!1,w=null,E=null,oe=document.createElement("div");oe.className="terminal-copy-banner",oe.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',oe.hidden=!0,e.appendChild(oe);let R=document.createElement("div");R.className="terminal-copy-banner terminal-copy-toast",R.textContent="Copied",R.hidden=!0,e.appendChild(R);function ot(a,i){let d=e.querySelector(".xterm-screen, canvas"),m=(d&&d.getBoundingClientRect().width>0?d:e).getBoundingClientRect(),b=m.height>0&&s.rows>0?m.height/s.rows:18,A=m.width>0&&s.cols>0?m.width/s.cols:9;return{row:Math.max(0,Math.min(s.rows-1,Math.floor((i-m.top)/b))),col:Math.max(0,Math.min(s.cols-1,Math.floor((a-m.left)/A)))}}async function Se(a){if(!a)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(a);return}}catch{}let i=document.createElement("textarea");i.value=a,i.style.position="fixed",i.style.opacity="0",i.style.pointerEvents="none",document.body.appendChild(i),i.focus(),i.select();try{document.execCommand("copy")}catch{}i.remove()}let le;function Te(){R.hidden=!1,le&&clearTimeout(le),le=setTimeout(()=>{R.hidden=!0},1e3)}function Q(a){ae=a,oe.hidden=!a,e.classList.toggle("terminal-copy-mode-active",a),a&&s.focus()}function lt(){Q(!ae)}function Jt(a){z||a.button!==0||ie||(ae||a.shiftKey)&&(a.preventDefault(),a.stopPropagation(),ie=!0,w=ot(a.clientX,a.clientY),E=w,s.selectCells(w.col,w.row,E.col,E.row),document.addEventListener("mousemove",Ue,!0),document.addEventListener("mouseup",Ye,!0))}function Ue(a){z||!ie||!w||(a.preventDefault(),a.stopPropagation(),E=ot(a.clientX,a.clientY),s.selectCells(w.col,w.row,E.col,E.row))}function Ye(a){if(z||!ie||!w||!E)return;a.preventDefault(),a.stopPropagation(),document.removeEventListener("mousemove",Ue,!0),document.removeEventListener("mouseup",Ye,!0),ie=!1;let i=s.getCellsText(w.col,w.row,E.col,E.row);w=null,E=null,s.clearSelection(),Q(!1),i&&(R.textContent="\u5DF2\u590D\u5236",Se(i).then(Te)),s.focus()}e.addEventListener("mousedown",Jt,!0),document.addEventListener("keydown",Xt,!0);function Xt(a){if(!(z||!s.isFocused()||a.defaultPrevented||a.isComposing)){if(ae){if(a.key==="Escape"){a.preventDefault(),a.stopPropagation(),w=null,E=null,s.clearSelection(),Q(!1);return}if(a.key==="Enter"||(a.ctrlKey||a.metaKey)&&a.code==="KeyC"){a.preventDefault(),a.stopPropagation(),Se(s.getScreenText()).then(Te),Q(!1);return}}if(a.altKey&&a.code==="KeyC"&&!a.ctrlKey&&!a.metaKey){if(a.preventDefault(),a.stopPropagation(),w&&E){let i=s.getCellsText(w.col,w.row,E.col,E.row);w=null,E=null,s.clearSelection(),Q(!1),i&&(R.textContent="Copied",Se(i).then(Te))}else{let i=s.getSelection();i?(s.clearSelection(),R.textContent="Copied",Se(i).then(Te)):lt()}return}if((a.ctrlKey||a.metaKey)&&a.code==="KeyV"){a.preventDefault(),a.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let i=await navigator.clipboard.read();for(let d of i)for(let m of d.types)if(at(m)){let b=await d.getType(m);await Oe(b);return}}}catch{}try{let i=await navigator.clipboard?.readText();i&&s.pasteText(rt(i))}catch{}})();return}}}let Me=0;function rt(a){return a.split(`\r
1
+ var un=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Ke(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function ue(e){return e.replace(un,"")}var Ie;function pn(){return Ie||(document.querySelector("link[data-tmux-web-xterm-css]")?Ie=Promise.resolve():(Ie=new Promise(e=>{let t=document.createElement("link");t.rel="stylesheet",t.href="/assets/xterm.css",t.setAttribute("data-tmux-web-xterm-css",""),t.addEventListener("load",()=>e(),{once:!0}),t.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(t)}),Ie))}var mn=class $t{terminal;fitAddon;container;constructor(t,n,s){this.container=t,this.terminal=n,this.fitAddon=s,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,n,s){let[{Terminal:l},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),pn()]),c=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,smoothScrollDuration:120,theme:s});return new $t(t,c,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(t){return new Promise(n=>this.terminal.write(t,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(t,n=!1){this.terminal.scrollToLine(Math.max(0,t),n)}scrollLines(t){let n=this.terminal.buffer?.active;this.terminal.scrollLines(t)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let t=fn(this.container);if(t.width<=0||t.height<=0)return!1;let n=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!n?.width||!n?.height)return this.fitAddon.fit(),!0;let s=n.width,l=n.height,r=this.terminal.element?.querySelector(".xterm-viewport"),c=r?Math.max(0,r.offsetWidth-r.clientWidth):0,a=Math.max(2,Math.floor((t.width-c)/s)),u=Math.max(2,Math.floor(t.height/l));return(a!==this.terminal.cols||u!==this.terminal.rows)&&this.terminal.resize(a,u),!0}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,n){let s=this.terminal.buffer.active,[l,r]=t<=n?[t,n]:[n,t],c=s.viewportY,a=[];for(let u=l;u<=r;u++){let m=s.getLine(c+u);a.push(m?m.translateToString(!0):"")}return a.join(`
2
+ `)}selectRows(t,n){let s=this.terminal.buffer.active,[l,r]=t<=n?[t,n]:[n,t];this.terminal.selectLines(s.viewportY+l,s.viewportY+r)}selectCells(t,n,s,l){let r=this.terminal.buffer.active,c=Math.min(n,l),a=Math.max(n,l);if(c===a){let u=Math.min(t,s),m=Math.max(t,s);this.terminal.select(u,r.viewportY+c,m-u+1)}else this.terminal.selectLines(r.viewportY+c,r.viewportY+a)}getCellsText(t,n,s,l){let r=this.terminal.buffer.active,c=Math.min(n,l),a=Math.max(n,l),u=n<l?t:n>l?s:Math.min(t,s),m=n<l?s:n>l?t:Math.max(t,s),k=r.viewportY,L=[];for(let H=c;H<=a;H++){let B=r.getLine(k+H);if(!B){L.push("");continue}let P=H===c?u:0,We=H===a?m:this.terminal.cols-1,Z="";for(let S=P;S<=We;S++){let U=B.getCell(S);U&&(Z+=U.getChars())}L.push(Z.replace(/\s+$/,""))}return L.join(`
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function fn(e){let t=e.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(t.width,n.width),height:Math.max(0,Math.min(t.height,n.height-Math.max(0,t.top)))}:t}function hn(e,t,n){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let s=await mn.create(e,n.scrollback,n.theme),l,r=0,c,a=null,u=0,m=0,k=.2,L=10,H=.95,B=.04;function P(){u&&(cancelAnimationFrame(u),u=0),m=0}function We(i){if(P(),Math.abs(i)<k)return;m=Math.max(-L,Math.min(L,i));let o=()=>{if(j){u=0;return}let d=e.querySelector(".xterm-rows > div"),p=d?d.getBoundingClientRect().height:16,w=p>0?Math.round(m*16/p):m>0?1:-1;w===0&&(w=m>0?1:-1);let I=s.viewportY()-w,V=s.baseY(),_=Math.max(0,Math.min(I,V));if(s.scrollToLine(_,!0),m*=H,_!==I){u=0;return}if(Math.abs(m)<B){u=0;return}u=requestAnimationFrame(o)};u=requestAnimationFrame(o)}let Z=0,S="connecting",U=0,se=!1,xe=[],G="",j=!1,ie=1e3,zt=location.protocol==="https:"?"wss:":"ws:",jt=n.wsBase||"/ws",_t=zt+"//"+location.host+jt+"/"+encodeURIComponent(t),Yt="/api/session/"+encodeURIComponent(t)+"/upload",Wt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Ot(){return xe.join("")+G}async function Ut(i,o=0){let d=i?s.viewportY():0,p=ue(Ot());if(s.reset(),!p){i||s.scrollToBottom();return}await s.write(p),i?s.scrollToLine(o+d):s.scrollToBottom()}function q(i){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(i))}function qt(i){let o;try{o=JSON.parse(i)}catch{S==="live"&&(G+=i,s.write(ue(i)));return}if(o.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?q({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(o.type==="attach_failed"&&typeof o.message=="string"){Ce=!0,S="dead",s.reset(),s.write(`\r
4
+ \x1B[31m`+ue(o.message)+`\x1B[0m\r
5
+ `).then(()=>{ke();try{l?.close()}catch{}});return}if(o.type==="auth.ok"){o.token&&localStorage.setItem("tmux-web-token",o.token),ie=1e3,K(),q({type:"resize",cols:s.cols,rows:s.rows}),T();return}if(o.type==="auth.failed"){if(o.permanentLock||o.retryAfterMs&&o.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(o.message);return}return}if(o.type==="snapshot"&&typeof o.data=="string"){let d=Ke(ue(o.data));xe=[d],G="",U=typeof o.lines=="number"?o.lines:n.terminal.initialLines,S="live",s.reset(),K(),q({type:"resize",cols:s.cols,rows:s.rows}),s.write(d).then(()=>s.scrollToBottom());return}if(o.type==="window_changed"&&typeof o.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(o.activeIndex)&&(d.searchParams.set("window",String(o.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:o.windows}));return}if(o.type==="history"&&typeof o.data=="string"){o.lines>0&&o.data?(xe.unshift(Ke(o.data)),U+=o.lines,Ut(!0,o.lines).finally(()=>{se=!1})):se=!1;return}if(o.type==="data"&&typeof o.data=="string"){let d=Ke(ue(o.data));if(S==="connecting"){S="live",G=d,s.write(d);return}S==="live"&&(G+=d,s.write(d))}}let Ce=!1;function it(){if(Ce)return;let i=_t,o=localStorage.getItem("tmux-web-token");o&&(i+=(i.includes("?")?"&":"?")+"token="+encodeURIComponent(o)),i+=(i.includes("?")?"&":"?")+"cols="+s.cols+"&rows="+s.rows,l=new WebSocket(i),l.onopen=()=>{S="connecting",U=0,se=!1,xe=[],G="",s.reset(),ie=1e3},l.onmessage=d=>{typeof d.data=="string"&&qt(d.data)},l.onclose=()=>{j||(S="connecting",!Ce&&setTimeout(()=>{ie=Math.min(ie*2,1e4),!j&&!Ce&&it()},ie))},l.onerror=()=>l?.close()}function ae(i){q({type:"input",data:i})}function K(){return s.fit()}function ke(){e.classList.remove("terminal-pending")}function T(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),K(),r=requestAnimationFrame(()=>{r=0,K()&&ke()}),c=setTimeout(()=>{K()&&ke()},120)}function Kt(){T(),setTimeout(T,50),setTimeout(T,150),setTimeout(T,300)}function at(i){return typeof i=="string"&&i.startsWith("image/")}function ot(i){return i?.files?.length?i.files[0]??null:null}function Vt(i){if(!i?.items)return null;for(let o=0;o<i.items.length;o++){let d=i.items[o];if(d?.kind==="file"&&at(d.type))return d.getAsFile()}return null}async function Xt(i){let o=new FormData,d=i instanceof File&&i.name?i.name:"upload";o.append("file",i,d);let p=localStorage.getItem("tmux-web-token"),w={};p&&(w.Authorization="Bearer "+p);let I=await fetch(Yt,{method:"POST",body:o,headers:w});if(!I.ok){let _="upload failed";try{let Be=await I.json();Be.error&&(_=Be.error)}catch{}throw new Error(_)}let V=await I.json();if(!V.path)throw new Error("no path in response");return V.path}async function Oe(i){try{let o=await Xt(i);ae(o)}catch{}}s.onData(i=>ae(i)),s.onResize(({cols:i,rows:o})=>q({type:"resize",cols:i,rows:o})),s.onScroll(()=>{S!=="live"||se||!s.isNearScrollbackTop()||(se=!0,q({type:"load_history",before:U}))});let oe=!1,le=!1,b=null,E=null,re=document.createElement("div");re.className="terminal-copy-banner",re.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',re.hidden=!0,e.appendChild(re);let $=document.createElement("div");$.className="terminal-copy-banner terminal-copy-toast",$.textContent="Copied",$.hidden=!0,e.appendChild($);function lt(i,o){let d=e.querySelector(".xterm-screen, canvas"),p=(d&&d.getBoundingClientRect().width>0?d:e).getBoundingClientRect(),w=p.height>0&&s.rows>0?p.height/s.rows:18,I=p.width>0&&s.cols>0?p.width/s.cols:9;return{row:Math.max(0,Math.min(s.rows-1,Math.floor((o-p.top)/w))),col:Math.max(0,Math.min(s.cols-1,Math.floor((i-p.left)/I)))}}async function Se(i){if(!i)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(i);return}}catch{}let o=document.createElement("textarea");o.value=i,o.style.position="fixed",o.style.opacity="0",o.style.pointerEvents="none",document.body.appendChild(o),o.focus(),o.select();try{document.execCommand("copy")}catch{}o.remove()}let ce;function Te(){$.hidden=!1,ce&&clearTimeout(ce),ce=setTimeout(()=>{$.hidden=!0},1e3)}function ee(i){oe=i,re.hidden=!i,e.classList.toggle("terminal-copy-mode-active",i),i&&s.focus()}function rt(){ee(!oe)}function Jt(i){j||i.button!==0||le||(oe||i.shiftKey)&&(i.preventDefault(),i.stopPropagation(),le=!0,b=lt(i.clientX,i.clientY),E=b,s.selectCells(b.col,b.row,E.col,E.row),document.addEventListener("mousemove",Ue,!0),document.addEventListener("mouseup",qe,!0))}function Ue(i){j||!le||!b||(i.preventDefault(),i.stopPropagation(),E=lt(i.clientX,i.clientY),s.selectCells(b.col,b.row,E.col,E.row))}function qe(i){if(j||!le||!b||!E)return;i.preventDefault(),i.stopPropagation(),document.removeEventListener("mousemove",Ue,!0),document.removeEventListener("mouseup",qe,!0),le=!1;let o=s.getCellsText(b.col,b.row,E.col,E.row);b=null,E=null,s.clearSelection(),ee(!1),o&&($.textContent="\u5DF2\u590D\u5236",Se(o).then(Te)),s.focus()}e.addEventListener("mousedown",Jt,!0),document.addEventListener("keydown",Qt,!0);function Qt(i){if(!(j||!s.isFocused()||i.defaultPrevented||i.isComposing)){if(oe){if(i.key==="Escape"){i.preventDefault(),i.stopPropagation(),b=null,E=null,s.clearSelection(),ee(!1);return}if(i.key==="Enter"||(i.ctrlKey||i.metaKey)&&i.code==="KeyC"){i.preventDefault(),i.stopPropagation(),Se(s.getScreenText()).then(Te),ee(!1);return}}if(i.altKey&&i.code==="KeyC"&&!i.ctrlKey&&!i.metaKey){if(i.preventDefault(),i.stopPropagation(),b&&E){let o=s.getCellsText(b.col,b.row,E.col,E.row);b=null,E=null,s.clearSelection(),ee(!1),o&&($.textContent="Copied",Se(o).then(Te))}else{let o=s.getSelection();o?(s.clearSelection(),$.textContent="Copied",Se(o).then(Te)):rt()}return}if((i.ctrlKey||i.metaKey)&&i.code==="KeyV"){i.preventDefault(),i.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let o=await navigator.clipboard.read();for(let d of o)for(let p of d.types)if(at(p)){let w=await d.getType(p);await Oe(w);return}}}catch{}try{let o=await navigator.clipboard?.readText();o&&s.pasteText(ct(o))}catch{}})();return}}}let Me=0;function ct(i){return i.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function Zt(a){let i=Kt(a.clipboardData);if(i){a.preventDefault(),a.stopPropagation();let b=Date.now();if(b-Me<50)return;Me=b,await Oe(i);return}let d=a.clipboardData?.getData("text/plain");if(!d)return;a.preventDefault(),a.stopPropagation();let m=Date.now();m-Me<50||(Me=m,s.pasteText(rt(d)))}let re=0;function Gt(a){a.preventDefault(),re++,it(a.dataTransfer)&&e.classList.add("terminal-drag-over")}function Qt(a){a.preventDefault(),a.dataTransfer&&(a.dataTransfer.dropEffect="copy")}function en(a){a.preventDefault(),re--,re<=0&&(re=0,e.classList.remove("terminal-drag-over"))}async function tn(a){a.preventDefault(),re=0,e.classList.remove("terminal-drag-over");let i=it(a.dataTransfer);i&&await Oe(i)}function ct(a){!Wt||a.key!=="Escape"||ae||a.defaultPrevented||a.repeat||a.metaKey||a.ctrlKey||a.altKey||s.isFocused()&&(a.preventDefault(),a.stopPropagation(),xe("\x1B"))}e.addEventListener("paste",Zt),e.addEventListener("dragenter",Gt),e.addEventListener("dragover",Qt),e.addEventListener("dragleave",en),e.addEventListener("drop",tn),document.addEventListener("keydown",ct,!0);function nn(a){if(a.touches.length!==1){o=null;return}let i=a.touches[0];i&&(P(),o={startX:i.clientX,startY:i.clientY,lastX:i.clientX,lastY:i.clientY,lastT:performance.now(),velocity:0,scrolling:!1},a.stopPropagation())}function sn(a){if(!o||a.touches.length!==1)return;let i=a.touches[0];if(!i)return;let d=i.clientY-o.startY,m=i.clientX-o.startX;if(!o.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(m))return;o.scrolling=!0,Z=Date.now()+500}a.preventDefault(),a.stopPropagation();let b=performance.now(),A=i.clientY-o.lastY,K=b-o.lastT;K>0&&(o.velocity=A/K),rn(-A),o.lastX=i.clientX,o.lastY=i.clientY,o.lastT=b}function an(a){if(!o)return;let i=o.scrolling,d=o.velocity;if(o=null,a.stopPropagation(),!i){s.focus();return}Z=Date.now()+500,a.preventDefault(),d&&We(d)}function on(a){o=null,P(),a.stopPropagation()}function ln(a){a.pointerType==="touch"&&Date.now()<Z&&(a.preventDefault(),a.stopPropagation())}function rn(a){let i=e.querySelector(".xterm-rows > div"),d=i?i.getBoundingClientRect().height:16,m=d>0?Math.round(a/d):a>0?1:-1;m===0&&(m=a>0?1:-1),s.scrollLines(m)}e.addEventListener("touchstart",nn,{passive:!0,capture:!0}),e.addEventListener("touchmove",sn,{passive:!1,capture:!0}),e.addEventListener("touchend",an,{passive:!1,capture:!0}),e.addEventListener("touchcancel",on,{passive:!0,capture:!0}),e.addEventListener("pointerup",ln,!0);function dt(){T()}window.addEventListener("resize",dt),window.visualViewport?.addEventListener("resize",T),window.visualViewport?.addEventListener("scroll",T);let ut=new ResizeObserver(()=>T());ut.observe(e),document.fonts?.ready.then(()=>{T(),setTimeout(T,50),setTimeout(T,200)}),e.addEventListener("focusin",qt,!0);async function cn(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(a=>setTimeout(a,1e3))])}catch{}}return requestAnimationFrame(()=>{q()&&ke(),(async()=>(await cn(),q()&&Y({type:"resize",cols:s.cols,rows:s.rows}),st(),T()))()}),{destroy(){if(z=!0,P(),Q(!1),le&&clearTimeout(le),document.removeEventListener("mousemove",Ue,!0),document.removeEventListener("mouseup",Ye,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",dt),ut.disconnect(),document.removeEventListener("keydown",ct,!0)},focus(){s.focus()},sendInput(a){xe(a)},toggleCopyMode(){lt()}}})()}var ue=window.__TMUX_WEB_SHELL__,v=null,g=null,pe=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,M=pe;window.__tmuxWebScopeAgent=pe;var Ke="sessions",_="",Ie="",De="",mt="";function C(e){return M?"/a/"+encodeURIComponent(M)+e:e}var f=document.getElementById("sidebar-content"),V=document.getElementById("main-placeholder"),I=document.getElementById("terminal-container"),me=document.getElementById("file-editor"),hn=document.getElementById("fe-path"),F=document.getElementById("fe-status"),fe=document.getElementById("fe-content"),J=document.getElementById("fe-save"),vn=document.getElementById("fe-delete"),gn=document.getElementById("fe-back"),ft=document.getElementById("fe-new-name"),yn=document.getElementById("fe-new-btn"),Ve=document.getElementById("settings-backdrop"),ht=document.getElementById("settings-popover"),vt=document.querySelector(".sidebar");function wn(){return window.matchMedia("(max-width: 640px)").matches}function Je(){wn()&&vt.classList.add("collapsed")}function gt(){vt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{gt(),yt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{gt(),yt("files")});function yt(e){Ke=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?(Xe="",W()):An()}var Xe="";async function W(){if(Ke!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),n=JSON.stringify({recent:t.recent,agents:t.agents,tombstones:t.tombstones});if(n===Xe)return;Xe=n,xn(t)}catch{f.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>{let t=window.__openNewSessionModal;typeof t=="function"?t():document.getElementById("new-session-modal")?.classList.add("open")}),nt()}var wt="tmux-web-sidebar-group-collapsed";function bt(){try{return new Set(JSON.parse(localStorage.getItem(wt)||"[]"))}catch{return new Set}}function bn(e){try{localStorage.setItem(wt,JSON.stringify([...e]))}catch{}}function Ln(e,t,n){if(!e)return"\u2026";let s=n&&(e===n||e.startsWith(n+"/"))?"~"+e.slice(n.length):e,l=s.split("/").filter(Boolean);if(l.length===0)return e;for(let r=1;r<=l.length;r++){let c=l.slice(-r).join("/");if(!t.some(o=>!o||o===e?!1:(n&&(o===n||o.startsWith(n+"/"))?"~"+o.slice(n.length):o).split("/").filter(Boolean).slice(-r).join("/")===c))return c}return s}function Lt(e,t,n,s=!0){let l=new Map;for(let o of e){let u=o.path||"";l.has(u)||l.set(u,[]),l.get(u).push(o)}let r=[...l.keys()],c=bt();for(let[o,u]of l){let p=Ln(o,r,t),k=c.has(o),L=document.createElement("div");L.className="session-group-header"+(k?" collapsed":""),L.title=o||"No working directory",L.innerHTML=`<span class="sg-folder">&#128193;</span><span class="sg-label">${h(p)}</span>`,L.addEventListener("click",()=>{let B=bt(),P=L.nextElementSibling;B.has(o)?(B.delete(o),L.classList.remove("collapsed"),P?.classList.remove("collapsed")):(B.add(o),L.classList.add("collapsed"),P?.classList.add("collapsed")),bn(B)});let H=document.createElement("div");H.className="session-group-body"+(k?" collapsed":"");for(let B of u)H.appendChild(n?En(B,n,s):Cn(B));f.appendChild(L),f.appendChild(H)}}function En(e,t,n){let s=document.createElement("div");return s.className="session-item"+(e.name===g&&M===t?" active":"")+(n?"":" session-offline"),s.setAttribute("data-session",e.name),s.setAttribute("data-agent",t),s.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
9
- <span>${h(e.name)}</span>${n?"":'<span class="meta">offline</span>'}`,n&&s.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||he(e.name,t)}),s}function Cn(e){let t=document.createElement("div");t.className="session-item"+(e.name===g&&!M?" active":""),t.setAttribute("data-session",e.name),t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
8
+ `).join("\r")}async function Zt(i){let o=Vt(i.clipboardData);if(o){i.preventDefault(),i.stopPropagation();let w=Date.now();if(w-Me<50)return;Me=w,await Oe(o);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let p=Date.now();p-Me<50||(Me=p,s.pasteText(ct(d)))}let de=0;function Gt(i){i.preventDefault(),de++,ot(i.dataTransfer)&&e.classList.add("terminal-drag-over")}function en(i){i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy")}function tn(i){i.preventDefault(),de--,de<=0&&(de=0,e.classList.remove("terminal-drag-over"))}async function nn(i){i.preventDefault(),de=0,e.classList.remove("terminal-drag-over");let o=ot(i.dataTransfer);o&&await Oe(o)}function dt(i){!Wt||i.key!=="Escape"||oe||i.defaultPrevented||i.repeat||i.metaKey||i.ctrlKey||i.altKey||s.isFocused()&&(i.preventDefault(),i.stopPropagation(),ae("\x1B"))}e.addEventListener("paste",Zt),e.addEventListener("dragenter",Gt),e.addEventListener("dragover",en),e.addEventListener("dragleave",tn),e.addEventListener("drop",nn),document.addEventListener("keydown",dt,!0);function sn(i){if(i.touches.length!==1){a=null;return}let o=i.touches[0];if(!o)return;P();let d=o.clientX-e.getBoundingClientRect().left<e.clientWidth*.4?"tui":"history";a={startX:o.clientX,startY:o.clientY,lastX:o.clientX,lastY:o.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:d,tuiAccum:0},i.stopPropagation()}function an(i){if(!a||i.touches.length!==1)return;let o=i.touches[0];if(!o)return;let d=o.clientY-a.startY,p=o.clientX-a.startX;if(!a.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(p))return;a.scrolling=!0,Z=Date.now()+500}i.preventDefault(),i.stopPropagation();let w=performance.now(),I=o.clientY-a.lastY;if(a.zone==="tui"){a.tuiAccum+=I;let _=e.querySelector(".xterm-rows > div"),Be=_?_.getBoundingClientRect().height:16,mt=Math.max(24,Be*1.2);for(;Math.abs(a.tuiAccum)>=mt;)ae(a.tuiAccum>0?"\x1B[6~":"\x1B[5~"),a.tuiAccum-=Math.sign(a.tuiAccum)*mt;a.lastX=o.clientX,a.lastY=o.clientY,a.lastT=w;return}let V=w-a.lastT;V>0&&(a.velocity=I/V),cn(-I),a.lastX=o.clientX,a.lastY=o.clientY,a.lastT=w}function on(i){if(!a)return;let o=a.scrolling,d=a.zone,p=a.velocity;if(a=null,i.stopPropagation(),!o){s.focus();return}Z=Date.now()+500,i.preventDefault(),d!=="tui"&&p&&We(p)}function ln(i){a=null,P(),i.stopPropagation()}function rn(i){i.pointerType==="touch"&&Date.now()<Z&&(i.preventDefault(),i.stopPropagation())}function cn(i){let o=e.querySelector(".xterm-rows > div"),d=o?o.getBoundingClientRect().height:16,p=d>0?Math.round(i/d):i>0?1:-1;p===0&&(p=i>0?1:-1),s.scrollLines(p)}e.addEventListener("touchstart",sn,{passive:!0,capture:!0}),e.addEventListener("touchmove",an,{passive:!1,capture:!0}),e.addEventListener("touchend",on,{passive:!1,capture:!0}),e.addEventListener("touchcancel",ln,{passive:!0,capture:!0}),e.addEventListener("pointerup",rn,!0);function ut(){T()}window.addEventListener("resize",ut),window.visualViewport?.addEventListener("resize",T),window.visualViewport?.addEventListener("scroll",T);let pt=new ResizeObserver(()=>T());pt.observe(e),document.fonts?.ready.then(()=>{T(),setTimeout(T,50),setTimeout(T,200)}),e.addEventListener("focusin",Kt,!0);async function dn(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(i=>setTimeout(i,1e3))])}catch{}}return requestAnimationFrame(()=>{K()&&ke(),(async()=>(await dn(),K()&&q({type:"resize",cols:s.cols,rows:s.rows}),it(),T()))()}),{destroy(){if(j=!0,P(),ee(!1),ce&&clearTimeout(ce),document.removeEventListener("mousemove",Ue,!0),document.removeEventListener("mouseup",qe,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",ut),pt.disconnect(),document.removeEventListener("keydown",dt,!0)},focus(){s.focus()},sendInput(i){ae(i)},toggleCopyMode(){rt()}}})()}var pe=window.__TMUX_WEB_SHELL__,v=null,g=null,me=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,M=me;window.__tmuxWebScopeAgent=me;var Ve="sessions",Y="",De="",Ae="",ft="";function x(e){return M?"/a/"+encodeURIComponent(M)+e:e}var f=document.getElementById("sidebar-content"),X=document.getElementById("main-placeholder"),D=document.getElementById("terminal-container"),fe=document.getElementById("file-editor"),vn=document.getElementById("fe-path"),F=document.getElementById("fe-status"),he=document.getElementById("fe-content"),J=document.getElementById("fe-save"),gn=document.getElementById("fe-delete"),yn=document.getElementById("fe-back"),ht=document.getElementById("fe-new-name"),wn=document.getElementById("fe-new-btn"),Xe=document.getElementById("settings-backdrop"),vt=document.getElementById("settings-popover"),gt=document.querySelector(".sidebar");function bn(){return window.matchMedia("(max-width: 640px)").matches}function Je(){bn()&&gt.classList.add("collapsed")}function yt(){gt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{yt(),wt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{yt(),wt("files")});function wt(e){Ve=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?(Qe="",W()):Fn()}var Qe="";async function W(){if(Ve!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),n=JSON.stringify({recent:t.recent,agents:t.agents,tombstones:t.tombstones});if(n===Qe)return;Qe=n,kn(t)}catch{f.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>{let t=window.__openNewSessionModal;typeof t=="function"?t():document.getElementById("new-session-modal")?.classList.add("open")}),st()}var bt="tmux-web-sidebar-group-collapsed";function Lt(){try{return new Set(JSON.parse(localStorage.getItem(bt)||"[]"))}catch{return new Set}}function Ln(e){try{localStorage.setItem(bt,JSON.stringify([...e]))}catch{}}function En(e,t,n){if(!e)return"\u2026";let s=n&&(e===n||e.startsWith(n+"/"))?"~"+e.slice(n.length):e,l=s.split("/").filter(Boolean);if(l.length===0)return e;for(let r=1;r<=l.length;r++){let c=l.slice(-r).join("/");if(!t.some(a=>!a||a===e?!1:(n&&(a===n||a.startsWith(n+"/"))?"~"+a.slice(n.length):a).split("/").filter(Boolean).slice(-r).join("/")===c))return c}return s}function Et(e,t,n,s=!0){let l=new Map;for(let a of e){let u=a.path||"";l.has(u)||l.set(u,[]),l.get(u).push(a)}let r=[...l.keys()],c=Lt();for(let[a,u]of l){let m=En(a,r,t),k=c.has(a),L=document.createElement("div");L.className="session-group-header"+(k?" collapsed":""),L.title=a||"No working directory",L.innerHTML=`<span class="sg-folder">&#128193;</span><span class="sg-label">${h(m)}</span>`,L.addEventListener("click",()=>{let B=Lt(),P=L.nextElementSibling;B.has(a)?(B.delete(a),L.classList.remove("collapsed"),P?.classList.remove("collapsed")):(B.add(a),L.classList.add("collapsed"),P?.classList.add("collapsed")),Ln(B)});let H=document.createElement("div");H.className="session-group-body"+(k?" collapsed":"");for(let B of u)H.appendChild(n?xn(B,n,s):Cn(B));f.appendChild(L),f.appendChild(H)}}function xn(e,t,n){let s=document.createElement("div");return s.className="session-item"+(e.name===g&&M===t?" active":"")+(n?"":" session-offline"),s.setAttribute("data-session",e.name),s.setAttribute("data-agent",t),s.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
9
+ <span>${h(e.name)}</span>${n?"":'<span class="meta">offline</span>'}`,n&&s.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||ve(e.name,t)}),s}function Cn(e){let t=document.createElement("div");t.className="session-item"+(e.name===g&&!M?" active":""),t.setAttribute("data-session",e.name),t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
10
10
  <span>${h(e.name)}</span>
11
11
  <button class="session-edit-btn" data-session="${h(e.name)}" title="Edit session"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
12
- `,t.addEventListener("click",s=>{s.target.closest(".session-edit-btn")||he(e.name)});let n=t.querySelector(".session-edit-btn");return n&&n.addEventListener("click",s=>{s.stopPropagation(),Ht(e.name,n)}),t}function xn(e){f.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let t=e.recent||[],n=document.createElement("div");n.className="sidebar-section-label"+(pe?" clickable":""),n.textContent=pe?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",pe&&(n.title="Back to hub",n.addEventListener("click",()=>{window.location.href="/"})),f.appendChild(n),Lt(t,e.home);let s=e.agents||[];for(let r of s){if(!r.sessions||r.sessions.length===0)continue;let c=r.agentId===M,o=document.createElement("div");o.className="sidebar-section-label machine-label"+(r.online?"":" agent-offline-label")+(!c&&r.online?" clickable":""),o.textContent=r.agentName+(r.online?"":" (offline)"),!c&&r.online&&(o.title="\u6253\u5F00 "+r.agentName,o.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(r.agentId)+"/"})),f.appendChild(o),Lt(r.sessions,e.home,r.agentId,r.online)}let l=e.tombstones||[];if(l.length>0){let r=document.createElement("div");r.className="sidebar-section-label tombstone-label",r.textContent="\u5931\u6548\u4F1A\u8BDD",f.appendChild(r);for(let c of l){let o=document.createElement("div");o.className="session-item session-tombstone",o.setAttribute("data-session",c.name),c.agentId&&o.setAttribute("data-agent",c.agentId);let u=c.agentId?c.agentOnline?" (agent)":" (agent \u79BB\u7EBF)":"";o.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"></svg>
12
+ `,t.addEventListener("click",s=>{s.target.closest(".session-edit-btn")||ve(e.name)});let n=t.querySelector(".session-edit-btn");return n&&n.addEventListener("click",s=>{s.stopPropagation(),Ht(e.name,n)}),t}function kn(e){f.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let t=e.recent||[],n=document.createElement("div");n.className="sidebar-section-label"+(me?" clickable":""),n.textContent=me?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",me&&(n.title="Back to hub",n.addEventListener("click",()=>{window.location.href="/"})),f.appendChild(n),Et(t,e.home);let s=e.agents||[];for(let r of s){if(!r.sessions||r.sessions.length===0)continue;let c=r.agentId===M,a=document.createElement("div");a.className="sidebar-section-label machine-label"+(r.online?"":" agent-offline-label")+(!c&&r.online?" clickable":""),a.textContent=r.agentName+(r.online?"":" (offline)"),!c&&r.online&&(a.title="\u6253\u5F00 "+r.agentName,a.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(r.agentId)+"/"})),f.appendChild(a),Et(r.sessions,e.home,r.agentId,r.online)}let l=e.tombstones||[];if(l.length>0){let r=document.createElement("div");r.className="sidebar-section-label tombstone-label",r.textContent="\u5931\u6548\u4F1A\u8BDD",f.appendChild(r);for(let c of l){let a=document.createElement("div");a.className="session-item session-tombstone",a.setAttribute("data-session",c.name),c.agentId&&a.setAttribute("data-agent",c.agentId);let u=c.agentId?c.agentOnline?" (agent)":" (agent \u79BB\u7EBF)":"";a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"></svg>
13
13
  <span>${h(c.name)}</span><span class="meta">\u5931\u6548${u}</span>
14
- <button class="session-edit-btn" data-session="${h(c.name)}" title="\u6062\u590D\u4F1A\u8BDD"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>`;let p=o.querySelector(".session-edit-btn");p&&p.addEventListener("click",k=>{k.stopPropagation(),Ht(c.name,p,{tombstone:!0,agentId:c.agentId})}),f.appendChild(o)}}}function h(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function he(e,t){if(!(g===e&&v&&M===(t||null))){me.style.display="none",V.style.display="none",I.style.display="",I.classList.add("terminal-pending"),Je(),v&&(je(),v.destroy(),v=null),g=e,M=t||null,window.__tmuxWebScopeAgent=M,window.__tmuxWebCurrentSession=g,Yn(e,t);for(let n of f.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===e&&(n.getAttribute("data-agent")||null)===M);try{v=await fn(I,e,{terminal:ue.terminal,scrollback:ue.scrollback,theme:ue.theme,wsBase:M?"/ws/a/"+encodeURIComponent(M):ue.wsBase||"/ws"}),Dt()}catch(n){console.error("[shell] terminal init failed:",n);let s=n&&typeof n=="object"&&"message"in n?n.message:String(n);I.textContent="Failed to open terminal for "+e+": "+s,I.classList.remove("terminal-pending")}}}var He=[],y=null,Et=!1;function kn(e){if(!y||!y.files.length)return null;let t=y.repoRoot;if(!t||!e.startsWith(t))return null;let n=e.slice(t.length+1);for(let s of y.files)if(s.path===n)return s.status;return null}async function Sn(e){Et=!0;try{let t=await fetch(C("/api/git/status?path="+encodeURIComponent(e)));t.ok?y=await t.json():y=null}catch{y=null}finally{Et=!1}}function Tn(){if(!y||!y.branch)return"";let{branch:e,linesAdded:t,linesRemoved:n}=y,s=`<div class="sidebar-git"><div class="git-branch-badge">
14
+ <button class="session-edit-btn" data-session="${h(c.name)}" title="\u6062\u590D\u4F1A\u8BDD"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>`;let m=a.querySelector(".session-edit-btn");m&&m.addEventListener("click",k=>{k.stopPropagation(),Ht(c.name,m,{tombstone:!0,agentId:c.agentId})}),f.appendChild(a)}}}function h(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function ve(e,t){if(!(g===e&&v&&M===(t||null))){fe.style.display="none",X.style.display="none",D.style.display="",D.classList.add("terminal-pending"),Je(),v&&(je(),v.destroy(),v=null),g=e,M=t||null,window.__tmuxWebScopeAgent=M,window.__tmuxWebCurrentSession=g,qn(e,t);for(let n of f.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===e&&(n.getAttribute("data-agent")||null)===M);try{v=await hn(D,e,{terminal:pe.terminal,scrollback:pe.scrollback,theme:pe.theme,wsBase:M?"/ws/a/"+encodeURIComponent(M):pe.wsBase||"/ws"}),At()}catch(n){console.error("[shell] terminal init failed:",n);let s=n&&typeof n=="object"&&"message"in n?n.message:String(n);D.textContent="Failed to open terminal for "+e+": "+s,D.classList.remove("terminal-pending")}}}var He=[],y=null,xt=!1;function Sn(e){if(!y||!y.files.length)return null;let t=y.repoRoot;if(!t||!e.startsWith(t))return null;let n=e.slice(t.length+1);for(let s of y.files)if(s.path===n)return s.status;return null}async function Tn(e){xt=!0;try{let t=await fetch(x("/api/git/status?path="+encodeURIComponent(e)));t.ok?y=await t.json():y=null}catch{y=null}finally{xt=!1}}function Mn(){if(!y||!y.branch)return"";let{branch:e,linesAdded:t,linesRemoved:n}=y,s=`<div class="sidebar-git"><div class="git-branch-badge">
15
15
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M11.63 1.22a.75.75 0 00-1.06 0L5.99 5.8 4.12 3.93a.75.75 0 10-1.06 1.06L4.93 6.87A3.5 3.5 0 003 10.09v3.16a.75.75 0 001 0v-3.16a2.5 2.5 0 012.5-2.5h.27l-1.06 1.06a.75.75 0 001.06 1.06l2.37-2.38a.75.75 0 000-1.06L7.59 5.27l4.04-4.05a.75.75 0 000-1.06L11.63 1.22zM3.25 13.94a.75.75 0 01-.75-.75v-.69a.75.75 0 011.5 0v.69c0 .414-.336.75-.75.75z"/></svg>
16
- ${h(e)}</div>`;return(t>0||n>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${n}</span></div>`),s+"</div>"}f.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&ee&&y&&(e.stopPropagation(),Tt())});var ve=document.getElementById("header-git"),Ct=document.getElementById("git-diff-view"),xt=document.getElementById("gd-path"),Ae=document.getElementById("gd-status"),Fe=document.getElementById("gd-content"),Mn=document.getElementById("gd-back"),ee=null;function ge(e){if(!e||!e.branch){ve.style.display="none",ee=null;return}ee=e.repoRoot;let{branch:t,linesAdded:n,linesRemoved:s}=e,l=`<span class="branch">${h(t)}</span>`;(n>0||s>0)&&(l+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),ve.innerHTML=l,ve.style.display="block"}ve.addEventListener("click",e=>{ee&&y&&(e.stopPropagation(),Tt())});var x=document.getElementById("git-popover"),Ne=document.getElementById("git-popover-backdrop");function Ze(){let e=ve.getBoundingClientRect();x.style.top=e.bottom+4+"px",x.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function kt(){let e=ee,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(Pe(),e){Ke="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),O(e)}})}var St="";function Tt(){if(!y)return;let e=y.files||[];if(!e.length)x.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
16
+ ${h(e)}</div>`;return(t>0||n>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${n}</span></div>`),s+"</div>"}f.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&te&&y&&(e.stopPropagation(),Mt())});var ge=document.getElementById("header-git"),Ct=document.getElementById("git-diff-view"),kt=document.getElementById("gd-path"),Fe=document.getElementById("gd-status"),Re=document.getElementById("gd-content"),Bn=document.getElementById("gd-back"),te=null;function ye(e){if(!e||!e.branch){ge.style.display="none",te=null;return}te=e.repoRoot;let{branch:t,linesAdded:n,linesRemoved:s}=e,l=`<span class="branch">${h(t)}</span>`;(n>0||s>0)&&(l+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),ge.innerHTML=l,ge.style.display="block"}ge.addEventListener("click",e=>{te&&y&&(e.stopPropagation(),Mt())});var C=document.getElementById("git-popover"),Ne=document.getElementById("git-popover-backdrop");function Ze(){let e=ge.getBoundingClientRect();C.style.top=e.bottom+4+"px",C.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function St(){let e=te,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if($e(),e){Ve="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),O(e)}})}var Tt="";function Mt(){if(!y)return;let e=y.files||[];if(!e.length)C.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
17
17
  <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let t=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let n=0;n<e.length;n++){let s=e[n],l=s.additions||0,r=s.deletions||0,c="";switch(s.status){case"M":c='<span class="git-file-status M">M</span>';break;case"A":c='<span class="git-file-status A">A</span>';break;case"D":c='<span class="git-file-status D">D</span>';break;case"?":c='<span class="git-file-status ?">?</span>';break;case"R":c='<span class="git-file-status R">R</span>';break}t+=`<div class="git-popover-item" data-idx="${n}">
18
18
  ${c}<span class="file-path">${h(s.path)}</span>
19
19
  <span class="file-add">${l>0?"+"+l:""}</span>
20
20
  <span class="file-del">${r>0?"-"+r:""}</span>
21
- </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',St=t,x.innerHTML=t,x.querySelectorAll(".git-popover-item").forEach(n=>{let s=parseInt(n.dataset.idx||"0",10),l=e[s];!l||l.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>Bt(l.path)))})}Ze(),x.classList.add("open"),Ne.classList.add("open"),kt()}function Bn(){return window.innerWidth>=1024}function Mt(e,t){let n=(e+(t&&e?`
21
+ </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',Tt=t,C.innerHTML=t,C.querySelectorAll(".git-popover-item").forEach(n=>{let s=parseInt(n.dataset.idx||"0",10),l=e[s];!l||l.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>It(l.path)))})}Ze(),C.classList.add("open"),Ne.classList.add("open"),St()}function In(){return window.innerWidth>=1024}function Bt(e,t){let n=(e+(t&&e?`
22
22
  `:"")+t).split(`
23
- `),s="",l=0,r=0;for(let c of n){let o=c;if(o.startsWith("@@")){let u=o.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(l=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),s+=`<div class="diff-line hunk"><span class="ln-body">${h(o)}</span></div>`;continue}if(o.startsWith("diff --git")||o.startsWith("index ")||o.startsWith("--- ")||o.startsWith("+++ ")||o.startsWith("new file")||o.startsWith("deleted file")){o.startsWith("diff --git")&&(s+=`<div class="diff-line header"><span class="ln-body">${h(o)}</span></div>`);continue}if(o.startsWith("+"))r++,s+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(o)}</span></div>`;else if(o.startsWith("-"))l++,s+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${h(o)}</span></div>`;else if(o.startsWith("\\"))s+=`<div class="diff-line"><span class="ln-body">${h(o)}</span></div>`;else{if(!o&&!l&&!r)continue;l++,r++,s+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(o)}</span></div>`}}return{html:s,lineCount:n.length}}var $e=null;async function Bt(e){let t=ee;if(t)if(Pe(),Bn()){I.style.display="none",v&&(je(),v.destroy(),v=null,$e=g,g=null),ge(null),me.style.display="none",V.style.display="none",Ct.style.display="flex",xt.textContent=e,Ae.textContent="Loading\u2026",Fe.innerHTML="";try{let n=await fetch(C("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!n.ok)throw new Error(await n.text());let s=await n.json();Dn(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);Ae.textContent="Error",Fe.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${h(s)}</div>`}}else{x.innerHTML=`<div class="git-diff-view"><div class="diff-header">
23
+ `),s="",l=0,r=0;for(let c of n){let a=c;if(a.startsWith("@@")){let u=a.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(l=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),s+=`<div class="diff-line hunk"><span class="ln-body">${h(a)}</span></div>`;continue}if(a.startsWith("diff --git")||a.startsWith("index ")||a.startsWith("--- ")||a.startsWith("+++ ")||a.startsWith("new file")||a.startsWith("deleted file")){a.startsWith("diff --git")&&(s+=`<div class="diff-line header"><span class="ln-body">${h(a)}</span></div>`);continue}if(a.startsWith("+"))r++,s+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(a)}</span></div>`;else if(a.startsWith("-"))l++,s+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${h(a)}</span></div>`;else if(a.startsWith("\\"))s+=`<div class="diff-line"><span class="ln-body">${h(a)}</span></div>`;else{if(!a&&!l&&!r)continue;l++,r++,s+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(a)}</span></div>`}}return{html:s,lineCount:n.length}}var Pe=null;async function It(e){let t=te;if(t)if($e(),In()){D.style.display="none",v&&(je(),v.destroy(),v=null,Pe=g,g=null),ye(null),fe.style.display="none",X.style.display="none",Ct.style.display="flex",kt.textContent=e,Fe.textContent="Loading\u2026",Re.innerHTML="";try{let n=await fetch(x("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!n.ok)throw new Error(await n.text());let s=await n.json();An(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);Fe.textContent="Error",Re.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${h(s)}</div>`}}else{C.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
24
  <button class="diff-back">\u2190 Back</button>
25
25
  <span class="diff-filename">${h(e)}</span>
26
26
  <span class="diff-loading">Loading\u2026</span>
27
- </div></div>`,Ze(),x.classList.add("open"),Ne.classList.add("open");try{let n=await fetch(C("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!n.ok)throw new Error(await n.text());let s=await n.json();In(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);x.innerHTML=`<div class="git-diff-view"><div class="diff-header">
27
+ </div></div>`,Ze(),C.classList.add("open"),Ne.classList.add("open");try{let n=await fetch(x("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!n.ok)throw new Error(await n.text());let s=await n.json();Dn(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);C.innerHTML=`<div class="git-diff-view"><div class="diff-header">
28
28
  <button class="diff-back">\u2190 Back</button>
29
29
  <span class="diff-filename">${h(e)}</span>
30
- </div><div class="diff-empty">Error loading diff: ${h(s)}</div></div>`}}}function In(e,t,n){if(!t&&!n){x.innerHTML=`<div class="git-diff-view"><div class="diff-header">
30
+ </div><div class="diff-empty">Error loading diff: ${h(s)}</div></div>`}}}function Dn(e,t,n){if(!t&&!n){C.innerHTML=`<div class="git-diff-view"><div class="diff-header">
31
31
  <button class="diff-back">\u2190 Back</button>
32
32
  <span class="diff-filename">${h(e)}</span>
33
- </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:s,lineCount:l}=Mt(t,n);x.innerHTML=`<div class="git-diff-view"><div class="diff-header">
33
+ </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:s,lineCount:l}=Bt(t,n);C.innerHTML=`<div class="git-diff-view"><div class="diff-header">
34
34
  <button class="diff-back">\u2190 Back</button>
35
35
  <span class="diff-filename">${h(e)}</span>
36
36
  <span class="diff-filesize">${l} lines</span>
37
- </div><div class="diff-content">${s}</div></div>`,x.querySelector(".diff-back")?.addEventListener("click",()=>{x.innerHTML=St,Ze(),y&&x.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),o=y.files[c];!o||o.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>Bt(o.path)))}),kt()})}function Dn(e,t,n){if(!t&&!n){Ae.textContent="No diff",Fe.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:s,lineCount:l}=Mt(t,n);xt.textContent=e,Ae.textContent=l+" lines",Fe.innerHTML=s}function Hn(){Ct.style.display="none",$e?(I.style.display="",I.classList.add("terminal-pending"),he($e),$e=null):g?(I.style.display="",I.classList.add("terminal-pending"),Dt()):V.style.display="flex"}Mn.addEventListener("click",Hn);function Pe(){x.classList.remove("open"),Ne.classList.remove("open")}Ne.addEventListener("click",Pe),document.addEventListener("keydown",e=>{e.key==="Escape"&&(Pe(),_e())});async function It(e){try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(n.ok){let s=await n.json();y=s,ge(s);return}}}catch{}y=null,ge(null)}var Re=null;function Dt(){je(),g&&(It(g),Re=setInterval(()=>{g&&It(g)},3e4))}function je(){Re&&(clearInterval(Re),Re=null)}function An(){if(He=ue.fsRoots||[],He.length===0){if(g){Fn(g);return}f.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}f.innerHTML="";for(let e of He){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(e)}`,t.addEventListener("click",()=>O(e)),f.appendChild(t)}}async function Fn(e){f.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(C("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?O(t.path):f.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{f.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function O(e){_=e,f.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=Sn(e);try{let n=await(await fetch(C("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,f.innerHTML="",ge(y),f.innerHTML+=Tn();let s=Nn(e);if(s&&!He.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>O(s)),f.appendChild(r)}if(n.dirs)for(let r of n.dirs){let c=r.split("/").pop()||r.split("\\").pop(),o=document.createElement("div");o.className="file-tree-item",o.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(c)}`,o.addEventListener("click",()=>O(r)),f.appendChild(o)}else f.innerHTML="";let l=n.files||[];for(let r of l){let c=r.split("/").pop()||r.split("\\").pop(),o=kn(r),u="",p="";if(o)switch(o){case"M":u="git-mod",p='<span class="git-file-status M">M</span>';break;case"A":u="git-add",p='<span class="git-file-status A">A</span>';break;case"D":u="git-del",p='<span class="git-file-status D">D</span>';break;case"?":u="git-untracked",p='<span class="git-file-status ?">?</span>';break;case"R":u="git-mod",p='<span class="git-file-status R">R</span>';break}let k=document.createElement("div");k.className="file-tree-item file"+(u?" "+u:""),k.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${p}${h(c)}`,k.addEventListener("click",()=>$n(r)),f.appendChild(k)}}catch{f.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function Nn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function $n(e){Ie=e,I.style.display="none",v&&(je(),v.destroy(),v=null,g=null),ge(null),Je(),V.style.display="none",me.style.display="flex",hn.textContent=e,F.textContent="Loading...",J.style.display="none";try{let t=await fetch(C("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let s=await t.json();F.textContent=s.error||"Failed to load",fe.value="";return}let n=await t.json();fe.value=n.content,De=n.content,mt=n.content,F.textContent=n.size+" bytes",J.style.display=""}catch{F.textContent="Failed to load",fe.value=""}}fe.addEventListener("input",()=>{De=fe.value}),J.addEventListener("click",async()=>{J.textContent="Saving...",J.disabled=!0;try{let e=await fetch(C("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ie,content:De})});if(!e.ok){let t=await e.json();F.textContent=t.error||"Save failed";return}mt=De,F.textContent="Saved"}catch{F.textContent="Save failed"}finally{J.disabled=!1,J.textContent="Save"}}),vn.addEventListener("click",async()=>{if(confirm("Delete "+Ie+"?"))try{let e=await fetch(C("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ie})});if(!e.ok){let t=await e.json();F.textContent=t.error||"Delete failed";return}me.style.display="none",V.style.display="flex",_&&O(_)}catch{F.textContent="Delete failed"}}),gn.addEventListener("click",()=>{me.style.display="none",V.style.display="flex",_&&O(_)}),yn.addEventListener("click",async()=>{let e=ft.value.trim();if(!e)return;let t=_+"/"+e;try{let n=await fetch(C("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){let s=await n.json();F.textContent=s.error||"Create failed";return}ft.value="",_&&O(_)}catch{F.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=ht.classList.toggle("open");Ve.classList.toggle("open",e)}),Ve.addEventListener("click",()=>{ht.classList.remove("open"),Ve.classList.remove("open")});var D="",j=!1,ze,ye=document.createElement("div");ye.className="session-popover-backdrop",document.body.appendChild(ye);var N=document.createElement("div");N.id="session-popover",N.className="session-popover",N.style.display="none",N.innerHTML=`<div class="sp-field">
37
+ </div><div class="diff-content">${s}</div></div>`,C.querySelector(".diff-back")?.addEventListener("click",()=>{C.innerHTML=Tt,Ze(),y&&C.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),a=y.files[c];!a||a.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>It(a.path)))}),St()})}function An(e,t,n){if(!t&&!n){Fe.textContent="No diff",Re.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:s,lineCount:l}=Bt(t,n);kt.textContent=e,Fe.textContent=l+" lines",Re.innerHTML=s}function Hn(){Ct.style.display="none",Pe?(D.style.display="",D.classList.add("terminal-pending"),ve(Pe),Pe=null):g?(D.style.display="",D.classList.add("terminal-pending"),At()):X.style.display="flex"}Bn.addEventListener("click",Hn);function $e(){C.classList.remove("open"),Ne.classList.remove("open")}Ne.addEventListener("click",$e),document.addEventListener("keydown",e=>{e.key==="Escape"&&($e(),Ye())});async function Dt(e){try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(n.ok){let s=await n.json();y=s,ye(s);return}}}catch{}y=null,ye(null)}var ze=null;function At(){je(),g&&(Dt(g),ze=setInterval(()=>{g&&Dt(g)},3e4))}function je(){ze&&(clearInterval(ze),ze=null)}function Fn(){if(He=pe.fsRoots||[],He.length===0){if(g){Rn(g);return}f.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}f.innerHTML="";for(let e of He){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(e)}`,t.addEventListener("click",()=>O(e)),f.appendChild(t)}}async function Rn(e){f.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(x("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?O(t.path):f.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{f.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function O(e){Y=e,f.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=Tn(e);try{let n=await(await fetch(x("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,f.innerHTML="",ye(y),f.innerHTML+=Mn();let s=Nn(e);if(s&&!He.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>O(s)),f.appendChild(r)}if(n.dirs)for(let r of n.dirs){let c=r.split("/").pop()||r.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item",a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(c)}`,a.addEventListener("click",()=>O(r)),f.appendChild(a)}else f.innerHTML="";let l=n.files||[];for(let r of l){let c=r.split("/").pop()||r.split("\\").pop(),a=Sn(r),u="",m="";if(a)switch(a){case"M":u="git-mod",m='<span class="git-file-status M">M</span>';break;case"A":u="git-add",m='<span class="git-file-status A">A</span>';break;case"D":u="git-del",m='<span class="git-file-status D">D</span>';break;case"?":u="git-untracked",m='<span class="git-file-status ?">?</span>';break;case"R":u="git-mod",m='<span class="git-file-status R">R</span>';break}let k=document.createElement("div");k.className="file-tree-item file"+(u?" "+u:""),k.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${m}${h(c)}`,k.addEventListener("click",()=>Pn(r)),f.appendChild(k)}}catch{f.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function Nn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Pn(e){De=e,D.style.display="none",v&&(je(),v.destroy(),v=null,g=null),ye(null),Je(),X.style.display="none",fe.style.display="flex",vn.textContent=e,F.textContent="Loading...",J.style.display="none";try{let t=await fetch(x("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let s=await t.json();F.textContent=s.error||"Failed to load",he.value="";return}let n=await t.json();he.value=n.content,Ae=n.content,ft=n.content,F.textContent=n.size+" bytes",J.style.display=""}catch{F.textContent="Failed to load",he.value=""}}he.addEventListener("input",()=>{Ae=he.value}),J.addEventListener("click",async()=>{J.textContent="Saving...",J.disabled=!0;try{let e=await fetch(x("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:De,content:Ae})});if(!e.ok){let t=await e.json();F.textContent=t.error||"Save failed";return}ft=Ae,F.textContent="Saved"}catch{F.textContent="Save failed"}finally{J.disabled=!1,J.textContent="Save"}}),gn.addEventListener("click",async()=>{if(confirm("Delete "+De+"?"))try{let e=await fetch(x("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:De})});if(!e.ok){let t=await e.json();F.textContent=t.error||"Delete failed";return}fe.style.display="none",X.style.display="flex",Y&&O(Y)}catch{F.textContent="Delete failed"}}),yn.addEventListener("click",()=>{fe.style.display="none",X.style.display="flex",Y&&O(Y)}),wn.addEventListener("click",async()=>{let e=ht.value.trim();if(!e)return;let t=Y+"/"+e;try{let n=await fetch(x("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){let s=await n.json();F.textContent=s.error||"Create failed";return}ht.value="",Y&&O(Y)}catch{F.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=vt.classList.toggle("open");Xe.classList.toggle("open",e)}),Xe.addEventListener("click",()=>{vt.classList.remove("open"),Xe.classList.remove("open")});var A="",z=!1,_e,we=document.createElement("div");we.className="session-popover-backdrop",document.body.appendChild(we);var R=document.createElement("div");R.id="session-popover",R.className="session-popover",R.style.display="none",R.innerHTML=`<div class="sp-field">
38
38
  <label>Rename session</label>
39
39
  <input type="text" id="sp-name" />
40
40
  </div>
@@ -44,5 +44,5 @@ var dn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
44
44
  </div>
45
45
  <hr class="sp-divider" />
46
46
  <button class="btn primary" id="sp-rebuild" style="display:none">\u91CD\u5EFA\u540C\u540D\u540C\u8DEF\u5F84 session</button>
47
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(N);function Pn(e){let t=e.getBoundingClientRect();N.style.top=t.bottom+4+"px",N.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Ht(e,t,n){D=e,j=!!n?.tombstone,ze=n?.agentId;let s=document.getElementById("sp-name");s.value=e;let l=N.querySelector(".sp-field label");l.textContent=j?"\u91CD\u547D\u540D (\u4EC5\u8BB0\u5F55\uFF0C\u4E0D\u91CD\u5EFA)":"Rename session";let r=document.getElementById("sp-rebuild");r.style.display=j?"":"none";let c=document.getElementById("sp-delete");c.textContent=j?"\u5220\u9664\u5931\u6548\u8BB0\u5F55 (\u5893\u7891\u6D88\u5931)":"Delete session";let o=document.getElementById("sp-save");o.textContent=j?"\u91CD\u547D\u540D":"Save",ye.classList.add("open"),N.style.display="block",Pn(t),setTimeout(()=>s.focus(),50)}function X(){ye.classList.remove("open"),N.style.display="none"}ye.addEventListener("click",X),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===D){X();return}try{let t=j?{oldName:D,newName:e,agentId:ze}:{oldName:D,newName:e};if(!(await fetch(C(j?"/api/sessions/rename-tombstone":"/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)return;X(),W(),!j&&g===D&&he(e)}catch{}}),document.getElementById("sp-rebuild").addEventListener("click",async()=>{if(confirm('\u91CD\u5EFA\u4F1A\u8BDD "'+D+'" (\u540C\u540D\u540C\u8DEF\u5F84\uFF0C\u5185\u5BB9\u4E3A\u7A7A)\uFF1F'))try{let e=await fetch(C("/api/sessions/rebuild"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:ze})});if(!e.ok){let t=await e.json();alert(t.error||"\u91CD\u5EFA\u5931\u8D25");return}X(),W()}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(j){if(!confirm('\u5220\u9664\u5931\u6548\u8BB0\u5F55 "'+D+'"\uFF1F'))return;try{if(!(await fetch(C("/api/sessions/dismiss"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:ze})})).ok)return;X(),W()}catch{}return}if(confirm('Delete session "'+D+'"?'))try{if(!(await fetch(C("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D})})).ok)return;X(),g===D&&(v&&(v.destroy(),v=null),g=null,I.style.display="none",V.style.display="flex"),W()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",X);var Rn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!v)return;let n=Rn[t.dataset.key];n&&(v.sendInput(n),v.focus())});var $=document.getElementById("mk-input"),At=document.getElementById("mk-send");function Ft(){if(!$||!v)return;let e=$.value;e&&(v.sendInput(e+"\r"),$.value="",$.rows=1,v.focus())}if($&&($.addEventListener("input",()=>{$.rows=1;let e=$.value.split(`
48
- `).length;$.rows=Math.min(e,4)}),$.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Ft())})),At&&At.addEventListener("click",Ft),window.visualViewport){let e=document.querySelector(".app-layout"),t=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",t),window.addEventListener("orientationchange",()=>setTimeout(t,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Ge=document.getElementById("process-panel"),we=document.getElementById("proc-list"),jn=document.getElementById("proc-close"),Qe=document.getElementById("proc-backdrop"),zn=document.getElementById("header-ram");function _n(){we.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(C("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){we.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let n of e){let s=n.rss,l=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+l+'</span><span class="proc-cmd" title="'+h(n.command)+'">'+h(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}we.innerHTML=t;for(let n of we.querySelectorAll(".proc-row")){let s=n.querySelector(".proc-kill");s&&s.addEventListener("click",l=>{let r=l.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(C("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?r.style.opacity="0.3":alert("Failed: "+(c.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{we.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Wn(){Ge.classList.add("open"),Qe.classList.add("open"),_n()}function _e(){Ge.classList.remove("open"),Qe.classList.remove("open")}zn.addEventListener("click",e=>{e.stopPropagation(),Ge.classList.contains("open")?_e():Wn()}),jn.addEventListener("click",_e),Qe.addEventListener("click",_e);var On=2e3,be=null,et=null,te=new Map,Le=new Map;function Nt(e,t){return(t?"a:"+t+":":"l:")+e}function Un(e,t,n){let s=Nt(e,t),l=Le.get(s),r=e===g&&(t??null)===M,c=n==="idle"&&l?.state==="working";Le.set(s,{session:e,agentId:t,state:n,doneUntil:c?Date.now()+On:void 0,unread:c?!r:!1});let o=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',u=f.querySelector(o);u&&tt(u,s,Le.get(s))}function tt(e,t,n){let s=te.get(t);s&&(clearTimeout(s),te.delete(t)),e.classList.remove("working","just-done");let l=n.session===g&&(n.agentId??null)===M;n.state==="working"?e.classList.add("working"):l&&n.doneUntil&&Date.now()<n.doneUntil?(e.classList.add("just-done"),te.set(t,setTimeout(()=>{e.classList.remove("just-done"),te.delete(t)},Math.max(0,n.doneUntil-Date.now())))):!l&&n.unread&&e.classList.add("just-done")}function nt(){for(let[e,t]of Le){let n=t.agentId?'.session-item[data-agent="'+CSS.escape(t.agentId)+'"][data-session="'+CSS.escape(t.session)+'"]':'.session-item[data-session="'+CSS.escape(t.session)+'"]:not([data-agent])',s=f.querySelector(n);s&&tt(s,e,t)}}function Yn(e,t){let n=Nt(e,t),s=Le.get(n);if(!s)return;let l=te.get(n);l&&(clearTimeout(l),te.delete(n)),s.doneUntil=void 0,s.unread=!1;let r=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',c=f.querySelector(r);c&&tt(c,n,s)}function $t(){if(be||et)return;let e=localStorage.getItem("tmux-web-token"),t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{let n=new WebSocket(t);be=n,n.onmessage=s=>{try{let l=JSON.parse(String(s.data));l&&l.type==="session.activity"&&typeof l.session=="string"&&Un(l.session,l.agentId||void 0,l.state==="working"?"working":"idle")}catch{}},n.onclose=()=>{be===n&&(be=null,et=setTimeout(()=>{et=null,$t()},5e3))},n.onerror=()=>{try{n.close()}catch{}}}catch{be=null}}window.__openSession=he,window.__refreshSidebar=W,W(),Je(),nt(),$t(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&nt()}),setInterval(()=>{document.visibilityState==="visible"&&W()},1e4);
47
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(R);function $n(e){let t=e.getBoundingClientRect();R.style.top=t.bottom+4+"px",R.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Ht(e,t,n){A=e,z=!!n?.tombstone,_e=n?.agentId;let s=document.getElementById("sp-name");s.value=e;let l=R.querySelector(".sp-field label");l.textContent=z?"\u91CD\u547D\u540D (\u4EC5\u8BB0\u5F55\uFF0C\u4E0D\u91CD\u5EFA)":"Rename session";let r=document.getElementById("sp-rebuild");r.style.display=z?"":"none";let c=document.getElementById("sp-delete");c.textContent=z?"\u5220\u9664\u5931\u6548\u8BB0\u5F55 (\u5893\u7891\u6D88\u5931)":"Delete session";let a=document.getElementById("sp-save");a.textContent=z?"\u91CD\u547D\u540D":"Save",we.classList.add("open"),R.style.display="block",$n(t),setTimeout(()=>s.focus(),50)}function Q(){we.classList.remove("open"),R.style.display="none"}we.addEventListener("click",Q),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===A){Q();return}try{let t=z?{oldName:A,newName:e,agentId:_e}:{oldName:A,newName:e};if(!(await fetch(x(z?"/api/sessions/rename-tombstone":"/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)return;Q(),W(),!z&&g===A&&ve(e)}catch{}}),document.getElementById("sp-rebuild").addEventListener("click",async()=>{if(confirm('\u91CD\u5EFA\u4F1A\u8BDD "'+A+'" (\u540C\u540D\u540C\u8DEF\u5F84\uFF0C\u5185\u5BB9\u4E3A\u7A7A)\uFF1F'))try{let e=await fetch(x("/api/sessions/rebuild"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:A,agentId:_e})});if(!e.ok){let t=await e.json();alert(t.error||"\u91CD\u5EFA\u5931\u8D25");return}Q(),W()}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(z){if(!confirm('\u5220\u9664\u5931\u6548\u8BB0\u5F55 "'+A+'"\uFF1F'))return;try{if(!(await fetch(x("/api/sessions/dismiss"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:A,agentId:_e})})).ok)return;Q(),W()}catch{}return}if(confirm('Delete session "'+A+'"?'))try{if(!(await fetch(x("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:A})})).ok)return;Q(),g===A&&(v&&(v.destroy(),v=null),g=null,D.style.display="none",X.style.display="flex"),W()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",Q);var zn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!v)return;let n=zn[t.dataset.key];n&&(v.sendInput(n),v.focus())});var N=document.getElementById("mk-input"),Ft=document.getElementById("mk-send");function Rt(){if(!N||!v)return;let e=N.value;e&&(v.sendInput(e+"\r"),N.value="",N.rows=1,v.focus())}if(N&&(N.addEventListener("input",()=>{N.rows=1;let e=N.value.split(`
48
+ `).length;N.rows=Math.min(e,4)}),N.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Rt())})),Ft&&Ft.addEventListener("click",Rt),window.visualViewport){let e=document.querySelector(".app-layout"),t=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",t),window.addEventListener("orientationchange",()=>setTimeout(t,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Ge=document.getElementById("process-panel"),be=document.getElementById("proc-list"),jn=document.getElementById("proc-close"),et=document.getElementById("proc-backdrop"),_n=document.getElementById("header-ram");function Yn(){be.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(x("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){be.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let n of e){let s=n.rss,l=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+l+'</span><span class="proc-cmd" title="'+h(n.command)+'">'+h(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}be.innerHTML=t;for(let n of be.querySelectorAll(".proc-row")){let s=n.querySelector(".proc-kill");s&&s.addEventListener("click",l=>{let r=l.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(x("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?r.style.opacity="0.3":alert("Failed: "+(c.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{be.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Wn(){Ge.classList.add("open"),et.classList.add("open"),Yn()}function Ye(){Ge.classList.remove("open"),et.classList.remove("open")}_n.addEventListener("click",e=>{e.stopPropagation(),Ge.classList.contains("open")?Ye():Wn()}),jn.addEventListener("click",Ye),et.addEventListener("click",Ye);var On=2e3,Le=null,tt=null,ne=new Map,Ee=new Map;function Nt(e,t){return(t?"a:"+t+":":"l:")+e}function Un(e,t,n){let s=Nt(e,t),l=Ee.get(s),r=e===g&&(t??null)===M,c=n==="idle"&&l?.state==="working";Ee.set(s,{session:e,agentId:t,state:n,doneUntil:c?Date.now()+On:void 0,unread:c?!r:!1});let a=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',u=f.querySelector(a);u&&nt(u,s,Ee.get(s))}function nt(e,t,n){let s=ne.get(t);s&&(clearTimeout(s),ne.delete(t)),e.classList.remove("working","just-done");let l=n.session===g&&(n.agentId??null)===M;n.state==="working"?e.classList.add("working"):l&&n.doneUntil&&Date.now()<n.doneUntil?(e.classList.add("just-done"),ne.set(t,setTimeout(()=>{e.classList.remove("just-done"),ne.delete(t)},Math.max(0,n.doneUntil-Date.now())))):!l&&n.unread&&e.classList.add("just-done")}function st(){for(let[e,t]of Ee){let n=t.agentId?'.session-item[data-agent="'+CSS.escape(t.agentId)+'"][data-session="'+CSS.escape(t.session)+'"]':'.session-item[data-session="'+CSS.escape(t.session)+'"]:not([data-agent])',s=f.querySelector(n);s&&nt(s,e,t)}}function qn(e,t){let n=Nt(e,t),s=Ee.get(n);if(!s)return;let l=ne.get(n);l&&(clearTimeout(l),ne.delete(n)),s.doneUntil=void 0,s.unread=!1;let r=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',c=f.querySelector(r);c&&nt(c,n,s)}function Pt(){if(Le||tt)return;let e=localStorage.getItem("tmux-web-token"),t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{let n=new WebSocket(t);Le=n,n.onmessage=s=>{try{let l=JSON.parse(String(s.data));l&&l.type==="session.activity"&&typeof l.session=="string"&&Un(l.session,l.agentId||void 0,l.state==="working"?"working":"idle")}catch{}},n.onclose=()=>{Le===n&&(Le=null,tt=setTimeout(()=>{tt=null,Pt()},5e3))},n.onerror=()=>{try{n.close()}catch{}}}catch{Le=null}}window.__openSession=ve,window.__refreshSidebar=W,W(),Je(),st(),Pt(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&st()}),setInterval(()=>{document.visibilityState==="visible"&&W()},1e4);
@@ -1,8 +1,8 @@
1
- const Oe=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function ne(s){return s.replace(/\x1b\[\?1049[hl]/g,"")}function R(s){return s.replace(Oe,"")}let X;function Ne(){return X||(document.querySelector("link[data-tmux-web-xterm-css]")?X=Promise.resolve():(X=new Promise(o=>{const i=document.createElement("link");i.rel="stylesheet",i.href="/assets/xterm.css",i.setAttribute("data-tmux-web-xterm-css",""),i.addEventListener("load",()=>o(),{once:!0}),i.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(i)}),X))}class oe{terminal;fitAddon;container;constructor(o,i,n){this.container=o,this.terminal=i,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(o)}static async create(o,i,n){const[{Terminal:a},{FitAddon:d}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Ne()]),m=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,smoothScrollDuration:120,theme:n});return new oe(o,m,new d)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(o){return new Promise(i=>this.terminal.write(o,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(o,i=!1){this.terminal.scrollToLine(Math.max(0,o),i)}scrollLines(o){const i=this.terminal.buffer?.active;this.terminal.scrollLines(o)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const o=Re(this.container);if(o.width<=0||o.height<=0)return!1;const n=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!n?.width||!n?.height)return this.fitAddon.fit(),!0;const a=n.width,d=n.height,m=this.terminal.element?.querySelector(".xterm-viewport"),l=m?Math.max(0,m.offsetWidth-m.clientWidth):0,f=Math.max(2,Math.floor((o.width-l)/a)),h=Math.max(2,Math.floor(o.height/d));return(f!==this.terminal.cols||h!==this.terminal.rows)&&this.terminal.resize(f,h),!0}focus(){this.terminal.focus()}pasteText(o){this.terminal.paste(o)}input(o){this.terminal.input(o,!1)}onData(o){this.terminal.onData(o),this.terminal.onBinary(o)}onResize(o){this.terminal.onResize(o)}onScroll(o){this.terminal.onScroll(o)}attachCustomKeyEventHandler(o){this.terminal.attachCustomKeyEventHandler(o)}isFocused(){const o=document.activeElement;return o?o===this.container||o===this.terminal.textarea||this.container.contains(o)||!!o.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(o,i){const n=this.terminal.buffer.active,[a,d]=o<=i?[o,i]:[i,o],m=n.viewportY,l=[];for(let f=a;f<=d;f++){const h=n.getLine(m+f);l.push(h?h.translateToString(!0):"")}return l.join(`
2
- `)}selectRows(o,i){const n=this.terminal.buffer.active,[a,d]=o<=i?[o,i]:[i,o];this.terminal.selectLines(n.viewportY+a,n.viewportY+d)}selectCells(o,i,n,a){const d=this.terminal.buffer.active,m=Math.min(i,a),l=Math.max(i,a);if(m===l){const f=Math.min(o,n),h=Math.max(o,n);this.terminal.select(f,d.viewportY+m,h-f+1)}else this.terminal.selectLines(d.viewportY+m,d.viewportY+l)}getCellsText(o,i,n,a){const d=this.terminal.buffer.active,m=Math.min(i,a),l=Math.max(i,a),f=i<a?o:i>a?n:Math.min(o,n),h=i<a?n:i>a?o:Math.max(o,n),$=d.viewportY,C=[];for(let E=m;E<=l;E++){const _=d.getLine($+E);if(!_){C.push("");continue}const D=E===m?f:0,Q=E===l?h:this.terminal.cols-1;let P="";for(let g=D;g<=Q;g++){const L=_.getCell(g);L&&(P+=L.getChars())}C.push(P.replace(/\s+$/,""))}return C.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function Re(s){const o=s.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(o.width,i.width),height:Math.max(0,Math.min(o.height,i.height-Math.max(0,o.top)))}:o}function Xe(s,o,i){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{const n=await oe.create(s,i.scrollback,i.theme);let a,d=0,m,l=null,f=0,h=0;const $=.2,C=10,E=.95,_=.04;function D(){f&&(cancelAnimationFrame(f),f=0),h=0}function Q(e){if(D(),Math.abs(e)<$)return;h=Math.max(-C,Math.min(C,e));const t=()=>{if(b){f=0;return}const r=s.querySelector(".xterm-rows > div"),c=r?r.getBoundingClientRect().height:16;let u=c>0?Math.round(h*16/c):h>0?1:-1;u===0&&(u=h>0?1:-1);const v=n.viewportY()-u,G=n.baseY(),N=Math.max(0,Math.min(v,G));if(n.scrollToLine(N,!0),h*=E,N!==v){f=0;return}if(Math.abs(h)<_){f=0;return}f=requestAnimationFrame(t)};f=requestAnimationFrame(t)}let P=0,g="connecting",L=0,Y=!1,q=[],I="",b=!1,F=1e3;const me=location.protocol==="https:"?"wss:":"ws:",he=i.wsBase||"/ws",pe=me+"//"+location.host+he+"/"+encodeURIComponent(o),we="/api/session/"+encodeURIComponent(o)+"/upload",ge=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function ye(){return q.join("")+I}async function ve(e,t=0){const r=e?n.viewportY():0,c=R(ye());if(n.reset(),!c){e||n.scrollToBottom();return}await n.write(c),e?n.scrollToLine(t+r):n.scrollToBottom()}function M(e){a?.readyState===WebSocket.OPEN&&a.send(JSON.stringify(e))}function Te(e){let t;try{t=JSON.parse(e)}catch{g==="live"&&(I+=e,n.write(R(e)));return}if(t.type==="auth.required"){const r=localStorage.getItem("tmux-web-token");r?M({type:"auth.token",token:r}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(t.type==="attach_failed"&&typeof t.message=="string"){U=!0,g="dead",n.reset(),n.write(`\r
4
- \x1B[31m`+R(t.message)+`\x1B[0m\r
5
- `).then(()=>{H();try{a?.close()}catch{}});return}if(t.type==="auth.ok"){t.token&&localStorage.setItem("tmux-web-token",t.token),F=1e3,S(),M({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(t.type==="auth.failed"){if(t.permanentLock||t.retryAfterMs&&t.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(t.message);return}return}if(t.type==="snapshot"&&typeof t.data=="string"){const r=ne(R(t.data));q=[r],I="",L=typeof t.lines=="number"?t.lines:i.terminal.initialLines,g="live",n.reset(),S(),M({type:"resize",cols:n.cols,rows:n.rows}),n.write(r).then(()=>n.scrollToBottom());return}if(t.type==="window_changed"&&typeof t.activeIndex=="number"){const r=new URL(location.href);r.searchParams.get("window")!==String(t.activeIndex)&&(r.searchParams.set("window",String(t.activeIndex)),history.replaceState(history.state,"",r)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:t.windows}));return}if(t.type==="history"&&typeof t.data=="string"){t.lines>0&&t.data?(q.unshift(ne(t.data)),L+=t.lines,ve(!0,t.lines).finally(()=>{Y=!1})):Y=!1;return}if(t.type==="data"&&typeof t.data=="string"){const r=ne(R(t.data));if(g==="connecting"){g="live",I=r,n.write(r);return}g==="live"&&(I+=r,n.write(r))}}let U=!1;function re(){if(U)return;let e=pe;const t=localStorage.getItem("tmux-web-token");t&&(e+=(e.includes("?")?"&":"?")+"token="+encodeURIComponent(t)),e+=(e.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,a=new WebSocket(e),a.onopen=()=>{g="connecting",L=0,Y=!1,q=[],I="",n.reset(),F=1e3},a.onmessage=r=>{typeof r.data=="string"&&Te(r.data)},a.onclose=()=>{b||(g="connecting",!U&&setTimeout(()=>{F=Math.min(F*2,1e4),!b&&!U&&re()},F))},a.onerror=()=>a?.close()}function j(e){M({type:"input",data:e})}function S(){return n.fit()}function H(){s.classList.remove("terminal-pending")}function y(){d&&cancelAnimationFrame(d),m&&clearTimeout(m),S(),d=requestAnimationFrame(()=>{d=0,S()&&H()}),m=setTimeout(()=>{S()&&H()},120)}function xe(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function ie(e){return typeof e=="string"&&e.startsWith("image/")}function se(e){return e?.files?.length?e.files[0]??null:null}function be(e){if(!e?.items)return null;for(let t=0;t<e.items.length;t++){const r=e.items[t];if(r?.kind==="file"&&ie(r.type))return r.getAsFile()}return null}async function Ee(e){const t=new FormData,r=e instanceof File&&e.name?e.name:"upload";t.append("file",e,r);const c=localStorage.getItem("tmux-web-token"),u={};c&&(u.Authorization="Bearer "+c);const x=await fetch(we,{method:"POST",body:t,headers:u});if(!x.ok){let G="upload failed";try{const N=await x.json();N.error&&(G=N.error)}catch{}throw new Error(G)}const v=await x.json();if(!v.path)throw new Error("no path in response");return v.path}async function Z(e){try{const t=await Ee(e);j(t)}catch{}}n.onData(e=>j(e)),n.onResize(({cols:e,rows:t})=>M({type:"resize",cols:e,rows:t})),n.onScroll(()=>{g!=="live"||Y||!n.isNearScrollbackTop()||(Y=!0,M({type:"load_history",before:L}))});let A=!1,K=!1,p=null,w=null;const B=document.createElement("div");B.className="terminal-copy-banner",B.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',B.hidden=!0,s.appendChild(B);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,s.appendChild(T);function ae(e,t){const r=s.querySelector(".xterm-screen, canvas"),u=(r&&r.getBoundingClientRect().width>0?r:s).getBoundingClientRect(),x=u.height>0&&n.rows>0?u.height/n.rows:18,v=u.width>0&&n.cols>0?u.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((t-u.top)/x))),col:Math.max(0,Math.min(n.cols-1,Math.floor((e-u.left)/v)))}}async function V(e){if(!e)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e);return}}catch{}const t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",t.style.pointerEvents="none",document.body.appendChild(t),t.focus(),t.select();try{document.execCommand("copy")}catch{}t.remove()}let z;function W(){T.hidden=!1,z&&clearTimeout(z),z=setTimeout(()=>{T.hidden=!0},1e3)}function k(e){A=e,B.hidden=!e,s.classList.toggle("terminal-copy-mode-active",e),e&&n.focus()}function le(){k(!A)}function Le(e){b||e.button!==0||K||(A||e.shiftKey)&&(e.preventDefault(),e.stopPropagation(),K=!0,p=ae(e.clientX,e.clientY),w=p,n.selectCells(p.col,p.row,w.col,w.row),document.addEventListener("mousemove",ee,!0),document.addEventListener("mouseup",te,!0))}function ee(e){b||!K||!p||(e.preventDefault(),e.stopPropagation(),w=ae(e.clientX,e.clientY),n.selectCells(p.col,p.row,w.col,w.row))}function te(e){if(b||!K||!p||!w)return;e.preventDefault(),e.stopPropagation(),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),K=!1;const t=n.getCellsText(p.col,p.row,w.col,w.row);p=null,w=null,n.clearSelection(),k(!1),t&&(T.textContent="\u5DF2\u590D\u5236",V(t).then(W)),n.focus()}s.addEventListener("mousedown",Le,!0),document.addEventListener("keydown",Me,!0);function Me(e){if(!(b||!n.isFocused()||e.defaultPrevented||e.isComposing)){if(A){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),p=null,w=null,n.clearSelection(),k(!1);return}if(e.key==="Enter"||(e.ctrlKey||e.metaKey)&&e.code==="KeyC"){e.preventDefault(),e.stopPropagation(),V(n.getScreenText()).then(W),k(!1);return}}if(e.altKey&&e.code==="KeyC"&&!e.ctrlKey&&!e.metaKey){if(e.preventDefault(),e.stopPropagation(),p&&w){const t=n.getCellsText(p.col,p.row,w.col,w.row);p=null,w=null,n.clearSelection(),k(!1),t&&(T.textContent="Copied",V(t).then(W))}else{const t=n.getSelection();t?(n.clearSelection(),T.textContent="Copied",V(t).then(W)):le()}return}if((e.ctrlKey||e.metaKey)&&e.code==="KeyV"){e.preventDefault(),e.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const t=await navigator.clipboard.read();for(const r of t)for(const c of r.types)if(ie(c)){const u=await r.getType(c);await Z(u);return}}}catch{}try{const t=await navigator.clipboard?.readText();t&&n.pasteText(ce(t))}catch{}})();return}}}let J=0;function ce(e){return e.split(`\r
1
+ const Rt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function nt(s){return s.replace(/\x1b\[\?1049[hl]/g,"")}function N(s){return s.replace(Rt,"")}let _;function Ot(){return _||(document.querySelector("link[data-tmux-web-xterm-css]")?_=Promise.resolve():(_=new Promise(o=>{const r=document.createElement("link");r.rel="stylesheet",r.href="/assets/xterm.css",r.setAttribute("data-tmux-web-xterm-css",""),r.addEventListener("load",()=>o(),{once:!0}),r.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(r)}),_))}class ot{terminal;fitAddon;container;constructor(o,r,n){this.container=o,this.terminal=r,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(o)}static async create(o,r,n){const[{Terminal:l},{FitAddon:d}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Ot()]),m=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:r,convertEol:!1,smoothScrollDuration:120,theme:n});return new ot(o,m,new d)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(o){return new Promise(r=>this.terminal.write(o,r))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(o,r=!1){this.terminal.scrollToLine(Math.max(0,o),r)}scrollLines(o){const r=this.terminal.buffer?.active;this.terminal.scrollLines(o)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const o=qt(this.container);if(o.width<=0||o.height<=0)return!1;const n=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!n?.width||!n?.height)return this.fitAddon.fit(),!0;const l=n.width,d=n.height,m=this.terminal.element?.querySelector(".xterm-viewport"),a=m?Math.max(0,m.offsetWidth-m.clientWidth):0,f=Math.max(2,Math.floor((o.width-a)/l)),h=Math.max(2,Math.floor(o.height/d));return(f!==this.terminal.cols||h!==this.terminal.rows)&&this.terminal.resize(f,h),!0}focus(){this.terminal.focus()}pasteText(o){this.terminal.paste(o)}input(o){this.terminal.input(o,!1)}onData(o){this.terminal.onData(o),this.terminal.onBinary(o)}onResize(o){this.terminal.onResize(o)}onScroll(o){this.terminal.onScroll(o)}attachCustomKeyEventHandler(o){this.terminal.attachCustomKeyEventHandler(o)}isFocused(){const o=document.activeElement;return o?o===this.container||o===this.terminal.textarea||this.container.contains(o)||!!o.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(o,r){const n=this.terminal.buffer.active,[l,d]=o<=r?[o,r]:[r,o],m=n.viewportY,a=[];for(let f=l;f<=d;f++){const h=n.getLine(m+f);a.push(h?h.translateToString(!0):"")}return a.join(`
2
+ `)}selectRows(o,r){const n=this.terminal.buffer.active,[l,d]=o<=r?[o,r]:[r,o];this.terminal.selectLines(n.viewportY+l,n.viewportY+d)}selectCells(o,r,n,l){const d=this.terminal.buffer.active,m=Math.min(r,l),a=Math.max(r,l);if(m===a){const f=Math.min(o,n),h=Math.max(o,n);this.terminal.select(f,d.viewportY+m,h-f+1)}else this.terminal.selectLines(d.viewportY+m,d.viewportY+a)}getCellsText(o,r,n,l){const d=this.terminal.buffer.active,m=Math.min(r,l),a=Math.max(r,l),f=r<l?o:r>l?n:Math.min(o,n),h=r<l?n:r>l?o:Math.max(o,n),$=d.viewportY,P=[];for(let E=m;E<=a;E++){const U=d.getLine($+E);if(!U){P.push("");continue}const I=E===m?f:0,Q=E===a?h:this.terminal.cols-1;let k="";for(let g=I;g<=Q;g++){const M=U.getCell(g);M&&(k+=M.getChars())}P.push(k.replace(/\s+$/,""))}return P.join(`
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function qt(s){const o=s.getBoundingClientRect(),r=window.visualViewport;return r?{width:Math.min(o.width,r.width),height:Math.max(0,Math.min(o.height,r.height-Math.max(0,o.top)))}:o}function Nt(s,o,r){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{const n=await ot.create(s,r.scrollback,r.theme);let l,d=0,m,a=null,f=0,h=0;const $=.2,P=10,E=.95,U=.04;function I(){f&&(cancelAnimationFrame(f),f=0),h=0}function Q(t){if(I(),Math.abs(t)<$)return;h=Math.max(-P,Math.min(P,t));const e=()=>{if(b){f=0;return}const i=s.querySelector(".xterm-rows > div"),c=i?i.getBoundingClientRect().height:16;let u=c>0?Math.round(h*16/c):h>0?1:-1;u===0&&(u=h>0?1:-1);const x=n.viewportY()-u,C=n.baseY(),D=Math.max(0,Math.min(x,C));if(n.scrollToLine(D,!0),h*=E,D!==x){f=0;return}if(Math.abs(h)<U){f=0;return}f=requestAnimationFrame(e)};f=requestAnimationFrame(e)}let k=0,g="connecting",M=0,F=!1,H=[],A="",b=!1,z=1e3;const ht=location.protocol==="https:"?"wss:":"ws:",pt=r.wsBase||"/ws",wt=ht+"//"+location.host+pt+"/"+encodeURIComponent(o),gt="/api/session/"+encodeURIComponent(o)+"/upload",yt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function vt(){return H.join("")+A}async function xt(t,e=0){const i=t?n.viewportY():0,c=N(vt());if(n.reset(),!c){t||n.scrollToBottom();return}await n.write(c),t?n.scrollToLine(e+i):n.scrollToBottom()}function L(t){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(t))}function Tt(t){let e;try{e=JSON.parse(t)}catch{g==="live"&&(A+=t,n.write(N(t)));return}if(e.type==="auth.required"){const i=localStorage.getItem("tmux-web-token");i?L({type:"auth.token",token:i}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="attach_failed"&&typeof e.message=="string"){j=!0,g="dead",n.reset(),n.write(`\r
4
+ \x1B[31m`+N(e.message)+`\x1B[0m\r
5
+ `).then(()=>{W();try{l?.close()}catch{}});return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),z=1e3,S(),L({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(e.type==="auth.failed"){if(e.permanentLock||e.retryAfterMs&&e.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(e.message);return}return}if(e.type==="snapshot"&&typeof e.data=="string"){const i=nt(N(e.data));H=[i],A="",M=typeof e.lines=="number"?e.lines:r.terminal.initialLines,g="live",n.reset(),S(),L({type:"resize",cols:n.cols,rows:n.rows}),n.write(i).then(()=>n.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){const i=new URL(location.href);i.searchParams.get("window")!==String(e.activeIndex)&&(i.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",i)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){e.lines>0&&e.data?(H.unshift(nt(e.data)),M+=e.lines,xt(!0,e.lines).finally(()=>{F=!1})):F=!1;return}if(e.type==="data"&&typeof e.data=="string"){const i=nt(N(e.data));if(g==="connecting"){g="live",A=i,n.write(i);return}g==="live"&&(A+=i,n.write(i))}}let j=!1;function it(){if(j)return;let t=wt;const e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),t+=(t.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,l=new WebSocket(t),l.onopen=()=>{g="connecting",M=0,F=!1,H=[],A="",n.reset(),z=1e3},l.onmessage=i=>{typeof i.data=="string"&&Tt(i.data)},l.onclose=()=>{b||(g="connecting",!j&&setTimeout(()=>{z=Math.min(z*2,1e4),!b&&!j&&it()},z))},l.onerror=()=>l?.close()}function B(t){L({type:"input",data:t})}function S(){return n.fit()}function W(){s.classList.remove("terminal-pending")}function y(){d&&cancelAnimationFrame(d),m&&clearTimeout(m),S(),d=requestAnimationFrame(()=>{d=0,S()&&W()}),m=setTimeout(()=>{S()&&W()},120)}function bt(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function rt(t){return typeof t=="string"&&t.startsWith("image/")}function st(t){return t?.files?.length?t.files[0]??null:null}function Et(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){const i=t.items[e];if(i?.kind==="file"&&rt(i.type))return i.getAsFile()}return null}async function Mt(t){const e=new FormData,i=t instanceof File&&t.name?t.name:"upload";e.append("file",t,i);const c=localStorage.getItem("tmux-web-token"),u={};c&&(u.Authorization="Bearer "+c);const v=await fetch(gt,{method:"POST",body:e,headers:u});if(!v.ok){let C="upload failed";try{const D=await v.json();D.error&&(C=D.error)}catch{}throw new Error(C)}const x=await v.json();if(!x.path)throw new Error("no path in response");return x.path}async function Z(t){try{const e=await Mt(t);B(e)}catch{}}n.onData(t=>B(t)),n.onResize(({cols:t,rows:e})=>L({type:"resize",cols:t,rows:e})),n.onScroll(()=>{g!=="live"||F||!n.isNearScrollbackTop()||(F=!0,L({type:"load_history",before:M}))});let K=!1,X=!1,p=null,w=null;const R=document.createElement("div");R.className="terminal-copy-banner",R.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',R.hidden=!0,s.appendChild(R);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,s.appendChild(T);function at(t,e){const i=s.querySelector(".xterm-screen, canvas"),u=(i&&i.getBoundingClientRect().width>0?i:s).getBoundingClientRect(),v=u.height>0&&n.rows>0?u.height/n.rows:18,x=u.width>0&&n.cols>0?u.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((e-u.top)/v))),col:Math.max(0,Math.min(n.cols-1,Math.floor((t-u.left)/x)))}}async function V(t){if(!t)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(t);return}}catch{}const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.opacity="0",e.style.pointerEvents="none",document.body.appendChild(e),e.focus(),e.select();try{document.execCommand("copy")}catch{}e.remove()}let O;function J(){T.hidden=!1,O&&clearTimeout(O),O=setTimeout(()=>{T.hidden=!0},1e3)}function Y(t){K=t,R.hidden=!t,s.classList.toggle("terminal-copy-mode-active",t),t&&n.focus()}function lt(){Y(!K)}function Lt(t){b||t.button!==0||X||(K||t.shiftKey)&&(t.preventDefault(),t.stopPropagation(),X=!0,p=at(t.clientX,t.clientY),w=p,n.selectCells(p.col,p.row,w.col,w.row),document.addEventListener("mousemove",tt,!0),document.addEventListener("mouseup",et,!0))}function tt(t){b||!X||!p||(t.preventDefault(),t.stopPropagation(),w=at(t.clientX,t.clientY),n.selectCells(p.col,p.row,w.col,w.row))}function et(t){if(b||!X||!p||!w)return;t.preventDefault(),t.stopPropagation(),document.removeEventListener("mousemove",tt,!0),document.removeEventListener("mouseup",et,!0),X=!1;const e=n.getCellsText(p.col,p.row,w.col,w.row);p=null,w=null,n.clearSelection(),Y(!1),e&&(T.textContent="\u5DF2\u590D\u5236",V(e).then(J)),n.focus()}s.addEventListener("mousedown",Lt,!0),document.addEventListener("keydown",St,!0);function St(t){if(!(b||!n.isFocused()||t.defaultPrevented||t.isComposing)){if(K){if(t.key==="Escape"){t.preventDefault(),t.stopPropagation(),p=null,w=null,n.clearSelection(),Y(!1);return}if(t.key==="Enter"||(t.ctrlKey||t.metaKey)&&t.code==="KeyC"){t.preventDefault(),t.stopPropagation(),V(n.getScreenText()).then(J),Y(!1);return}}if(t.altKey&&t.code==="KeyC"&&!t.ctrlKey&&!t.metaKey){if(t.preventDefault(),t.stopPropagation(),p&&w){const e=n.getCellsText(p.col,p.row,w.col,w.row);p=null,w=null,n.clearSelection(),Y(!1),e&&(T.textContent="Copied",V(e).then(J))}else{const e=n.getSelection();e?(n.clearSelection(),T.textContent="Copied",V(e).then(J)):lt()}return}if((t.ctrlKey||t.metaKey)&&t.code==="KeyV"){t.preventDefault(),t.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const e=await navigator.clipboard.read();for(const i of e)for(const c of i.types)if(rt(c)){const u=await i.getType(c);await Z(u);return}}}catch{}try{const e=await navigator.clipboard?.readText();e&&n.pasteText(ct(e))}catch{}})();return}}}let G=0;function ct(t){return t.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function Se(e){const t=be(e.clipboardData);if(t){e.preventDefault(),e.stopPropagation();const u=Date.now();if(u-J<50)return;J=u,await Z(t);return}const r=e.clipboardData?.getData("text/plain");if(!r)return;e.preventDefault(),e.stopPropagation();const c=Date.now();c-J<50||(J=c,n.pasteText(ce(r)))}let O=0;function Ce(e){e.preventDefault(),O++,se(e.dataTransfer)&&s.classList.add("terminal-drag-over")}function De(e){e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy")}function Pe(e){e.preventDefault(),O--,O<=0&&(O=0,s.classList.remove("terminal-drag-over"))}async function Ie(e){e.preventDefault(),O=0,s.classList.remove("terminal-drag-over");const t=se(e.dataTransfer);t&&await Z(t)}function ue(e){!ge||e.key!=="Escape"||A||e.defaultPrevented||e.repeat||e.metaKey||e.ctrlKey||e.altKey||n.isFocused()&&(e.preventDefault(),e.stopPropagation(),j("\x1B"))}s.addEventListener("paste",Se),s.addEventListener("dragenter",Ce),s.addEventListener("dragover",De),s.addEventListener("dragleave",Pe),s.addEventListener("drop",Ie),document.addEventListener("keydown",ue,!0);function ke(e){if(e.touches.length!==1){l=null;return}const t=e.touches[0];t&&(D(),l={startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,lastT:performance.now(),velocity:0,scrolling:!1},e.stopPropagation())}function Ye(e){if(!l||e.touches.length!==1)return;const t=e.touches[0];if(!t)return;const r=t.clientY-l.startY,c=t.clientX-l.startX;if(!l.scrolling){if(Math.abs(r)<8||Math.abs(r)<Math.abs(c))return;l.scrolling=!0,P=Date.now()+500}e.preventDefault(),e.stopPropagation();const u=performance.now(),x=t.clientY-l.lastY,v=u-l.lastT;v>0&&(l.velocity=x/v),Be(-x),l.lastX=t.clientX,l.lastY=t.clientY,l.lastT=u}function Fe(e){if(!l)return;const t=l.scrolling,r=l.velocity;if(l=null,e.stopPropagation(),!t){n.focus();return}P=Date.now()+500,e.preventDefault(),r&&Q(r)}function Ae(e){l=null,D(),e.stopPropagation()}function Ke(e){e.pointerType==="touch"&&Date.now()<P&&(e.preventDefault(),e.stopPropagation())}function Be(e){const t=s.querySelector(".xterm-rows > div"),r=t?t.getBoundingClientRect().height:16;let c=r>0?Math.round(e/r):e>0?1:-1;c===0&&(c=e>0?1:-1),n.scrollLines(c)}s.addEventListener("touchstart",ke,{passive:!0,capture:!0}),s.addEventListener("touchmove",Ye,{passive:!1,capture:!0}),s.addEventListener("touchend",Fe,{passive:!1,capture:!0}),s.addEventListener("touchcancel",Ae,{passive:!0,capture:!0}),s.addEventListener("pointerup",Ke,!0);function fe(){y()}window.addEventListener("resize",fe),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const de=new ResizeObserver(()=>y());de.observe(s),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),s.addEventListener("focusin",xe,!0);async function ze(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(e=>setTimeout(e,1e3))])}catch{}}return requestAnimationFrame(()=>{S()&&H(),(async()=>(await ze(),S()&&M({type:"resize",cols:n.cols,rows:n.rows}),re(),y()))()}),{destroy(){if(b=!0,D(),k(!1),z&&clearTimeout(z),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),a){try{a.close(1e3,"session switch")}catch{}a=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",fe),de.disconnect(),document.removeEventListener("keydown",ue,!0)},focus(){n.focus()},sendInput(e){j(e)},toggleCopyMode(){le()}}})()}export{Xe as initTerminal,ne as stripAltScreenSequences};
8
+ `).join("\r")}async function Ct(t){const e=Et(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();const u=Date.now();if(u-G<50)return;G=u,await Z(e);return}const i=t.clipboardData?.getData("text/plain");if(!i)return;t.preventDefault(),t.stopPropagation();const c=Date.now();c-G<50||(G=c,n.pasteText(ct(i)))}let q=0;function Dt(t){t.preventDefault(),q++,st(t.dataTransfer)&&s.classList.add("terminal-drag-over")}function Pt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function It(t){t.preventDefault(),q--,q<=0&&(q=0,s.classList.remove("terminal-drag-over"))}async function kt(t){t.preventDefault(),q=0,s.classList.remove("terminal-drag-over");const e=st(t.dataTransfer);e&&await Z(e)}function ut(t){!yt||t.key!=="Escape"||K||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||n.isFocused()&&(t.preventDefault(),t.stopPropagation(),B("\x1B"))}s.addEventListener("paste",Ct),s.addEventListener("dragenter",Dt),s.addEventListener("dragover",Pt),s.addEventListener("dragleave",It),s.addEventListener("drop",kt),document.addEventListener("keydown",ut,!0);function At(t){if(t.touches.length!==1){a=null;return}const e=t.touches[0];if(!e)return;I();const i=e.clientX-s.getBoundingClientRect().left<s.clientWidth*.4?"tui":"history";a={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:i,tuiAccum:0},t.stopPropagation()}function Yt(t){if(!a||t.touches.length!==1)return;const e=t.touches[0];if(!e)return;const i=e.clientY-a.startY,c=e.clientX-a.startX;if(!a.scrolling){if(Math.abs(i)<8||Math.abs(i)<Math.abs(c))return;a.scrolling=!0,k=Date.now()+500}t.preventDefault(),t.stopPropagation();const u=performance.now(),v=e.clientY-a.lastY;if(a.zone==="tui"){a.tuiAccum+=v;const C=s.querySelector(".xterm-rows > div"),D=C?C.getBoundingClientRect().height:16,mt=Math.max(24,D*1.2);for(;Math.abs(a.tuiAccum)>=mt;)B(a.tuiAccum>0?"\x1B[6~":"\x1B[5~"),a.tuiAccum-=Math.sign(a.tuiAccum)*mt;a.lastX=e.clientX,a.lastY=e.clientY,a.lastT=u;return}const x=u-a.lastT;x>0&&(a.velocity=v/x),Kt(-v),a.lastX=e.clientX,a.lastY=e.clientY,a.lastT=u}function Ft(t){if(!a)return;const e=a.scrolling,i=a.zone,c=a.velocity;if(a=null,t.stopPropagation(),!e){n.focus();return}k=Date.now()+500,t.preventDefault(),i!=="tui"&&c&&Q(c)}function zt(t){a=null,I(),t.stopPropagation()}function Bt(t){t.pointerType==="touch"&&Date.now()<k&&(t.preventDefault(),t.stopPropagation())}function Kt(t){const e=s.querySelector(".xterm-rows > div"),i=e?e.getBoundingClientRect().height:16;let c=i>0?Math.round(t/i):t>0?1:-1;c===0&&(c=t>0?1:-1),n.scrollLines(c)}s.addEventListener("touchstart",At,{passive:!0,capture:!0}),s.addEventListener("touchmove",Yt,{passive:!1,capture:!0}),s.addEventListener("touchend",Ft,{passive:!1,capture:!0}),s.addEventListener("touchcancel",zt,{passive:!0,capture:!0}),s.addEventListener("pointerup",Bt,!0);function ft(){y()}window.addEventListener("resize",ft),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const dt=new ResizeObserver(()=>y());dt.observe(s),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),s.addEventListener("focusin",bt,!0);async function Xt(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(t=>setTimeout(t,1e3))])}catch{}}return requestAnimationFrame(()=>{S()&&W(),(async()=>(await Xt(),S()&&L({type:"resize",cols:n.cols,rows:n.rows}),it(),y()))()}),{destroy(){if(b=!0,I(),Y(!1),O&&clearTimeout(O),document.removeEventListener("mousemove",tt,!0),document.removeEventListener("mouseup",et,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",ft),dt.disconnect(),document.removeEventListener("keydown",ut,!0)},focus(){n.focus()},sendInput(t){B(t)},toggleCopyMode(){lt()}}})()}export{Nt as initTerminal,nt as stripAltScreenSequences};
@@ -1 +1 @@
1
- const i=1,f=2;function u(t,e,n){const s=typeof n=="string"?Buffer.from(n,"utf-8"):Buffer.isBuffer(n)?n:Buffer.from(n),r=Buffer.allocUnsafe(5+s.length);return r[0]=t,r.writeUInt32BE(e>>>0,1),s.copy(r,5),r}function c(t){return t.length<5?null:{kind:t[0],id:t.readUInt32BE(1),payload:t.subarray(5)}}function l(t){if(typeof t!="object"||t===null)return!1;const e=t.type;return typeof e=="string"&&o.has(e)}function a(t){if(typeof t!="object"||t===null)return!1;const e=t.type;return typeof e=="string"&&_.has(e)}const o=new Set(["hello","sessions","http_resp","http_body_end","attach_ok","attach_err","ws_to_hub","ws_close","activity","rebuild_session_result","ping"]),_=new Set(["hello_ok","hello_err","sessions_req","http_req","http_body_end","attach","ws_to_agent","rebuild_session","detach","pong"]);export{f as BIN_KIND_HTTP_BODY,i as BIN_KIND_TERM,c as decodeBinaryFrame,u as encodeBinaryFrame,l as isAgentToHubMessage,a as isHubToAgentMessage};
1
+ const i=1,f=2;function u(t,e,n){const s=typeof n=="string"?Buffer.from(n,"utf-8"):Buffer.isBuffer(n)?n:Buffer.from(n),r=Buffer.allocUnsafe(5+s.length);return r[0]=t,r.writeUInt32BE(e>>>0,1),s.copy(r,5),r}function c(t){return t.length<5?null:{kind:t[0],id:t.readUInt32BE(1),payload:t.subarray(5)}}function l(t){if(typeof t!="object"||t===null)return!1;const e=t.type;return typeof e=="string"&&o.has(e)}function p(t){if(typeof t!="object"||t===null)return!1;const e=t.type;return typeof e=="string"&&_.has(e)}const o=new Set(["hello","sessions","http_resp","http_body_end","attach_ok","attach_err","ws_to_hub","ws_close","activity","rebuild_session_result","ping"]),_=new Set(["hello_ok","hello_err","sessions_req","http_req","http_body_end","attach","ws_to_agent","rebuild_session","ping","detach","pong"]);export{f as BIN_KIND_HTTP_BODY,i as BIN_KIND_TERM,c as decodeBinaryFrame,u as encodeBinaryFrame,l as isAgentToHubMessage,p as isHubToAgentMessage};
@@ -1 +1 @@
1
- import{WebSocket as w}from"ws";import{attachTerminal as F}from"./attach-terminal.js";import{encodeBinaryFrame as G,decodeBinaryFrame as X,BIN_KIND_HTTP_BODY as O}from"./agent-channel.js";import{ActivityProbe as Y}from"./activity-probe.js";import{sessionWorkingPath as z}from"./sessions-sidebar.js";import{newTmuxSession as Q}from"./tmux-windows.js";const V=parseInt(process.env.TMUX_WEB_AGENT_HEARTBEAT_MS||"15000",10),Z=parseInt(process.env.TMUX_WEB_AGENT_SESSIONS_POLL_MS||"10000",10),ee=new Set(["content-type","content-disposition","location","cache-control","etag"]);function ce(o){const B=o.log??(e=>console.log(`[agent] ${e}`)),h=e=>console.error(`[agent] ${e}`);let s=null,I=1e3,v=null,T=!1;const u=new Map,y=new Map,p=new Map,E=new Set;function H(e){return E.has(e)?Promise.resolve():new Promise(t=>{p.set(e,t),setTimeout(()=>{p.delete(e)&&t()},3e4)})}let _=null,m=null,S=null,g=null,M=new Map,C="",P=null;const b={state:"idle",agentId:null,hub:o.hub,name:o.name};function f(e,t){t!==void 0&&(b.lastError=t),b.state=e,b.agentId=P,o.onStatus?.({...b})}function c(e){s&&s.readyState===w.OPEN&&s.send(JSON.stringify(e))}function k(){let e;try{e=o.listSessions()}catch{return}const t=e.map(r=>({...r,path:z(r.name)})),n=JSON.stringify(t);n!==C&&(C=n,c({type:"sessions",sessions:t}))}function R(){_||(_=setInterval(k,Z)),m||(m=setInterval(()=>c({type:"ping",ts:Date.now()}),V)),S||(g=new Y,S=setInterval(()=>void q(),g.scanMs))}function L(){_&&(clearInterval(_),_=null),m&&(clearInterval(m),m=null),S&&(clearInterval(S),S=null),g=null}async function q(){if(!(!s||s.readyState!==w.OPEN||!g))try{const e=await g.scan(),t=[];for(const[n,r]of e)M.get(n)!==r&&t.push({session:n,state:r});M=e,t.length&&c({type:"activity",activities:t})}catch{}}async function U(e){let t;if(e.hasBody){await H(e.id),E.delete(e.id);const a=y.get(e.id);y.delete(e.id),t=a&&a.length?Buffer.concat(a):Buffer.alloc(0)}const n=new URL(e.path,"http://agent.invalid");let r;try{const a={method:e.method,headers:e.headers};t&&t.length&&(a.body=t),r=await Promise.race([o.app.request(new Request(n,a)),new Promise((te,K)=>setTimeout(()=>K(new Error("app.request timed out")),3e4))])}catch(a){h(`tunnel request failed: ${e.method} ${e.path} ${a.message}`),c({type:"http_resp",id:e.id,status:500,headers:{"content-type":"application/json"},hasBody:!1});return}const d={};r.headers.forEach((a,l)=>{ee.has(l.toLowerCase())&&(d[l]=a)});const i=Buffer.from(await r.arrayBuffer());if(c({type:"http_resp",id:e.id,status:r.status,headers:d,hasBody:i.length>0}),i.length>0){for(let l=0;l<i.length;l+=65536){if(!s||s.readyState!==w.OPEN)return;s.send(G(O,e.id,i.subarray(l,l+65536)))}c({type:"http_body_end",id:e.id})}}function x(e,t){let n=!1,r="";try{Q(e,t),n=!0}catch(d){r=d instanceof Error?d.message:"rebuild failed"}s&&s.readyState===w.OPEN&&s.send(JSON.stringify({type:"rebuild_session_result",session:e,ok:n,message:r})),k()}function J(e){const t=e.session;let n=!1;try{n=o.listSessions().some(i=>i.name===t)}catch{n=!1}if(!n){c({type:"attach_err",connId:e.connId,session:t,message:"no such session"});return}let r=null;const d=F(t,{send:i=>c({type:"ws_to_hub",connId:e.connId,msg:i}),onMessage:i=>{r=i},onClose:()=>{}},{terminalBufferConfig:o.terminalBufferConfig,getSessionPaneTarget:o.getSessionPaneTarget,capturePaneTail:o.capturePaneTail,capturePaneHistoryChunk:o.capturePaneHistoryChunk,toCrlf:o.toCrlf,handleClientMessage:o.handleClientMessage,acquireControlClient:o.acquireControlClient,initialCols:e.cols,initialRows:e.rows,tmuxSocketPath:o.tmuxSocketPath,onPtyExit:(i,a)=>{c({type:"ws_close",connId:e.connId,code:1e3,reason:`tmux exited (${a})`})},onSpawnError:(i,a)=>{h(`attach failed for session "${e.session}": ${a}`),c({type:"attach_err",connId:e.connId,session:t,message:a})},onAttached:i=>{c({type:"attach_ok",connId:e.connId,session:t})}});u.set(e.connId,{onMessage:i=>r?.(i),dispose:()=>{u.delete(e.connId),d.dispose()}})}function D(e){let t;try{t=JSON.parse(e)}catch{return}switch(t.type){case"hello_ok":P=t.agentId,I=1e3,B(`connected to hub as "${P}"`),f("connected"),k(),R();break;case"hello_err":if(h(`hub rejected us: ${t.reason}`),f("error",t.reason),s)try{s.close(4003,"hello rejected")}catch{}break;case"sessions_req":k();break;case"http_req":U(t);break;case"http_body_end":{E.add(t.id);const n=p.get(t.id);n&&(p.delete(t.id),n());break}case"attach":J(t);break;case"rebuild_session":x(t.session,t.dir);break;case"ws_to_agent":{const n=u.get(t.connId);if(n)try{n.onMessage(JSON.stringify(t.msg))}catch{}break}case"detach":{const n=u.get(t.connId);n&&n.dispose();break}case"pong":break;default:break}}function W(e){const t=X(e);if(t&&t.kind===O){const n=y.get(t.id)??[];n.push(t.payload),y.set(t.id,n)}}function N(){L();for(const e of u.values())try{e.dispose()}catch{}u.clear(),y.clear(),p.clear(),E.clear(),C=""}function A(){if(s){try{s.removeAllListeners()}catch{}s=null}if(N(),T)return;f("connecting");const e=I+Math.random()*500;B(`reconnecting in ${Math.round(e)}ms\u2026`),v=setTimeout($,e),I=Math.min(I*2,6e4)}function $(){if(T||s)return;const e=o.hub.replace(/\/+$/,"").replace(/^http:/,"ws:").replace(/^https:/,"wss:")+"/agent/ws";let t;try{t=new w(e)}catch(n){h(`bad hub URL: ${n.message} (${e})`),f("error",`bad hub URL: ${n.message}`);return}s=t,f("connecting"),t.on("open",()=>{B(`connecting to ${e}\u2026`),c({type:"hello",token:o.token,name:o.name,version:o.version})}),t.on("message",(n,r)=>{r?W(Array.isArray(n)?Buffer.concat(n):Buffer.isBuffer(n)?n:Buffer.from(n)):D(typeof n=="string"?n:Buffer.from(n).toString("utf-8"))}),t.on("close",()=>{s===t&&A()}),t.on("error",n=>{h(`connection error: ${n.message}`),s===t&&A()})}function j(){if(!T){if(T=!0,v&&clearTimeout(v),N(),s){try{s.removeAllListeners(),s.close(1e3,"shutdown")}catch{}s=null}P=null,f("idle")}}return $(),{stop:j,status:()=>({...b})}}export{ce as startAgentClient};
1
+ import{WebSocket as h}from"ws";import{attachTerminal as X}from"./attach-terminal.js";import{encodeBinaryFrame as Y,decodeBinaryFrame as z,BIN_KIND_HTTP_BODY as R}from"./agent-channel.js";import{ActivityProbe as Q}from"./activity-probe.js";import{sessionWorkingPath as V}from"./sessions-sidebar.js";import{newTmuxSession as Z}from"./tmux-windows.js";const ee=parseInt(process.env.TMUX_WEB_AGENT_HEARTBEAT_MS||"15000",10),te=parseInt(process.env.TMUX_WEB_AGENT_SESSIONS_POLL_MS||"10000",10),ne=parseInt(process.env.TMUX_WEB_AGENT_PONG_TIMEOUT_MS||"45000",10),H=5,se=6e4,oe=new Set(["content-type","content-disposition","location","cache-control","etag"]);function fe(o){const E=o.log??(t=>console.log(`[agent] ${t}`)),p=t=>console.error(`[agent] ${t}`);let s=null,I=0,y=0,B=null,P=!1;const d=new Map,_=new Map,S=new Map,M=new Set;function U(t){return M.has(t)?Promise.resolve():new Promise(e=>{S.set(t,e),setTimeout(()=>{S.delete(t)&&e()},3e4)})}let m=null,g=null,b=null,w=null,v=new Map,C="",N=null;const T={state:"idle",agentId:null,hub:o.hub,name:o.name};function f(t,e){e!==void 0&&(T.lastError=e),T.state=t,T.agentId=N,o.onStatus?.({...T})}function c(t){s&&s.readyState===h.OPEN&&s.send(JSON.stringify(t))}function k(){let t;try{t=o.listSessions()}catch{return}const e=t.map(r=>({...r,path:V(r.name)})),n=JSON.stringify(e);n!==C&&(C=n,c({type:"sessions",sessions:e}))}function L(){m||(m=setInterval(k,te)),g||(g=setInterval(()=>{if(s&&s.readyState===h.OPEN&&y>0&&Date.now()-y>ne){E(`hub heartbeat timeout (${Date.now()-y}ms without pong), reconnecting`);try{s.close(1001,"hub heartbeat timeout")}catch{}return}c({type:"ping",ts:Date.now()})},ee)),b||(w=new Q,b=setInterval(()=>void D(),w.scanMs))}function q(){m&&(clearInterval(m),m=null),g&&(clearInterval(g),g=null),b&&(clearInterval(b),b=null),w=null}async function D(){if(!(!s||s.readyState!==h.OPEN||!w))try{const t=await w.scan(),e=[];for(const[n,r]of t)v.get(n)!==r&&e.push({session:n,state:r});v=t,e.length&&c({type:"activity",activities:e})}catch{}}async function x(t){let e;if(t.hasBody){await U(t.id),M.delete(t.id);const a=_.get(t.id);_.delete(t.id),e=a&&a.length?Buffer.concat(a):Buffer.alloc(0)}const n=new URL(t.path,"http://agent.invalid");let r;try{const a={method:t.method,headers:t.headers};e&&e.length&&(a.body=e),r=await Promise.race([o.app.request(new Request(n,a)),new Promise((re,K)=>setTimeout(()=>K(new Error("app.request timed out")),3e4))])}catch(a){p(`tunnel request failed: ${t.method} ${t.path} ${a.message}`),c({type:"http_resp",id:t.id,status:500,headers:{"content-type":"application/json"},hasBody:!1});return}const u={};r.headers.forEach((a,l)=>{oe.has(l.toLowerCase())&&(u[l]=a)});const i=Buffer.from(await r.arrayBuffer());if(c({type:"http_resp",id:t.id,status:r.status,headers:u,hasBody:i.length>0}),i.length>0){for(let l=0;l<i.length;l+=65536){if(!s||s.readyState!==h.OPEN)return;s.send(Y(R,t.id,i.subarray(l,l+65536)))}c({type:"http_body_end",id:t.id})}}function G(t,e){let n=!1,r="";try{Z(t,e),n=!0}catch(u){r=u instanceof Error?u.message:"rebuild failed"}s&&s.readyState===h.OPEN&&s.send(JSON.stringify({type:"rebuild_session_result",session:t,ok:n,message:r})),k()}function J(t){const e=t.session;let n=!1;try{n=o.listSessions().some(i=>i.name===e)}catch{n=!1}if(!n){c({type:"attach_err",connId:t.connId,session:e,message:"no such session"});return}let r=null;const u=X(e,{send:i=>c({type:"ws_to_hub",connId:t.connId,msg:i}),onMessage:i=>{r=i},onClose:()=>{}},{terminalBufferConfig:o.terminalBufferConfig,getSessionPaneTarget:o.getSessionPaneTarget,capturePaneTail:o.capturePaneTail,capturePaneHistoryChunk:o.capturePaneHistoryChunk,toCrlf:o.toCrlf,handleClientMessage:o.handleClientMessage,acquireControlClient:o.acquireControlClient,initialCols:t.cols,initialRows:t.rows,tmuxSocketPath:o.tmuxSocketPath,onPtyExit:(i,a)=>{c({type:"ws_close",connId:t.connId,code:1e3,reason:`tmux exited (${a})`})},onSpawnError:(i,a)=>{p(`attach failed for session "${t.session}": ${a}`),c({type:"attach_err",connId:t.connId,session:e,message:a})},onAttached:i=>{c({type:"attach_ok",connId:t.connId,session:e})}});d.set(t.connId,{onMessage:i=>r?.(i),dispose:()=>{d.delete(t.connId),u.dispose()}})}function W(t){let e;try{e=JSON.parse(t)}catch{return}switch(e.type){case"hello_ok":N=e.agentId,I=0,y=Date.now(),E(`connected to hub as "${N}"`),f("connected"),k(),L();break;case"hello_err":if(p(`hub rejected us: ${e.reason}`),f("error",e.reason),s)try{s.close(4003,"hello rejected")}catch{}break;case"sessions_req":k();break;case"http_req":x(e);break;case"http_body_end":{M.add(e.id);const n=S.get(e.id);n&&(S.delete(e.id),n());break}case"attach":J(e);break;case"rebuild_session":G(e.session,e.dir);break;case"ws_to_agent":{const n=d.get(e.connId);if(n)try{n.onMessage(JSON.stringify(e.msg))}catch{}break}case"detach":{const n=d.get(e.connId);n&&n.dispose();break}case"pong":y=Date.now();break;case"ping":c({type:"pong",ts:e.ts});break;default:break}}function j(t){const e=z(t);if(e&&e.kind===R){const n=_.get(e.id)??[];n.push(e.payload),_.set(e.id,n)}}function A(){q();for(const t of d.values())try{t.dispose()}catch{}d.clear(),_.clear(),S.clear(),M.clear(),C=""}function O(){if(s){try{s.removeAllListeners()}catch{}s=null}if(A(),P)return;f("connecting"),I+=1;const e=(I<=H?1e3:Math.min(1e3*Math.pow(2,I-H),se))+Math.random()*500;E(`reconnecting in ${Math.round(e)}ms\u2026`),B=setTimeout($,e)}function $(){if(P||s)return;const t=o.hub.replace(/\/+$/,"").replace(/^http:/,"ws:").replace(/^https:/,"wss:")+"/agent/ws";let e;try{e=new h(t)}catch(n){p(`bad hub URL: ${n.message} (${t})`),f("error",`bad hub URL: ${n.message}`);return}s=e,f("connecting"),e.on("open",()=>{E(`connecting to ${t}\u2026`),c({type:"hello",token:o.token,name:o.name,version:o.version})}),e.on("message",(n,r)=>{r?j(Array.isArray(n)?Buffer.concat(n):Buffer.isBuffer(n)?n:Buffer.from(n)):W(typeof n=="string"?n:Buffer.from(n).toString("utf-8"))}),e.on("close",()=>{s===e&&O()}),e.on("error",n=>{p(`connection error: ${n.message}`),s===e&&O()})}function F(){if(!P){if(P=!0,B&&clearTimeout(B),A(),s){try{s.removeAllListeners(),s.close(1e3,"shutdown")}catch{}s=null}N=null,f("idle")}}return $(),{stop:F,status:()=>({...T})}}export{fe as startAgentClient};
@@ -1,3 +1,3 @@
1
- import{WebSocket as r}from"ws";import{decodeBinaryFrame as u,encodeBinaryFrame as f,BIN_KIND_TERM as g,BIN_KIND_HTTP_BODY as h}from"./agent-channel.js";const S=parseInt(process.env.TMUX_WEB_MAX_AGENT_CONNS||"64",10),_=parseInt(process.env.TMUX_WEB_TUNNEL_TIMEOUT_MS||"30000",10);let E=1,w=1;class k{ws;agentId;pending=new Map;relays=new Map;onSessions;onActivity;onRebuildResult;onClose;disposed=!1;constructor(t,e,s){this.ws=t,this.agentId=e,this.onSessions=s.onSessions,this.onActivity=s.onActivity,this.onRebuildResult=s.onRebuildResult,this.onClose=s.onClose}send(t){return this.disposed||this.ws.readyState!==r.OPEN?!1:(this.ws.send(JSON.stringify(t)),!0)}attach(t,e,s){return this.send({type:"attach",connId:t,session:e,cols:s?.cols,rows:s?.rows})}detach(t){this.send({type:"detach",connId:t})}sendToAgent(t,e){this.send({type:"ws_to_agent",connId:t,msg:e})}sendSessionsReq(){this.send({type:"sessions_req"})}rebuildSession(t,e){return this.send({type:"rebuild_session",session:t,dir:e})}async httpRequest(t,e,s,n){const i=w++,o=!!n,c=new Promise((d,a)=>{const l=setTimeout(()=>{this.pending.delete(i),a(new Error("tunnel request timed out"))},_);this.pending.set(i,{id:i,resolve:d,reject:a,status:null,headers:null,body:[],hasBody:o,timer:l})});if(!this.send({type:"http_req",id:i,method:t,path:e,headers:s,hasBody:o}))return this.pending.delete(i),Promise.reject(new Error("agent offline"));if(n){const d=n.getReader();(async()=>{for(;;){const{done:l,value:p}=await d.read();if(l)break;const y=Buffer.from(p);this.ws.send(f(h,i,y))}this.send({type:"http_body_end",id:i})})().catch(()=>{this.send({type:"http_body_end",id:i})})}return c}registerRelay(t,e,s){this.relays.set(t,{connId:t,session:e,ws:s,closed:!1})}relayClosed(t){const e=this.relays.get(t);e&&!e.closed&&(e.closed=!0,this.relays.delete(t),this.detach(t))}get relayCount(){return this.relays.size}handleData(t){const e=Buffer.isBuffer(t)?t:Buffer.from(t),s=u(e);if(s){if(s.kind===g){const n=this.relays.get(s.id);n&&!n.closed&&n.ws.readyState===r.OPEN&&n.ws.send(JSON.stringify({type:"data",data:s.payload.toString("utf-8")}));return}if(s.kind===h){const n=this.pending.get(s.id);n&&n.body.push(s.payload)}}}handleMessage(t){let e;try{e=JSON.parse(t)}catch{return}switch(e.type){case"sessions":this.onSessions?.(e.sessions);break;case"activity":this.onActivity?.(e.activities);break;case"rebuild_session_result":this.onRebuildResult?.({session:e.session,ok:e.ok,message:e.message});break;case"http_resp":{const s=this.pending.get(e.id);if(!s)break;s.status=e.status,s.headers=e.headers,e.hasBody||this.finalizeHttp(s.id);break}case"http_body_end":this.finalizeHttp(e.id);break;case"attach_ok":{const s=this.relays.get(e.connId);s&&!s.closed&&s.ws.readyState===r.OPEN&&s.ws.send(JSON.stringify({type:"auth.ok",setupMode:!1}));break}case"attach_err":{const s=this.relays.get(e.connId);s&&!s.closed&&s.ws.readyState===r.OPEN&&s.ws.send(JSON.stringify({type:"attach_failed",message:`Agent "${this.agentId}": cannot attach to session "${e.session}": ${e.message}`}));break}case"ws_to_hub":{const s=this.relays.get(e.connId);s&&!s.closed&&s.ws.readyState===r.OPEN&&s.ws.send(JSON.stringify(e.msg));break}case"ws_close":{const s=this.relays.get(e.connId);if(s&&!s.closed){s.closed=!0;try{s.ws.close(e.code??1e3,e.reason??"agent closed")}catch{}this.relays.delete(e.connId)}break}case"ping":this.send({type:"pong",ts:e.ts});break;default:break}}finalizeHttp(t){const e=this.pending.get(t);if(!e)return;this.pending.delete(t),clearTimeout(e.timer);const s=e.status??500,n=e.headers??{},i=Buffer.concat(e.body),o={};for(const[c,d]of Object.entries(n)){const a=c.toLowerCase();(a==="content-type"||a==="content-disposition"||a==="cache-control"||a==="etag"||a==="location")&&(o[c]=d)}e.resolve(new Response(i,{status:s,headers:o}))}failAll(t){for(const e of this.pending.values())clearTimeout(e.timer),e.reject(new Error(t));this.pending.clear();for(const e of this.relays.values()){if(!e.closed&&e.ws.readyState===r.OPEN){e.ws.send(JSON.stringify({type:"data",data:`\r
2
- \x1B[31m--- agent disconnected (${t}) ---\x1B[0m\r
3
- `}));try{e.ws.close(1011,"agent disconnected")}catch{}}e.closed=!0}this.relays.clear()}dispose(){this.disposed||(this.disposed=!0,this.failAll("agent disconnected"),this.onClose?.(this))}}export{k as AgentChannel,S as MAX_RELAY_CONNS_PER_AGENT,_ as TUNNEL_REQUEST_TIMEOUT_MS};
1
+ import{WebSocket as r}from"ws";import{decodeBinaryFrame as u,encodeBinaryFrame as f,BIN_KIND_TERM as g,BIN_KIND_HTTP_BODY as h}from"./agent-channel.js";const _=parseInt(process.env.TMUX_WEB_HUB_HEARTBEAT_MS||"15000",10),w=parseInt(process.env.TMUX_WEB_HUB_PONG_TIMEOUT_MS||"90000",10),N=parseInt(process.env.TMUX_WEB_MAX_AGENT_CONNS||"64",10),b=parseInt(process.env.TMUX_WEB_TUNNEL_TIMEOUT_MS||"30000",10);let O=1,m=1;class B{ws;agentId;pending=new Map;relays=new Map;onSessions;onActivity;onRebuildResult;onClose;lastPongAt=Date.now();heartbeatTimer=null;disposed=!1;constructor(s,e,t){this.ws=s,this.agentId=e,this.onSessions=t.onSessions,this.onActivity=t.onActivity,this.onRebuildResult=t.onRebuildResult,this.onClose=t.onClose,this.heartbeatTimer=setInterval(()=>{if(!(this.disposed||this.ws.readyState!==r.OPEN)){if(Date.now()-this.lastPongAt>w){try{this.ws.close(1001,"agent heartbeat timeout")}catch{}return}this.send({type:"ping",ts:Date.now()})}},_)}send(s){return this.disposed||this.ws.readyState!==r.OPEN?!1:(this.ws.send(JSON.stringify(s)),!0)}attach(s,e,t){return this.send({type:"attach",connId:s,session:e,cols:t?.cols,rows:t?.rows})}detach(s){this.send({type:"detach",connId:s})}sendToAgent(s,e){this.send({type:"ws_to_agent",connId:s,msg:e})}sendSessionsReq(){this.send({type:"sessions_req"})}rebuildSession(s,e){return this.send({type:"rebuild_session",session:s,dir:e})}async httpRequest(s,e,t,n){const i=m++,o=!!n,c=new Promise((d,a)=>{const l=setTimeout(()=>{this.pending.delete(i),a(new Error("tunnel request timed out"))},b);this.pending.set(i,{id:i,resolve:d,reject:a,status:null,headers:null,body:[],hasBody:o,timer:l})});if(!this.send({type:"http_req",id:i,method:s,path:e,headers:t,hasBody:o}))return this.pending.delete(i),Promise.reject(new Error("agent offline"));if(n){const d=n.getReader();(async()=>{for(;;){const{done:l,value:p}=await d.read();if(l)break;const y=Buffer.from(p);this.ws.send(f(h,i,y))}this.send({type:"http_body_end",id:i})})().catch(()=>{this.send({type:"http_body_end",id:i})})}return c}registerRelay(s,e,t){this.relays.set(s,{connId:s,session:e,ws:t,closed:!1})}relayClosed(s){const e=this.relays.get(s);e&&!e.closed&&(e.closed=!0,this.relays.delete(s),this.detach(s))}get relayCount(){return this.relays.size}handleData(s){const e=Buffer.isBuffer(s)?s:Buffer.from(s),t=u(e);if(t){if(t.kind===g){const n=this.relays.get(t.id);n&&!n.closed&&n.ws.readyState===r.OPEN&&n.ws.send(JSON.stringify({type:"data",data:t.payload.toString("utf-8")}));return}if(t.kind===h){const n=this.pending.get(t.id);n&&n.body.push(t.payload)}}}handleMessage(s){let e;try{e=JSON.parse(s)}catch{return}switch(e.type){case"sessions":this.onSessions?.(e.sessions);break;case"activity":this.onActivity?.(e.activities);break;case"rebuild_session_result":this.onRebuildResult?.({session:e.session,ok:e.ok,message:e.message});break;case"http_resp":{const t=this.pending.get(e.id);if(!t)break;t.status=e.status,t.headers=e.headers,e.hasBody||this.finalizeHttp(t.id);break}case"http_body_end":this.finalizeHttp(e.id);break;case"attach_ok":{const t=this.relays.get(e.connId);t&&!t.closed&&t.ws.readyState===r.OPEN&&t.ws.send(JSON.stringify({type:"auth.ok",setupMode:!1}));break}case"attach_err":{const t=this.relays.get(e.connId);t&&!t.closed&&t.ws.readyState===r.OPEN&&t.ws.send(JSON.stringify({type:"attach_failed",message:`Agent "${this.agentId}": cannot attach to session "${e.session}": ${e.message}`}));break}case"ws_to_hub":{const t=this.relays.get(e.connId);t&&!t.closed&&t.ws.readyState===r.OPEN&&t.ws.send(JSON.stringify(e.msg));break}case"ws_close":{const t=this.relays.get(e.connId);if(t&&!t.closed){t.closed=!0;try{t.ws.close(e.code??1e3,e.reason??"agent closed")}catch{}this.relays.delete(e.connId)}break}case"ping":this.lastPongAt=Date.now(),this.send({type:"pong",ts:e.ts});break;default:break}}finalizeHttp(s){const e=this.pending.get(s);if(!e)return;this.pending.delete(s),clearTimeout(e.timer);const t=e.status??500,n=e.headers??{},i=Buffer.concat(e.body),o={};for(const[c,d]of Object.entries(n)){const a=c.toLowerCase();(a==="content-type"||a==="content-disposition"||a==="cache-control"||a==="etag"||a==="location")&&(o[c]=d)}e.resolve(new Response(i,{status:t,headers:o}))}failAll(s){for(const e of this.pending.values())clearTimeout(e.timer),e.reject(new Error(s));this.pending.clear();for(const e of this.relays.values()){if(!e.closed&&e.ws.readyState===r.OPEN){e.ws.send(JSON.stringify({type:"data",data:`\r
2
+ \x1B[31m--- agent disconnected (${s}) ---\x1B[0m\r
3
+ `}));try{e.ws.close(1011,"agent disconnected")}catch{}}e.closed=!0}this.relays.clear()}dispose(){this.disposed||(this.disposed=!0,this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.failAll("agent disconnected"),this.onClose?.(this))}}export{B as AgentChannel,N as MAX_RELAY_CONNS_PER_AGENT,b as TUNNEL_REQUEST_TIMEOUT_MS};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "2.2.10",
3
+ "version": "2.2.12",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {