tmux-weblink 2.0.4 → 2.0.6

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,36 +1,38 @@
1
- var jt="https://esm.sh/ghostty-web@0.4.0",Vt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function ce(t){return t.replace(Vt,"")}var de;function Ft(){return de||(document.querySelector("link[data-tmux-web-xterm-css]")?de=Promise.resolve():(de=new Promise(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>t(),{once:!0}),e.addEventListener("error",()=>t(),{once:!0}),document.head.appendChild(e)}),de))}var Ue=class ht{terminal;fitAddon;container;constructor(e,n,a){this.container=e,this.terminal=n,this.fitAddon=a,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,n,a){let[{Terminal:o},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Ft()]),c=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:a});return new ht(e,c,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=Oe(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(e,n){let a=this.terminal.buffer.active,[o,r]=e<=n?[e,n]:[n,e],c=a.viewportY,l=[];for(let u=o;u<=r;u++){let p=a.getLine(c+u);l.push(p?p.translateToString(!0):"")}return l.join(`
2
- `)}selectRows(e,n){let a=this.terminal.buffer.active,[o,r]=e<=n?[e,n]:[n,e];this.terminal.selectLines(a.viewportY+o,a.viewportY+r)}hasSelectionSupport(){return!0}},_t=class vt{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,n){this.container=e,this.terminal=n,this.terminal.open(e)}static async create(e,n,a){let o=await import(jt);await o.init();let r=new o.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:a});return new vt(e,r)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){let e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){let e=Oe(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let n=Math.floor(e.width/this.charW),a=Math.floor(e.height/this.charH);if(n<10||a<5)return!1;if(n!==this.colsValue||a!==this.rowsValue){this.colsValue=n,this.rowsValue=a,this.terminal.resize(n,a);for(let o of this.resizeCallbacks)o({cols:n,rows:a})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let n=this.terminal.getMode?.(2004)??!1;this.input(n?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(n=>e(n))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.rowsValue-1)}getRowsText(e,n){try{let a=this.terminal.buffer?.active;if(!a||typeof a.getLine!="function")return"";let[o,r]=e<=n?[e,n]:[n,e],c=typeof a.viewportY=="number"?a.viewportY:0,l=[];for(let u=o;u<=r;u++){let p=a.getLine(c+u);l.push(typeof p?.translateToString=="function"?p.translateToString(!0):"")}return l.join(`
3
- `)}catch{return""}}selectRows(e,n){try{if(typeof this.terminal?.selectLines!="function")return;let a=this.terminal.buffer?.active,o=typeof a?.viewportY=="number"?a.viewportY:0,[r,c]=e<=n?[e,n]:[n,e];this.terminal.selectLines(o+r,o+c)}catch{}}hasSelectionSupport(){return typeof this.terminal?.selectLines=="function"}updateCellMetrics(e=!1){let n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let a=n.offsetWidth/this.colsValue,o=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=a,this.charH=o)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function Oe(t){let e=t.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(e.width,n.width),height:Math.max(0,Math.min(e.height,n.height-Math.max(0,e.top)))}:e}function Kt(t,e,n){return t.innerHTML="",t.classList.add("terminal-pending"),(async()=>{let a;if(n.renderer==="ghostty")try{a=await _t.create(t,n.scrollback,n.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),a=await Ue.create(t,n.scrollback,n.theme)}else a=await Ue.create(t,n.scrollback,n.theme);let o,r=0,c,l=null,u=0,p="connecting",I=0,se=!1,oe=[],z="",N=!1,V=1e3,yt=location.protocol==="https:"?"wss:":"ws:",gt=n.wsBase||"/ws",wt=yt+"//"+location.host+gt+"/"+encodeURIComponent(e),bt="/api/session/"+encodeURIComponent(e)+"/upload",Et=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Lt(){return oe.join("")+z}async function kt(i,s=0){let d=i?a.viewportY():0,L=i?a.baseY():0,C=ce(Lt());if(a.reset(),!C){i||a.scrollToBottom();return}await a.write(C),i?a.scrollToLine(L+d+s):a.scrollToBottom()}function F(i){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(i))}function xt(i){let s;try{s=JSON.parse(i)}catch{p==="live"&&(z+=i,a.write(ce(i)));return}if(s.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?F({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(s.type==="auth.ok"){s.token&&localStorage.setItem("tmux-web-token",s.token),V=1e3,_(),F({type:"resize",cols:a.cols,rows:a.rows}),b();return}if(s.type==="auth.failed"){if(s.permanentLock||s.retryAfterMs&&s.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(s.message);return}return}if(s.type==="snapshot"&&typeof s.data=="string"){let d=ce(s.data);oe=[d],z="",I=typeof s.lines=="number"?s.lines:n.terminal.initialLines,p="live",a.reset(),a.write(d).then(()=>a.scrollToBottom());return}if(s.type==="window_changed"&&typeof s.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(s.activeIndex)&&(d.searchParams.set("window",String(s.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:s.windows}));return}if(s.type==="history"&&typeof s.data=="string"){se=!1,s.lines>0&&s.data&&(oe.unshift(s.data),I+=s.lines,kt(!0,s.lines));return}if(s.type==="data"&&typeof s.data=="string"){let d=ce(s.data);if(p==="connecting"){p="live",z=d,a.write(d);return}p==="live"&&(z+=d,a.write(d))}}function De(){let i=wt,s=localStorage.getItem("tmux-web-token");s&&(i+=(i.includes("?")?"&":"?")+"token="+encodeURIComponent(s)),o=new WebSocket(i),o.onopen=()=>{p="connecting",I=0,se=!1,oe=[],z="",a.reset(),V=1e3},o.onmessage=d=>{typeof d.data=="string"&&xt(d.data)},o.onclose=()=>{N||(p="connecting",setTimeout(()=>{V=Math.min(V*2,1e4),N||De()},V))},o.onerror=()=>o?.close()}function le(i){F({type:"input",data:i})}function _(){return a.fit()}function Le(){t.classList.remove("terminal-pending")}function b(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),_(),r=requestAnimationFrame(()=>{r=0,_()&&Le()}),c=setTimeout(()=>{_()&&Le()},120)}function Tt(){b(),setTimeout(b,50),setTimeout(b,150),setTimeout(b,300)}function Re(i){return typeof i=="string"&&i.startsWith("image/")}function Ae(i){return i?.files?.length?i.files[0]??null:null}function Ct(i){if(!i?.items)return null;for(let s=0;s<i.items.length;s++){let d=i.items[s];if(d?.kind==="file"&&Re(d.type))return d.getAsFile()}return null}async function Mt(i){let s=new FormData,d=i instanceof File&&i.name?i.name:"upload";s.append("file",i,d);let L=localStorage.getItem("tmux-web-token"),C={};L&&(C.Authorization="Bearer "+L);let Ce=await fetch(bt,{method:"POST",body:s,headers:C});if(!Ce.ok){let _e="upload failed";try{let Ke=await Ce.json();Ke.error&&(_e=Ke.error)}catch{}throw new Error(_e)}let Fe=await Ce.json();if(!Fe.path)throw new Error("no path in response");return Fe.path}async function ke(i){try{let s=await Mt(i);le(s)}catch{}}a.onData(i=>le(i)),a.onResize(({cols:i,rows:s})=>F({type:"resize",cols:i,rows:s})),a.onScroll(()=>{p!=="live"||se||!a.isNearScrollbackTop()||(se=!0,F({type:"load_history",before:I}))});let K=!1,U=!1,x=null,D=null,O=document.createElement("div");O.className="terminal-copy-banner",O.innerHTML='<strong>Copy mode</strong><span class="hint">drag to select \xB7 Esc exits</span>',O.hidden=!0,t.appendChild(O);let P=document.createElement("div");P.className="terminal-copy-banner terminal-copy-toast",P.textContent="Copied",P.hidden=!0,t.appendChild(P);function $e(i){let s=t.getBoundingClientRect(),d=s.height>0&&a.rows>0?s.height/a.rows:18;return Math.max(0,Math.min(a.rows-1,Math.floor((i-s.top)/d)))}async function ze(i){if(!i)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(i);return}}catch{}let s=document.createElement("textarea");s.value=i,s.style.position="fixed",s.style.opacity="0",s.style.pointerEvents="none",document.body.appendChild(s),s.focus(),s.select();try{document.execCommand("copy")}catch{}s.remove()}let q;function Ne(){P.hidden=!1,q&&clearTimeout(q),q=setTimeout(()=>{P.hidden=!0},1e3)}function X(i){K=i,O.hidden=!i,t.classList.toggle("terminal-copy-mode-active",i),i&&a.focus()}function Pe(){X(!K)}function It(){return x===null||D===null?null:x<=D?[x,D]:[D,x]}function Bt(i){N||i.button!==0||U||(K||i.shiftKey)&&(i.preventDefault(),i.stopPropagation(),U=!0,x=$e(i.clientY),D=x,a.selectRows(x,x),document.addEventListener("mousemove",xe,!0),document.addEventListener("mouseup",Te,!0))}function xe(i){N||!U||x===null||(i.preventDefault(),i.stopPropagation(),D=$e(i.clientY),a.selectRows(x,D))}function Te(i){if(N||!U||x===null)return;i.preventDefault(),i.stopPropagation();let s=It();document.removeEventListener("mousemove",xe,!0),document.removeEventListener("mouseup",Te,!0),U=!1,X(!1),s&&(ze(a.getRowsText(s[0],s[1])).then(Ne),a.focus())}t.addEventListener("mousedown",Bt,!0),a.attachCustomKeyEventHandler(i=>{if(i.type!=="keydown")return!0;if(K){if(i.key==="Escape")return i.preventDefault(),X(!1),!1;if(i.key==="Enter"||(i.ctrlKey||i.metaKey)&&i.code==="KeyC")return i.preventDefault(),ze(a.getScreenText()).then(Ne),X(!1),!1}return i.altKey&&i.code==="KeyC"&&!i.ctrlKey&&!i.metaKey?(i.preventDefault(),Pe(),!1):(i.ctrlKey||i.metaKey)&&i.code==="KeyV"?(i.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let s=await navigator.clipboard.read();for(let d of s)for(let L of d.types)if(Re(L)){let C=await d.getType(L);await ke(C);return}}}catch{}try{let s=await navigator.clipboard?.readText();s&&a.pasteText(Ye(s))}catch{}})(),!1):!0});let re=0;function Ye(i){return i.split(`\r
1
+ var Vt="https://esm.sh/ghostty-web@0.4.0",_t=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Z(t){return t.replace(_t,"")}var pe;function Ft(){return pe||(document.querySelector("link[data-tmux-web-xterm-css]")?pe=Promise.resolve():(pe=new Promise(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>t(),{once:!0}),e.addEventListener("error",()=>t(),{once:!0}),document.head.appendChild(e)}),pe))}var Oe=class vt{terminal;fitAddon;container;constructor(e,n,i){this.container=e,this.terminal=n,this.fitAddon=i,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,n,i){let[{Terminal:o},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Ft()]),c=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new vt(e,c,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=qe(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(e,n){let i=this.terminal.buffer.active,[o,r]=e<=n?[e,n]:[n,e],c=i.viewportY,l=[];for(let u=o;u<=r;u++){let p=i.getLine(c+u);l.push(p?p.translateToString(!0):"")}return l.join(`
2
+ `)}selectRows(e,n){let i=this.terminal.buffer.active,[o,r]=e<=n?[e,n]:[n,e];this.terminal.selectLines(i.viewportY+o,i.viewportY+r)}hasSelectionSupport(){return!0}},Kt=class yt{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,n){this.container=e,this.terminal=n,this.terminal.open(e)}static async create(e,n,i){let o=await import(Vt);await o.init();let r=new o.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new yt(e,r)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){let e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){let e=qe(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let n=Math.floor(e.width/this.charW),i=Math.floor(e.height/this.charH);if(n<10||i<5)return!1;if(n!==this.colsValue||i!==this.rowsValue){this.colsValue=n,this.rowsValue=i,this.terminal.resize(n,i);for(let o of this.resizeCallbacks)o({cols:n,rows:i})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let n=this.terminal.getMode?.(2004)??!1;this.input(n?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(n=>e(n))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.rowsValue-1)}getRowsText(e,n){try{let i=this.terminal.buffer?.active;if(!i||typeof i.getLine!="function")return"";let[o,r]=e<=n?[e,n]:[n,e],c=typeof i.viewportY=="number"?i.viewportY:0,l=[];for(let u=o;u<=r;u++){let p=i.getLine(c+u);l.push(typeof p?.translateToString=="function"?p.translateToString(!0):"")}return l.join(`
3
+ `)}catch{return""}}selectRows(e,n){try{if(typeof this.terminal?.selectLines!="function")return;let i=this.terminal.buffer?.active,o=typeof i?.viewportY=="number"?i.viewportY:0,[r,c]=e<=n?[e,n]:[n,e];this.terminal.selectLines(o+r,o+c)}catch{}}hasSelectionSupport(){return typeof this.terminal?.selectLines=="function"}updateCellMetrics(e=!1){let n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let i=n.offsetWidth/this.colsValue,o=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=i,this.charH=o)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function qe(t){let e=t.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(e.width,n.width),height:Math.max(0,Math.min(e.height,n.height-Math.max(0,e.top)))}:e}function Ut(t,e,n){return t.innerHTML="",t.classList.add("terminal-pending"),(async()=>{let i;if(n.renderer==="ghostty")try{i=await Kt.create(t,n.scrollback,n.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),i=await Oe.create(t,n.scrollback,n.theme)}else i=await Oe.create(t,n.scrollback,n.theme);let o,r=0,c,l=null,u=0,p="connecting",B=0,oe=!1,le=[],z="",N=!1,V=1e3,gt=location.protocol==="https:"?"wss:":"ws:",wt=n.wsBase||"/ws",bt=gt+"//"+location.host+wt+"/"+encodeURIComponent(e),Et="/api/session/"+encodeURIComponent(e)+"/upload",Lt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function kt(){return le.join("")+z}async function xt(a,s=0){let d=a?i.viewportY():0,L=a?i.baseY():0,C=Z(kt());if(i.reset(),!C){a||i.scrollToBottom();return}await i.write(C),a?i.scrollToLine(L+d+s):i.scrollToBottom()}function _(a){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(a))}function Tt(a){let s;try{s=JSON.parse(a)}catch{p==="live"&&(z+=a,i.write(Z(a)));return}if(s.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?_({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(s.type==="attach_failed"&&typeof s.message=="string"){re=!0,p="dead",i.reset(),i.write(`\r
4
+ \x1B[31m`+Z(s.message)+`\x1B[0m\r
5
+ `).then(()=>{de();try{o?.close()}catch{}});return}if(s.type==="auth.ok"){s.token&&localStorage.setItem("tmux-web-token",s.token),V=1e3,F(),_({type:"resize",cols:i.cols,rows:i.rows}),b();return}if(s.type==="auth.failed"){if(s.permanentLock||s.retryAfterMs&&s.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(s.message);return}return}if(s.type==="snapshot"&&typeof s.data=="string"){let d=Z(s.data);le=[d],z="",B=typeof s.lines=="number"?s.lines:n.terminal.initialLines,p="live",i.reset(),i.write(d).then(()=>i.scrollToBottom());return}if(s.type==="window_changed"&&typeof s.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(s.activeIndex)&&(d.searchParams.set("window",String(s.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:s.windows}));return}if(s.type==="history"&&typeof s.data=="string"){oe=!1,s.lines>0&&s.data&&(le.unshift(s.data),B+=s.lines,xt(!0,s.lines));return}if(s.type==="data"&&typeof s.data=="string"){let d=Z(s.data);if(p==="connecting"){p="live",z=d,i.write(d);return}p==="live"&&(z+=d,i.write(d))}}let re=!1;function Re(){if(re)return;let a=bt,s=localStorage.getItem("tmux-web-token");s&&(a+=(a.includes("?")?"&":"?")+"token="+encodeURIComponent(s)),o=new WebSocket(a),o.onopen=()=>{p="connecting",B=0,oe=!1,le=[],z="",i.reset(),V=1e3},o.onmessage=d=>{typeof d.data=="string"&&Tt(d.data)},o.onclose=()=>{N||(p="connecting",!re&&setTimeout(()=>{V=Math.min(V*2,1e4),!N&&!re&&Re()},V))},o.onerror=()=>o?.close()}function ce(a){_({type:"input",data:a})}function F(){return i.fit()}function de(){t.classList.remove("terminal-pending")}function b(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),F(),r=requestAnimationFrame(()=>{r=0,F()&&de()}),c=setTimeout(()=>{F()&&de()},120)}function Ct(){b(),setTimeout(b,50),setTimeout(b,150),setTimeout(b,300)}function Ae(a){return typeof a=="string"&&a.startsWith("image/")}function $e(a){return a?.files?.length?a.files[0]??null:null}function Mt(a){if(!a?.items)return null;for(let s=0;s<a.items.length;s++){let d=a.items[s];if(d?.kind==="file"&&Ae(d.type))return d.getAsFile()}return null}async function Bt(a){let s=new FormData,d=a instanceof File&&a.name?a.name:"upload";s.append("file",a,d);let L=localStorage.getItem("tmux-web-token"),C={};L&&(C.Authorization="Bearer "+L);let Me=await fetch(Et,{method:"POST",body:s,headers:C});if(!Me.ok){let Ke="upload failed";try{let Ue=await Me.json();Ue.error&&(Ke=Ue.error)}catch{}throw new Error(Ke)}let Fe=await Me.json();if(!Fe.path)throw new Error("no path in response");return Fe.path}async function xe(a){try{let s=await Bt(a);ce(s)}catch{}}i.onData(a=>ce(a)),i.onResize(({cols:a,rows:s})=>_({type:"resize",cols:a,rows:s})),i.onScroll(()=>{p!=="live"||oe||!i.isNearScrollbackTop()||(oe=!0,_({type:"load_history",before:B}))});let K=!1,U=!1,x=null,D=null,O=document.createElement("div");O.className="terminal-copy-banner",O.innerHTML='<strong>Copy mode</strong><span class="hint">drag to select \xB7 Esc exits</span>',O.hidden=!0,t.appendChild(O);let P=document.createElement("div");P.className="terminal-copy-banner terminal-copy-toast",P.textContent="Copied",P.hidden=!0,t.appendChild(P);function ze(a){let s=t.getBoundingClientRect(),d=s.height>0&&i.rows>0?s.height/i.rows:18;return Math.max(0,Math.min(i.rows-1,Math.floor((a-s.top)/d)))}async function Ne(a){if(!a)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(a);return}}catch{}let s=document.createElement("textarea");s.value=a,s.style.position="fixed",s.style.opacity="0",s.style.pointerEvents="none",document.body.appendChild(s),s.focus(),s.select();try{document.execCommand("copy")}catch{}s.remove()}let q;function Pe(){P.hidden=!1,q&&clearTimeout(q),q=setTimeout(()=>{P.hidden=!0},1e3)}function X(a){K=a,O.hidden=!a,t.classList.toggle("terminal-copy-mode-active",a),a&&i.focus()}function Ye(){X(!K)}function It(){return x===null||D===null?null:x<=D?[x,D]:[D,x]}function St(a){N||a.button!==0||U||(K||a.shiftKey)&&(a.preventDefault(),a.stopPropagation(),U=!0,x=ze(a.clientY),D=x,i.selectRows(x,x),document.addEventListener("mousemove",Te,!0),document.addEventListener("mouseup",Ce,!0))}function Te(a){N||!U||x===null||(a.preventDefault(),a.stopPropagation(),D=ze(a.clientY),i.selectRows(x,D))}function Ce(a){if(N||!U||x===null)return;a.preventDefault(),a.stopPropagation();let s=It();document.removeEventListener("mousemove",Te,!0),document.removeEventListener("mouseup",Ce,!0),U=!1,X(!1),s&&(Ne(i.getRowsText(s[0],s[1])).then(Pe),i.focus())}t.addEventListener("mousedown",St,!0),i.attachCustomKeyEventHandler(a=>{if(a.type!=="keydown")return!0;if(K){if(a.key==="Escape")return a.preventDefault(),X(!1),!1;if(a.key==="Enter"||(a.ctrlKey||a.metaKey)&&a.code==="KeyC")return a.preventDefault(),Ne(i.getScreenText()).then(Pe),X(!1),!1}return a.altKey&&a.code==="KeyC"&&!a.ctrlKey&&!a.metaKey?(a.preventDefault(),Ye(),!1):(a.ctrlKey||a.metaKey)&&a.code==="KeyV"?(a.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let s=await navigator.clipboard.read();for(let d of s)for(let L of d.types)if(Ae(L)){let C=await d.getType(L);await xe(C);return}}}catch{}try{let s=await navigator.clipboard?.readText();s&&i.pasteText(We(s))}catch{}})(),!1):!0});let ue=0;function We(a){return a.split(`\r
4
6
  `).join(`
5
7
  `).split(`
6
- `).join("\r")}async function St(i){let s=Ct(i.clipboardData);if(s){i.preventDefault(),i.stopPropagation();let C=Date.now();if(C-re<50)return;re=C,await ke(s);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let L=Date.now();L-re<50||(re=L,a.pasteText(Ye(d)))}let J=0;function Ht(i){i.preventDefault(),J++,Ae(i.dataTransfer)&&t.classList.add("terminal-drag-over")}function Dt(i){i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy")}function Rt(i){i.preventDefault(),J--,J<=0&&(J=0,t.classList.remove("terminal-drag-over"))}async function At(i){i.preventDefault(),J=0,t.classList.remove("terminal-drag-over");let s=Ae(i.dataTransfer);s&&await ke(s)}function We(i){!Et||i.key!=="Escape"||K||i.defaultPrevented||i.repeat||i.metaKey||i.ctrlKey||i.altKey||a.isFocused()&&(i.preventDefault(),i.stopPropagation(),le("\x1B"))}t.addEventListener("paste",St),t.addEventListener("dragenter",Ht),t.addEventListener("dragover",Dt),t.addEventListener("dragleave",Rt),t.addEventListener("drop",At),document.addEventListener("keydown",We,!0);function $t(i){if(i.touches.length!==1){l=null;return}let s=i.touches[0];s&&(l={startX:s.clientX,startY:s.clientY,lastX:s.clientX,lastY:s.clientY,scrolling:!1},i.stopPropagation())}function zt(i){if(!l||i.touches.length!==1)return;let s=i.touches[0];if(!s)return;let d=s.clientY-l.startY,L=s.clientX-l.startX;if(!l.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(L))return;l.scrolling=!0,u=Date.now()+500}i.preventDefault(),i.stopPropagation(),Wt(-(s.clientY-l.lastY),s.clientX,s.clientY),l.lastX=s.clientX,l.lastY=s.clientY}function Nt(i){if(!l)return;let s=l.scrolling;l=null,i.stopPropagation(),s?(u=Date.now()+500,i.preventDefault()):a.focus()}function Pt(i){l=null,i.stopPropagation()}function Yt(i){i.pointerType==="touch"&&Date.now()<u&&(i.preventDefault(),i.stopPropagation())}function Wt(i,s,d){(t.querySelector(".xterm-screen")??t).dispatchEvent(new WheelEvent("wheel",{deltaY:i,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:s,clientY:d,bubbles:!0,cancelable:!0}))}t.addEventListener("touchstart",$t,{passive:!0,capture:!0}),t.addEventListener("touchmove",zt,{passive:!1,capture:!0}),t.addEventListener("touchend",Nt,{passive:!1,capture:!0}),t.addEventListener("touchcancel",Pt,{passive:!0,capture:!0}),t.addEventListener("pointerup",Yt,!0);function je(){b()}window.addEventListener("resize",je),window.visualViewport?.addEventListener("resize",b),window.visualViewport?.addEventListener("scroll",b);let Ve=new ResizeObserver(()=>b());return Ve.observe(t),document.fonts?.ready.then(()=>{b(),setTimeout(b,50),setTimeout(b,200)}),t.addEventListener("focusin",Tt,!0),requestAnimationFrame(()=>{_()&&Le(),De(),b()}),{destroy(){if(N=!0,X(!1),q&&clearTimeout(q),document.removeEventListener("mousemove",xe,!0),document.removeEventListener("mouseup",Te,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}t.innerHTML="",t.classList.remove("terminal-pending"),window.removeEventListener("resize",je),Ve.disconnect(),document.removeEventListener("keydown",We,!0)},focus(){a.focus()},sendInput(i){le(i)},toggleCopyMode(){Pe()}}})()}var Y=window.__TMUX_WEB_SHELL__,h=null,y=null,qe=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,w=qe;window.__tmuxWebScopeAgent=qe;var Xe="sessions",S="",ue="",pe="",Je="";function E(t){return w?"/a/"+encodeURIComponent(w)+t:t}var m=document.getElementById("sidebar-content"),R=document.getElementById("main-placeholder"),k=document.getElementById("terminal-container"),Z=document.getElementById("file-editor"),Ut=document.getElementById("fe-path"),T=document.getElementById("fe-status"),G=document.getElementById("fe-content"),A=document.getElementById("fe-save"),Ot=document.getElementById("fe-delete"),qt=document.getElementById("fe-back"),Ze=document.getElementById("fe-new-name"),Xt=document.getElementById("fe-new-btn"),Me=document.getElementById("settings-backdrop"),Ge=document.getElementById("settings-popover"),Qe=document.querySelector(".sidebar");function Jt(){return window.matchMedia("(max-width: 640px)").matches}function Ie(){Jt()&&Qe.classList.add("collapsed")}function et(){Qe.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{et(),tt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{et(),tt("files")});function tt(t){Xe=t,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let e=document.getElementById(t==="sessions"?"mode-sessions":"mode-files");e&&e.classList.add("active"),t==="sessions"?W():ln()}var nt="";async function W(){try{let e=await(await fetch("/api/sidebar/sessions")).json(),n=JSON.stringify({recent:e.recent,agents:e.agents});if(n===nt)return;nt=n,Zt(e)}catch{m.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let t=document.getElementById("ns-btn");t&&t.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function Zt(t){m.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let e=t.recent||[],n=document.createElement("div");n.className="sidebar-section-label"+(w?" clickable":""),n.textContent=w?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",w&&(n.title="Back to hub",n.addEventListener("click",()=>{window.location.href="/"})),m.appendChild(n);for(let o of e){let r=document.createElement("div");r.className="session-item"+(o.name===y&&!w?" active":""),r.setAttribute("data-session",o.name),r.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${o.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 Ht(a){let s=Mt(a.clipboardData);if(s){a.preventDefault(),a.stopPropagation();let C=Date.now();if(C-ue<50)return;ue=C,await xe(s);return}let d=a.clipboardData?.getData("text/plain");if(!d)return;a.preventDefault(),a.stopPropagation();let L=Date.now();L-ue<50||(ue=L,i.pasteText(We(d)))}let J=0;function Dt(a){a.preventDefault(),J++,$e(a.dataTransfer)&&t.classList.add("terminal-drag-over")}function Rt(a){a.preventDefault(),a.dataTransfer&&(a.dataTransfer.dropEffect="copy")}function At(a){a.preventDefault(),J--,J<=0&&(J=0,t.classList.remove("terminal-drag-over"))}async function $t(a){a.preventDefault(),J=0,t.classList.remove("terminal-drag-over");let s=$e(a.dataTransfer);s&&await xe(s)}function je(a){!Lt||a.key!=="Escape"||K||a.defaultPrevented||a.repeat||a.metaKey||a.ctrlKey||a.altKey||i.isFocused()&&(a.preventDefault(),a.stopPropagation(),ce("\x1B"))}t.addEventListener("paste",Ht),t.addEventListener("dragenter",Dt),t.addEventListener("dragover",Rt),t.addEventListener("dragleave",At),t.addEventListener("drop",$t),document.addEventListener("keydown",je,!0);function zt(a){if(a.touches.length!==1){l=null;return}let s=a.touches[0];s&&(l={startX:s.clientX,startY:s.clientY,lastX:s.clientX,lastY:s.clientY,scrolling:!1},a.stopPropagation())}function Nt(a){if(!l||a.touches.length!==1)return;let s=a.touches[0];if(!s)return;let d=s.clientY-l.startY,L=s.clientX-l.startX;if(!l.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(L))return;l.scrolling=!0,u=Date.now()+500}a.preventDefault(),a.stopPropagation(),jt(-(s.clientY-l.lastY),s.clientX,s.clientY),l.lastX=s.clientX,l.lastY=s.clientY}function Pt(a){if(!l)return;let s=l.scrolling;l=null,a.stopPropagation(),s?(u=Date.now()+500,a.preventDefault()):i.focus()}function Yt(a){l=null,a.stopPropagation()}function Wt(a){a.pointerType==="touch"&&Date.now()<u&&(a.preventDefault(),a.stopPropagation())}function jt(a,s,d){(t.querySelector(".xterm-screen")??t).dispatchEvent(new WheelEvent("wheel",{deltaY:a,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:s,clientY:d,bubbles:!0,cancelable:!0}))}t.addEventListener("touchstart",zt,{passive:!0,capture:!0}),t.addEventListener("touchmove",Nt,{passive:!1,capture:!0}),t.addEventListener("touchend",Pt,{passive:!1,capture:!0}),t.addEventListener("touchcancel",Yt,{passive:!0,capture:!0}),t.addEventListener("pointerup",Wt,!0);function Ve(){b()}window.addEventListener("resize",Ve),window.visualViewport?.addEventListener("resize",b),window.visualViewport?.addEventListener("scroll",b);let _e=new ResizeObserver(()=>b());return _e.observe(t),document.fonts?.ready.then(()=>{b(),setTimeout(b,50),setTimeout(b,200)}),t.addEventListener("focusin",Ct,!0),requestAnimationFrame(()=>{F()&&de(),Re(),b()}),{destroy(){if(N=!0,X(!1),q&&clearTimeout(q),document.removeEventListener("mousemove",Te,!0),document.removeEventListener("mouseup",Ce,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}t.innerHTML="",t.classList.remove("terminal-pending"),window.removeEventListener("resize",Ve),_e.disconnect(),document.removeEventListener("keydown",je,!0)},focus(){i.focus()},sendInput(a){ce(a)},toggleCopyMode(){Ye()}}})()}var Y=window.__TMUX_WEB_SHELL__,h=null,y=null,Xe=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,w=Xe;window.__tmuxWebScopeAgent=Xe;var Je="sessions",S="",fe="",me="",Ze="";function E(t){return w?"/a/"+encodeURIComponent(w)+t:t}var m=document.getElementById("sidebar-content"),R=document.getElementById("main-placeholder"),k=document.getElementById("terminal-container"),G=document.getElementById("file-editor"),Ot=document.getElementById("fe-path"),T=document.getElementById("fe-status"),Q=document.getElementById("fe-content"),A=document.getElementById("fe-save"),qt=document.getElementById("fe-delete"),Xt=document.getElementById("fe-back"),Ge=document.getElementById("fe-new-name"),Jt=document.getElementById("fe-new-btn"),Be=document.getElementById("settings-backdrop"),Qe=document.getElementById("settings-popover"),et=document.querySelector(".sidebar");function Zt(){return window.matchMedia("(max-width: 640px)").matches}function Ie(){Zt()&&et.classList.add("collapsed")}function tt(){et.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{tt(),nt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{tt(),nt("files")});function nt(t){Je=t,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let e=document.getElementById(t==="sessions"?"mode-sessions":"mode-files");e&&e.classList.add("active"),t==="sessions"?W():rn()}var it="";async function W(){try{let e=await(await fetch("/api/sidebar/sessions")).json(),n=JSON.stringify({recent:e.recent,agents:e.agents});if(n===it)return;it=n,Gt(e)}catch{m.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let t=document.getElementById("ns-btn");t&&t.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function Gt(t){m.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let e=t.recent||[],n=document.createElement("div");n.className="sidebar-section-label"+(w?" clickable":""),n.textContent=w?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",w&&(n.title="Back to hub",n.addEventListener("click",()=>{window.location.href="/"})),m.appendChild(n);for(let o of e){let r=document.createElement("div");r.className="session-item"+(o.name===y&&!w?" active":""),r.setAttribute("data-session",o.name),r.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${o.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>
7
9
  <span>${f(o.name)}</span>
8
10
  <button class="session-edit-btn" data-session="${f(o.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>
9
- `,r.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||Q(o.name)}),m.appendChild(r);let c=r.querySelector(".session-edit-btn");c&&c.addEventListener("click",l=>{l.stopPropagation(),pn(o.name,c)})}let a=t.agents||[];for(let o of a){if(!o.sessions||o.sessions.length===0)continue;let r=o.agentId===w,c=document.createElement("div");c.className="sidebar-section-label machine-label"+(o.online?"":" agent-offline-label")+(!r&&o.online?" clickable":""),c.textContent=o.agentName+(o.online?"":" (offline)"),!r&&o.online&&(c.title="\u6253\u5F00 "+o.agentName,c.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(o.agentId)+"/"})),m.appendChild(c);for(let l of o.sessions){let u=document.createElement("div");u.className="session-item"+(l.name===y&&w===o.agentId?" active":"")+(o.online?"":" session-offline"),u.setAttribute("data-session",l.name),u.setAttribute("data-agent",o.agentId),u.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${l.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
- <span>${f(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||Q(l.name,o.agentId)}),m.appendChild(u)}}}function f(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function Q(t,e){if(!(y===t&&h&&w===(e||null))){Z.style.display="none",R.style.display="none",k.style.display="",k.classList.add("terminal-pending"),Ie(),h&&(be(),h.destroy(),h=null),y=t,w=e||null,window.__tmuxWebScopeAgent=w;for(let n of m.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===t&&(n.getAttribute("data-agent")||null)===w);try{h=await Kt(k,t,{terminal:Y.terminal,scrollback:Y.scrollback,theme:Y.theme,renderer:Y.renderer,wsBase:w?"/ws/a/"+encodeURIComponent(w):Y.wsBase||"/ws"}),pt()}catch(n){console.error("[shell] terminal init failed:",n);let a=n&&typeof n=="object"&&"message"in n?n.message:String(n);k.textContent="Failed to open terminal for "+t+": "+a,k.classList.remove("terminal-pending")}}}var fe=[],v=null,it=!1;function Gt(t){if(!v||!v.files.length)return null;let e=v.repoRoot;if(!e||!t.startsWith(e))return null;let n=t.slice(e.length+1);for(let a of v.files)if(a.path===n)return a.status;return null}async function Qt(t){it=!0;try{let e=await fetch(E("/api/git/status?path="+encodeURIComponent(t)));e.ok?v=await e.json():v=null}catch{v=null}finally{it=!1}}function en(){if(!v||!v.branch)return"";let{branch:t,linesAdded:e,linesRemoved:n}=v,a=`<div class="sidebar-git"><div class="git-branch-badge">
11
+ `,r.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||ee(o.name)}),m.appendChild(r);let c=r.querySelector(".session-edit-btn");c&&c.addEventListener("click",l=>{l.stopPropagation(),fn(o.name,c)})}let i=t.agents||[];for(let o of i){if(!o.sessions||o.sessions.length===0)continue;let r=o.agentId===w,c=document.createElement("div");c.className="sidebar-section-label machine-label"+(o.online?"":" agent-offline-label")+(!r&&o.online?" clickable":""),c.textContent=o.agentName+(o.online?"":" (offline)"),!r&&o.online&&(c.title="\u6253\u5F00 "+o.agentName,c.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(o.agentId)+"/"})),m.appendChild(c);for(let l of o.sessions){let u=document.createElement("div");u.className="session-item"+(l.name===y&&w===o.agentId?" active":"")+(o.online?"":" session-offline"),u.setAttribute("data-session",l.name),u.setAttribute("data-agent",o.agentId),u.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${l.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>
12
+ <span>${f(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||ee(l.name,o.agentId)}),m.appendChild(u)}}}function f(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function ee(t,e){if(!(y===t&&h&&w===(e||null))){G.style.display="none",R.style.display="none",k.style.display="",k.classList.add("terminal-pending"),Ie(),h&&(Le(),h.destroy(),h=null),y=t,w=e||null,window.__tmuxWebScopeAgent=w;for(let n of m.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===t&&(n.getAttribute("data-agent")||null)===w);try{h=await Ut(k,t,{terminal:Y.terminal,scrollback:Y.scrollback,theme:Y.theme,renderer:Y.renderer,wsBase:w?"/ws/a/"+encodeURIComponent(w):Y.wsBase||"/ws"}),ft()}catch(n){console.error("[shell] terminal init failed:",n);let i=n&&typeof n=="object"&&"message"in n?n.message:String(n);k.textContent="Failed to open terminal for "+t+": "+i,k.classList.remove("terminal-pending")}}}var he=[],v=null,at=!1;function Qt(t){if(!v||!v.files.length)return null;let e=v.repoRoot;if(!e||!t.startsWith(e))return null;let n=t.slice(e.length+1);for(let i of v.files)if(i.path===n)return i.status;return null}async function en(t){at=!0;try{let e=await fetch(E("/api/git/status?path="+encodeURIComponent(t)));e.ok?v=await e.json():v=null}catch{v=null}finally{at=!1}}function tn(){if(!v||!v.branch)return"";let{branch:t,linesAdded:e,linesRemoved:n}=v,i=`<div class="sidebar-git"><div class="git-branch-badge">
11
13
  <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>
12
- ${f(t)}</div>`;return(e>0||n>0)&&(a+=`<div class="git-diff-stats">+<span class="add">${e}</span> <span class="del">-${n}</span></div>`),a+"</div>"}m.addEventListener("click",t=>{t.target.closest(".sidebar-git")&&j&&v&&(t.stopPropagation(),rt())});var ee=document.getElementById("header-git"),at=document.getElementById("git-diff-view"),st=document.getElementById("gd-path"),me=document.getElementById("gd-status"),he=document.getElementById("gd-content"),tn=document.getElementById("gd-back"),j=null;function te(t){if(!t||!t.branch){ee.style.display="none",j=null;return}j=t.repoRoot;let{branch:e,linesAdded:n,linesRemoved:a}=t,o=`<span class="branch">${f(e)}</span>`;(n>0||a>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${a}</span>`),ee.innerHTML=o,ee.style.display="block"}ee.addEventListener("click",t=>{j&&v&&(t.stopPropagation(),rt())});var g=document.getElementById("git-popover"),ve=document.getElementById("git-popover-backdrop");function Be(){let t=ee.getBoundingClientRect();g.style.top=t.bottom+4+"px",g.style.left=Math.max(8,Math.min(t.left,window.innerWidth-400))+"px"}function ot(){let t=j,e=document.getElementById("git-popover-browse");e&&e.addEventListener("click",()=>{if(ge(),t){Xe="files",document.querySelectorAll(".mode-btn").forEach(a=>a.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),H(t)}})}var lt="";function rt(){if(!v)return;let t=v.files||[];if(!t.length)g.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
13
- <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let e=`<div class="git-popover-header">${t.length} file${t.length>1?"s":""} changed</div>`;for(let n=0;n<t.length;n++){let a=t[n],o=a.additions||0,r=a.deletions||0,c="";switch(a.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}e+=`<div class="git-popover-item" data-idx="${n}">
14
- ${c}<span class="file-path">${f(a.path)}</span>
14
+ ${f(t)}</div>`;return(e>0||n>0)&&(i+=`<div class="git-diff-stats">+<span class="add">${e}</span> <span class="del">-${n}</span></div>`),i+"</div>"}m.addEventListener("click",t=>{t.target.closest(".sidebar-git")&&j&&v&&(t.stopPropagation(),ct())});var te=document.getElementById("header-git"),st=document.getElementById("git-diff-view"),ot=document.getElementById("gd-path"),ve=document.getElementById("gd-status"),ye=document.getElementById("gd-content"),nn=document.getElementById("gd-back"),j=null;function ne(t){if(!t||!t.branch){te.style.display="none",j=null;return}j=t.repoRoot;let{branch:e,linesAdded:n,linesRemoved:i}=t,o=`<span class="branch">${f(e)}</span>`;(n>0||i>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${i}</span>`),te.innerHTML=o,te.style.display="block"}te.addEventListener("click",t=>{j&&v&&(t.stopPropagation(),ct())});var g=document.getElementById("git-popover"),ge=document.getElementById("git-popover-backdrop");function Se(){let t=te.getBoundingClientRect();g.style.top=t.bottom+4+"px",g.style.left=Math.max(8,Math.min(t.left,window.innerWidth-400))+"px"}function lt(){let t=j,e=document.getElementById("git-popover-browse");e&&e.addEventListener("click",()=>{if(be(),t){Je="files",document.querySelectorAll(".mode-btn").forEach(i=>i.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),H(t)}})}var rt="";function ct(){if(!v)return;let t=v.files||[];if(!t.length)g.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
15
+ <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let e=`<div class="git-popover-header">${t.length} file${t.length>1?"s":""} changed</div>`;for(let n=0;n<t.length;n++){let i=t[n],o=i.additions||0,r=i.deletions||0,c="";switch(i.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}e+=`<div class="git-popover-item" data-idx="${n}">
16
+ ${c}<span class="file-path">${f(i.path)}</span>
15
17
  <span class="file-add">${o>0?"+"+o:""}</span>
16
18
  <span class="file-del">${r>0?"-"+r:""}</span>
17
- </div>`}e+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',lt=e,g.innerHTML=e,g.querySelectorAll(".git-popover-item").forEach(n=>{let a=parseInt(n.dataset.idx||"0",10),o=t[a];!o||o.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>dt(o.path)))})}Be(),g.classList.add("open"),ve.classList.add("open"),ot()}function nn(){return window.innerWidth>=1024}function ct(t,e){let n=(t+(e&&t?`
19
+ </div>`}e+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',rt=e,g.innerHTML=e,g.querySelectorAll(".git-popover-item").forEach(n=>{let i=parseInt(n.dataset.idx||"0",10),o=t[i];!o||o.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>ut(o.path)))})}Se(),g.classList.add("open"),ge.classList.add("open"),lt()}function an(){return window.innerWidth>=1024}function dt(t,e){let n=(t+(e&&t?`
18
20
  `:"")+e).split(`
19
- `),a="",o=0,r=0;for(let c of n){let l=c;if(l.startsWith("@@")){let u=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(o=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),a+=`<div class="diff-line hunk"><span class="ln-body">${f(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(a+=`<div class="diff-line header"><span class="ln-body">${f(l)}</span></div>`);continue}if(l.startsWith("+"))r++,a+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${f(l)}</span></div>`;else if(l.startsWith("-"))o++,a+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${f(l)}</span></div>`;else if(l.startsWith("\\"))a+=`<div class="diff-line"><span class="ln-body">${f(l)}</span></div>`;else{if(!l&&!o&&!r)continue;o++,r++,a+=`<div class="diff-line"><span class="ln-no">${o}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${f(l)}</span></div>`}}return{html:a,lineCount:n.length}}var ye=null;async function dt(t){let e=j;if(e)if(ge(),nn()){k.style.display="none",h&&(be(),h.destroy(),h=null,ye=y,y=null),te(null),Z.style.display="none",R.style.display="none",at.style.display="flex",st.textContent=t,me.textContent="Loading\u2026",he.innerHTML="";try{let n=await fetch(E("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t)));if(!n.ok)throw new Error(await n.text());let a=await n.json();sn(t,a.diff,a.stagedDiff)}catch(n){let a=n instanceof Error?n.message:String(n);me.textContent="Error",he.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${f(a)}</div>`}}else{g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
21
+ `),i="",o=0,r=0;for(let c of n){let l=c;if(l.startsWith("@@")){let u=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(o=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${f(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${f(l)}</span></div>`);continue}if(l.startsWith("+"))r++,i+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${f(l)}</span></div>`;else if(l.startsWith("-"))o++,i+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${f(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${f(l)}</span></div>`;else{if(!l&&!o&&!r)continue;o++,r++,i+=`<div class="diff-line"><span class="ln-no">${o}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${f(l)}</span></div>`}}return{html:i,lineCount:n.length}}var we=null;async function ut(t){let e=j;if(e)if(be(),an()){k.style.display="none",h&&(Le(),h.destroy(),h=null,we=y,y=null),ne(null),G.style.display="none",R.style.display="none",st.style.display="flex",ot.textContent=t,ve.textContent="Loading\u2026",ye.innerHTML="";try{let n=await fetch(E("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t)));if(!n.ok)throw new Error(await n.text());let i=await n.json();on(t,i.diff,i.stagedDiff)}catch(n){let i=n instanceof Error?n.message:String(n);ve.textContent="Error",ye.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${f(i)}</div>`}}else{g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
20
22
  <button class="diff-back">\u2190 Back</button>
21
23
  <span class="diff-filename">${f(t)}</span>
22
24
  <span class="diff-loading">Loading\u2026</span>
23
- </div></div>`,Be(),g.classList.add("open"),ve.classList.add("open");try{let n=await fetch(E("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t)));if(!n.ok)throw new Error(await n.text());let a=await n.json();an(t,a.diff,a.stagedDiff)}catch(n){let a=n instanceof Error?n.message:String(n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
25
+ </div></div>`,Se(),g.classList.add("open"),ge.classList.add("open");try{let n=await fetch(E("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t)));if(!n.ok)throw new Error(await n.text());let i=await n.json();sn(t,i.diff,i.stagedDiff)}catch(n){let i=n instanceof Error?n.message:String(n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
26
  <button class="diff-back">\u2190 Back</button>
25
27
  <span class="diff-filename">${f(t)}</span>
26
- </div><div class="diff-empty">Error loading diff: ${f(a)}</div></div>`}}}function an(t,e,n){if(!e&&!n){g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
28
+ </div><div class="diff-empty">Error loading diff: ${f(i)}</div></div>`}}}function sn(t,e,n){if(!e&&!n){g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
27
29
  <button class="diff-back">\u2190 Back</button>
28
30
  <span class="diff-filename">${f(t)}</span>
29
- </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:a,lineCount:o}=ct(e,n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
31
+ </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:i,lineCount:o}=dt(e,n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
30
32
  <button class="diff-back">\u2190 Back</button>
31
33
  <span class="diff-filename">${f(t)}</span>
32
34
  <span class="diff-filesize">${o} lines</span>
33
- </div><div class="diff-content">${a}</div></div>`,g.querySelector(".diff-back")?.addEventListener("click",()=>{g.innerHTML=lt,Be(),v&&g.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),l=v.files[c];!l||l.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>dt(l.path)))}),ot()})}function sn(t,e,n){if(!e&&!n){me.textContent="No diff",he.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:a,lineCount:o}=ct(e,n);st.textContent=t,me.textContent=o+" lines",he.innerHTML=a}function on(){at.style.display="none",ye?(k.style.display="",k.classList.add("terminal-pending"),Q(ye),ye=null):y?(k.style.display="",k.classList.add("terminal-pending"),pt()):R.style.display="flex"}tn.addEventListener("click",on);function ge(){g.classList.remove("open"),ve.classList.remove("open")}ve.addEventListener("click",ge),document.addEventListener("keydown",t=>{t.key==="Escape"&&(ge(),Ee())});async function ut(t){try{let e=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(t))).json();if(e.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(e.path));if(n.ok){let a=await n.json();v=a,te(a);return}}}catch{}v=null,te(null)}var we=null;function pt(){be(),y&&(ut(y),we=setInterval(()=>{y&&ut(y)},3e4))}function be(){we&&(clearInterval(we),we=null)}function ln(){if(fe=Y.fsRoots||[],fe.length===0){if(y){rn(y);return}m.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}m.innerHTML="";for(let t of fe){let e=document.createElement("div");e.className="file-tree-item",e.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> ${f(t)}`,e.addEventListener("click",()=>H(t)),m.appendChild(e)}}async function rn(t){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let e=await(await fetch(E("/api/fs/session-path?session="+encodeURIComponent(t)))).json();e.path?H(e.path):m.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{m.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function H(t){S=t,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let e=Qt(t);try{let n=await(await fetch(E("/api/fs/list?path="+encodeURIComponent(t)))).json();await e,m.innerHTML="",te(v),m.innerHTML+=en();let a=cn(t);if(a&&!fe.some(r=>t===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>H(a)),m.appendChild(r)}if(n.dirs)for(let r of n.dirs){let c=r.split("/").pop()||r.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.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> ${f(c)}`,l.addEventListener("click",()=>H(r)),m.appendChild(l)}else m.innerHTML="";let o=n.files||[];for(let r of o){let c=r.split("/").pop()||r.split("\\").pop(),l=Gt(r),u="",p="";if(l)switch(l){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 I=document.createElement("div");I.className="file-tree-item file"+(u?" "+u:""),I.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${p}${f(c)}`,I.addEventListener("click",()=>dn(r)),m.appendChild(I)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function cn(t){let e=t.lastIndexOf("/");return e<=0?null:t.substring(0,e)}async function dn(t){ue=t,k.style.display="none",h&&(be(),h.destroy(),h=null,y=null),te(null),Ie(),R.style.display="none",Z.style.display="flex",Ut.textContent=t,T.textContent="Loading...",A.style.display="none";try{let e=await fetch(E("/api/file?path="+encodeURIComponent(t)));if(!e.ok){let a=await e.json();T.textContent=a.error||"Failed to load",G.value="";return}let n=await e.json();G.value=n.content,pe=n.content,Je=n.content,T.textContent=n.size+" bytes",A.style.display=""}catch{T.textContent="Failed to load",G.value=""}}G.addEventListener("input",()=>{pe=G.value}),A.addEventListener("click",async()=>{A.textContent="Saving...",A.disabled=!0;try{let t=await fetch(E("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:ue,content:pe})});if(!t.ok){let e=await t.json();T.textContent=e.error||"Save failed";return}Je=pe,T.textContent="Saved"}catch{T.textContent="Save failed"}finally{A.disabled=!1,A.textContent="Save"}}),Ot.addEventListener("click",async()=>{if(confirm("Delete "+ue+"?"))try{let t=await fetch(E("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:ue})});if(!t.ok){let e=await t.json();T.textContent=e.error||"Delete failed";return}Z.style.display="none",R.style.display="flex",S&&H(S)}catch{T.textContent="Delete failed"}}),qt.addEventListener("click",()=>{Z.style.display="none",R.style.display="flex",S&&H(S)}),Xt.addEventListener("click",async()=>{let t=Ze.value.trim();if(!t)return;let e=S+"/"+t;try{let n=await fetch(E("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})});if(!n.ok){let a=await n.json();T.textContent=a.error||"Create failed";return}Ze.value="",S&&H(S)}catch{T.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let t=Ge.classList.toggle("open");Me.classList.toggle("open",t)}),Me.addEventListener("click",()=>{Ge.classList.remove("open"),Me.classList.remove("open")});var $="",ne=document.createElement("div");ne.className="session-popover-backdrop",document.body.appendChild(ne);var B=document.createElement("div");B.id="session-popover",B.className="session-popover",B.style.display="none",B.innerHTML=`<div class="sp-field">
35
+ </div><div class="diff-content">${i}</div></div>`,g.querySelector(".diff-back")?.addEventListener("click",()=>{g.innerHTML=rt,Se(),v&&g.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),l=v.files[c];!l||l.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>ut(l.path)))}),lt()})}function on(t,e,n){if(!e&&!n){ve.textContent="No diff",ye.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:i,lineCount:o}=dt(e,n);ot.textContent=t,ve.textContent=o+" lines",ye.innerHTML=i}function ln(){st.style.display="none",we?(k.style.display="",k.classList.add("terminal-pending"),ee(we),we=null):y?(k.style.display="",k.classList.add("terminal-pending"),ft()):R.style.display="flex"}nn.addEventListener("click",ln);function be(){g.classList.remove("open"),ge.classList.remove("open")}ge.addEventListener("click",be),document.addEventListener("keydown",t=>{t.key==="Escape"&&(be(),ke())});async function pt(t){try{let e=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(t))).json();if(e.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(e.path));if(n.ok){let i=await n.json();v=i,ne(i);return}}}catch{}v=null,ne(null)}var Ee=null;function ft(){Le(),y&&(pt(y),Ee=setInterval(()=>{y&&pt(y)},3e4))}function Le(){Ee&&(clearInterval(Ee),Ee=null)}function rn(){if(he=Y.fsRoots||[],he.length===0){if(y){cn(y);return}m.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}m.innerHTML="";for(let t of he){let e=document.createElement("div");e.className="file-tree-item",e.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> ${f(t)}`,e.addEventListener("click",()=>H(t)),m.appendChild(e)}}async function cn(t){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let e=await(await fetch(E("/api/fs/session-path?session="+encodeURIComponent(t)))).json();e.path?H(e.path):m.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{m.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function H(t){S=t,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let e=en(t);try{let n=await(await fetch(E("/api/fs/list?path="+encodeURIComponent(t)))).json();await e,m.innerHTML="",ne(v),m.innerHTML+=tn();let i=dn(t);if(i&&!he.some(r=>t===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>H(i)),m.appendChild(r)}if(n.dirs)for(let r of n.dirs){let c=r.split("/").pop()||r.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.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> ${f(c)}`,l.addEventListener("click",()=>H(r)),m.appendChild(l)}else m.innerHTML="";let o=n.files||[];for(let r of o){let c=r.split("/").pop()||r.split("\\").pop(),l=Qt(r),u="",p="";if(l)switch(l){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 B=document.createElement("div");B.className="file-tree-item file"+(u?" "+u:""),B.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${p}${f(c)}`,B.addEventListener("click",()=>un(r)),m.appendChild(B)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function dn(t){let e=t.lastIndexOf("/");return e<=0?null:t.substring(0,e)}async function un(t){fe=t,k.style.display="none",h&&(Le(),h.destroy(),h=null,y=null),ne(null),Ie(),R.style.display="none",G.style.display="flex",Ot.textContent=t,T.textContent="Loading...",A.style.display="none";try{let e=await fetch(E("/api/file?path="+encodeURIComponent(t)));if(!e.ok){let i=await e.json();T.textContent=i.error||"Failed to load",Q.value="";return}let n=await e.json();Q.value=n.content,me=n.content,Ze=n.content,T.textContent=n.size+" bytes",A.style.display=""}catch{T.textContent="Failed to load",Q.value=""}}Q.addEventListener("input",()=>{me=Q.value}),A.addEventListener("click",async()=>{A.textContent="Saving...",A.disabled=!0;try{let t=await fetch(E("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:fe,content:me})});if(!t.ok){let e=await t.json();T.textContent=e.error||"Save failed";return}Ze=me,T.textContent="Saved"}catch{T.textContent="Save failed"}finally{A.disabled=!1,A.textContent="Save"}}),qt.addEventListener("click",async()=>{if(confirm("Delete "+fe+"?"))try{let t=await fetch(E("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:fe})});if(!t.ok){let e=await t.json();T.textContent=e.error||"Delete failed";return}G.style.display="none",R.style.display="flex",S&&H(S)}catch{T.textContent="Delete failed"}}),Xt.addEventListener("click",()=>{G.style.display="none",R.style.display="flex",S&&H(S)}),Jt.addEventListener("click",async()=>{let t=Ge.value.trim();if(!t)return;let e=S+"/"+t;try{let n=await fetch(E("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})});if(!n.ok){let i=await n.json();T.textContent=i.error||"Create failed";return}Ge.value="",S&&H(S)}catch{T.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let t=Qe.classList.toggle("open");Be.classList.toggle("open",t)}),Be.addEventListener("click",()=>{Qe.classList.remove("open"),Be.classList.remove("open")});var $="",ie=document.createElement("div");ie.className="session-popover-backdrop",document.body.appendChild(ie);var I=document.createElement("div");I.id="session-popover",I.className="session-popover",I.style.display="none",I.innerHTML=`<div class="sp-field">
34
36
  <label>Rename session</label>
35
37
  <input type="text" id="sp-name" />
36
38
  </div>
@@ -39,5 +41,5 @@ var jt="https://esm.sh/ghostty-web@0.4.0",Vt=/\x1b\](?:10|11|110|111|104)(?:;[^\
39
41
  <button class="btn" id="sp-cancel">Cancel</button>
40
42
  </div>
41
43
  <hr class="sp-divider" />
42
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(B);function un(t){let e=t.getBoundingClientRect();B.style.top=e.bottom+4+"px",B.style.left=Math.max(8,Math.min(e.left,window.innerWidth-240))+"px"}function pn(t,e){$=t;let n=document.getElementById("sp-name");n.value=t,ne.classList.add("open"),B.style.display="block",un(e),setTimeout(()=>n.focus(),50)}function ie(){ne.classList.remove("open"),B.style.display="none"}ne.addEventListener("click",ie),document.getElementById("sp-save").addEventListener("click",async()=>{let t=document.getElementById("sp-name").value.trim();if(!t||t===$){ie();return}try{if(!(await fetch(E("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:$,newName:t})})).ok)return;ie(),W(),y===$&&Q(t)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+$+'"?'))try{if(!(await fetch(E("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:$})})).ok)return;ie(),y===$&&(h&&(h.destroy(),h=null),y=null,k.style.display="none",R.style.display="flex"),W()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",ie);var fn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",t=>{let e=t.target.closest("button[data-key]");if(!e||!h)return;let n=fn[e.dataset.key];n&&(h.sendInput(n),h.focus())});var M=document.getElementById("mk-input"),ft=document.getElementById("mk-send");function mt(){if(!M||!h)return;let t=M.value;t&&(h.sendInput(t+"\r"),M.value="",M.rows=1,h.focus())}if(M&&(M.addEventListener("input",()=>{M.rows=1;let t=M.value.split(`
43
- `).length;M.rows=Math.min(t,4)}),M.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),mt())})),ft&&ft.addEventListener("click",mt),window.visualViewport){let t=document.querySelector(".app-layout"),e=()=>{t&&(t.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",e),window.addEventListener("orientationchange",()=>setTimeout(e,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Se=document.getElementById("process-panel"),ae=document.getElementById("proc-list"),mn=document.getElementById("proc-close"),He=document.getElementById("proc-backdrop"),hn=document.getElementById("header-ram");function vn(){ae.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(E("/api/system/processes")).then(t=>t.json()).then(t=>{if(!Array.isArray(t)||!t.length){ae.innerHTML='<div class="proc-empty">No processes</div>';return}let e="";for(let n of t){let a=n.rss,o=a<1048576?(a/1024).toFixed(0)+"K":(a/1048576).toFixed(1)+"M";e+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+f(n.command)+'">'+f(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}ae.innerHTML=e;for(let n of ae.querySelectorAll(".proc-row")){let a=n.querySelector(".proc-kill");a&&a.addEventListener("click",o=>{let r=o.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(E("/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(()=>{ae.innerHTML='<div class="proc-empty">Failed to load</div>'})}function yn(){Se.classList.add("open"),He.classList.add("open"),vn()}function Ee(){Se.classList.remove("open"),He.classList.remove("open")}hn.addEventListener("click",t=>{t.stopPropagation(),Se.classList.contains("open")?Ee():yn()}),mn.addEventListener("click",Ee),He.addEventListener("click",Ee),window.__openSession=Q,window.__refreshSidebar=W,W(),Ie(),setInterval(()=>{document.visibilityState==="visible"&&W()},1e4);
44
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(I);function pn(t){let e=t.getBoundingClientRect();I.style.top=e.bottom+4+"px",I.style.left=Math.max(8,Math.min(e.left,window.innerWidth-240))+"px"}function fn(t,e){$=t;let n=document.getElementById("sp-name");n.value=t,ie.classList.add("open"),I.style.display="block",pn(e),setTimeout(()=>n.focus(),50)}function ae(){ie.classList.remove("open"),I.style.display="none"}ie.addEventListener("click",ae),document.getElementById("sp-save").addEventListener("click",async()=>{let t=document.getElementById("sp-name").value.trim();if(!t||t===$){ae();return}try{if(!(await fetch(E("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:$,newName:t})})).ok)return;ae(),W(),y===$&&ee(t)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+$+'"?'))try{if(!(await fetch(E("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:$})})).ok)return;ae(),y===$&&(h&&(h.destroy(),h=null),y=null,k.style.display="none",R.style.display="flex"),W()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",ae);var mn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",t=>{let e=t.target.closest("button[data-key]");if(!e||!h)return;let n=mn[e.dataset.key];n&&(h.sendInput(n),h.focus())});var M=document.getElementById("mk-input"),mt=document.getElementById("mk-send");function ht(){if(!M||!h)return;let t=M.value;t&&(h.sendInput(t+"\r"),M.value="",M.rows=1,h.focus())}if(M&&(M.addEventListener("input",()=>{M.rows=1;let t=M.value.split(`
45
+ `).length;M.rows=Math.min(t,4)}),M.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),ht())})),mt&&mt.addEventListener("click",ht),window.visualViewport){let t=document.querySelector(".app-layout"),e=()=>{t&&(t.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",e),window.addEventListener("orientationchange",()=>setTimeout(e,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var He=document.getElementById("process-panel"),se=document.getElementById("proc-list"),hn=document.getElementById("proc-close"),De=document.getElementById("proc-backdrop"),vn=document.getElementById("header-ram");function yn(){se.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(E("/api/system/processes")).then(t=>t.json()).then(t=>{if(!Array.isArray(t)||!t.length){se.innerHTML='<div class="proc-empty">No processes</div>';return}let e="";for(let n of t){let i=n.rss,o=i<1048576?(i/1024).toFixed(0)+"K":(i/1048576).toFixed(1)+"M";e+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+f(n.command)+'">'+f(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}se.innerHTML=e;for(let n of se.querySelectorAll(".proc-row")){let i=n.querySelector(".proc-kill");i&&i.addEventListener("click",o=>{let r=o.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(E("/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(()=>{se.innerHTML='<div class="proc-empty">Failed to load</div>'})}function gn(){He.classList.add("open"),De.classList.add("open"),yn()}function ke(){He.classList.remove("open"),De.classList.remove("open")}vn.addEventListener("click",t=>{t.stopPropagation(),He.classList.contains("open")?ke():gn()}),hn.addEventListener("click",ke),De.addEventListener("click",ke),window.__openSession=ee,window.__refreshSidebar=W,W(),Ie(),setInterval(()=>{document.visibilityState==="visible"&&W()},1e4);
@@ -1,6 +1,8 @@
1
- const kt="https://esm.sh/ghostty-web@0.4.0",Yt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function I(s){return s.replace(Yt,"")}let Y;function Pt(){return Y||(document.querySelector("link[data-tmux-web-xterm-css]")?Y=Promise.resolve():(Y=new Promise(e=>{const i=document.createElement("link");i.rel="stylesheet",i.href="/assets/xterm.css",i.setAttribute("data-tmux-web-xterm-css",""),i.addEventListener("load",()=>e(),{once:!0}),i.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(i)}),Y))}class K{terminal;fitAddon;container;constructor(e,i,r){this.container=e,this.terminal=i,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,i,r){const[{Terminal:a},{FitAddon:c}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Pt()]),d=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new K(e,d,new c)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const e=it(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){const e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(e,i){const r=this.terminal.buffer.active,[a,c]=e<=i?[e,i]:[i,e],d=r.viewportY,l=[];for(let m=a;m<=c;m++){const u=r.getLine(d+m);l.push(u?u.translateToString(!0):"")}return l.join(`
2
- `)}selectRows(e,i){const r=this.terminal.buffer.active,[a,c]=e<=i?[e,i]:[i,e];this.terminal.selectLines(r.viewportY+a,r.viewportY+c)}hasSelectionSupport(){return!0}}class U{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,i){this.container=e,this.terminal=i,this.terminal.open(e)}static async create(e,i,r){const a=await import(kt);await a.init();const c=new a.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new U(e,c)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){const e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){const e=it(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);const i=Math.floor(e.width/this.charW),r=Math.floor(e.height/this.charH);if(i<10||r<5)return!1;if(i!==this.colsValue||r!==this.rowsValue){this.colsValue=i,this.rowsValue=r,this.terminal.resize(i,r);for(const a of this.resizeCallbacks)a({cols:i,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){const i=this.terminal.getMode?.(2004)??!1;this.input(i?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(i=>e(i))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){const e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.rowsValue-1)}getRowsText(e,i){try{const r=this.terminal.buffer?.active;if(!r||typeof r.getLine!="function")return"";const[a,c]=e<=i?[e,i]:[i,e],d=typeof r.viewportY=="number"?r.viewportY:0,l=[];for(let m=a;m<=c;m++){const u=r.getLine(d+m);l.push(typeof u?.translateToString=="function"?u.translateToString(!0):"")}return l.join(`
3
- `)}catch{return""}}selectRows(e,i){try{if(typeof this.terminal?.selectLines!="function")return;const r=this.terminal.buffer?.active,a=typeof r?.viewportY=="number"?r.viewportY:0,[c,d]=e<=i?[e,i]:[i,e];this.terminal.selectLines(a+c,a+d)}catch{}}hasSelectionSupport(){return typeof this.terminal?.selectLines=="function"}updateCellMetrics(e=!1){const i=this.container.querySelector("canvas");if(i instanceof HTMLElement&&i.offsetWidth>0&&i.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){const r=i.offsetWidth/this.colsValue,a=i.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=r,this.charH=a)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}}function it(s){const e=s.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(e.width,i.width),height:Math.max(0,Math.min(e.height,i.height-Math.max(0,e.top)))}:e}function Ft(s,e,i){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{let r;if(i.renderer==="ghostty")try{r=await U.create(s,i.scrollback,i.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),r=await K.create(s,i.scrollback,i.theme)}else r=await K.create(s,i.scrollback,i.theme);let a,c=0,d,l=null,m=0,u="connecting",P=0,F=!1,B=[],g="",v=!1,T=1e3;const ot=location.protocol==="https:"?"wss:":"ws:",st=i.wsBase||"/ws",at=ot+"//"+location.host+st+"/"+encodeURIComponent(e),lt="/api/session/"+encodeURIComponent(e)+"/upload",ct=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function ut(){return B.join("")+g}async function ft(t,n=0){const o=t?r.viewportY():0,h=t?r.baseY():0,w=I(ut());if(r.reset(),!w){t||r.scrollToBottom();return}await r.write(w),t?r.scrollToLine(h+o+n):r.scrollToBottom()}function x(t){a?.readyState===WebSocket.OPEN&&a.send(JSON.stringify(t))}function ht(t){let n;try{n=JSON.parse(t)}catch{u==="live"&&(g+=t,r.write(I(t)));return}if(n.type==="auth.required"){const o=localStorage.getItem("tmux-web-token");o?x({type:"auth.token",token:o}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(n.type==="auth.ok"){n.token&&localStorage.setItem("tmux-web-token",n.token),T=1e3,E(),x({type:"resize",cols:r.cols,rows:r.rows}),f();return}if(n.type==="auth.failed"){if(n.permanentLock||n.retryAfterMs&&n.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(n.message);return}return}if(n.type==="snapshot"&&typeof n.data=="string"){const o=I(n.data);B=[o],g="",P=typeof n.lines=="number"?n.lines:i.terminal.initialLines,u="live",r.reset(),r.write(o).then(()=>r.scrollToBottom());return}if(n.type==="window_changed"&&typeof n.activeIndex=="number"){const o=new URL(location.href);o.searchParams.get("window")!==String(n.activeIndex)&&(o.searchParams.set("window",String(n.activeIndex)),history.replaceState(history.state,"",o)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:n.windows}));return}if(n.type==="history"&&typeof n.data=="string"){F=!1,n.lines>0&&n.data&&(B.unshift(n.data),P+=n.lines,ft(!0,n.lines));return}if(n.type==="data"&&typeof n.data=="string"){const o=I(n.data);if(u==="connecting"){u="live",g=o,r.write(o);return}u==="live"&&(g+=o,r.write(o))}}function X(){let t=at;const n=localStorage.getItem("tmux-web-token");n&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(n)),a=new WebSocket(t),a.onopen=()=>{u="connecting",P=0,F=!1,B=[],g="",r.reset(),T=1e3},a.onmessage=o=>{typeof o.data=="string"&&ht(o.data)},a.onclose=()=>{v||(u="connecting",setTimeout(()=>{T=Math.min(T*2,1e4),v||X()},T))},a.onerror=()=>a?.close()}function H(t){x({type:"input",data:t})}function E(){return r.fit()}function V(){s.classList.remove("terminal-pending")}function f(){c&&cancelAnimationFrame(c),d&&clearTimeout(d),E(),c=requestAnimationFrame(()=>{c=0,E()&&V()}),d=setTimeout(()=>{E()&&V()},120)}function dt(){f(),setTimeout(f,50),setTimeout(f,150),setTimeout(f,300)}function j(t){return typeof t=="string"&&t.startsWith("image/")}function q(t){return t?.files?.length?t.files[0]??null:null}function mt(t){if(!t?.items)return null;for(let n=0;n<t.items.length;n++){const o=t.items[n];if(o?.kind==="file"&&j(o.type))return o.getAsFile()}return null}async function pt(t){const n=new FormData,o=t instanceof File&&t.name?t.name:"upload";n.append("file",t,o);const h=localStorage.getItem("tmux-web-token"),w={};h&&(w.Authorization="Bearer "+h);const O=await fetch(lt,{method:"POST",body:n,headers:w});if(!O.ok){let nt="upload failed";try{const rt=await O.json();rt.error&&(nt=rt.error)}catch{}throw new Error(nt)}const et=await O.json();if(!et.path)throw new Error("no path in response");return et.path}async function A(t){try{const n=await pt(t);H(n)}catch{}}r.onData(t=>H(t)),r.onResize(({cols:t,rows:n})=>x({type:"resize",cols:t,rows:n})),r.onScroll(()=>{u!=="live"||F||!r.isNearScrollbackTop()||(F=!0,x({type:"load_history",before:P}))});let L=!1,S=!1,p=null,y=null;const C=document.createElement("div");C.className="terminal-copy-banner",C.innerHTML='<strong>Copy mode</strong><span class="hint">drag to select \xB7 Esc exits</span>',C.hidden=!0,s.appendChild(C);const b=document.createElement("div");b.className="terminal-copy-banner terminal-copy-toast",b.textContent="Copied",b.hidden=!0,s.appendChild(b);function N(t){const n=s.getBoundingClientRect(),o=n.height>0&&r.rows>0?n.height/r.rows:18;return Math.max(0,Math.min(r.rows-1,Math.floor((t-n.top)/o)))}async function _(t){if(!t)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(t);return}}catch{}const n=document.createElement("textarea");n.value=t,n.style.position="fixed",n.style.opacity="0",n.style.pointerEvents="none",document.body.appendChild(n),n.focus(),n.select();try{document.execCommand("copy")}catch{}n.remove()}let D;function J(){b.hidden=!1,D&&clearTimeout(D),D=setTimeout(()=>{b.hidden=!0},1e3)}function M(t){L=t,C.hidden=!t,s.classList.toggle("terminal-copy-mode-active",t),t&&r.focus()}function G(){M(!L)}function wt(){return p===null||y===null?null:p<=y?[p,y]:[y,p]}function yt(t){v||t.button!==0||S||(L||t.shiftKey)&&(t.preventDefault(),t.stopPropagation(),S=!0,p=N(t.clientY),y=p,r.selectRows(p,p),document.addEventListener("mousemove",W,!0),document.addEventListener("mouseup",R,!0))}function W(t){v||!S||p===null||(t.preventDefault(),t.stopPropagation(),y=N(t.clientY),r.selectRows(p,y))}function R(t){if(v||!S||p===null)return;t.preventDefault(),t.stopPropagation();const n=wt();document.removeEventListener("mousemove",W,!0),document.removeEventListener("mouseup",R,!0),S=!1,M(!1),n&&(_(r.getRowsText(n[0],n[1])).then(J),r.focus())}s.addEventListener("mousedown",yt,!0),r.attachCustomKeyEventHandler(t=>{if(t.type!=="keydown")return!0;if(L){if(t.key==="Escape")return t.preventDefault(),M(!1),!1;if(t.key==="Enter"||(t.ctrlKey||t.metaKey)&&t.code==="KeyC")return t.preventDefault(),_(r.getScreenText()).then(J),M(!1),!1}return t.altKey&&t.code==="KeyC"&&!t.ctrlKey&&!t.metaKey?(t.preventDefault(),G(),!1):(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){const n=await navigator.clipboard.read();for(const o of n)for(const h of o.types)if(j(h)){const w=await o.getType(h);await A(w);return}}}catch{}try{const n=await navigator.clipboard?.readText();n&&r.pasteText(Q(n))}catch{}})(),!1):!0});let z=0;function Q(t){return t.split(`\r
1
+ const Yt="https://esm.sh/ghostty-web@0.4.0",Pt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Y(s){return s.replace(Pt,"")}let P;function Ft(){return P||(document.querySelector("link[data-tmux-web-xterm-css]")?P=Promise.resolve():(P=new Promise(n=>{const i=document.createElement("link");i.rel="stylesheet",i.href="/assets/xterm.css",i.setAttribute("data-tmux-web-xterm-css",""),i.addEventListener("load",()=>n(),{once:!0}),i.addEventListener("error",()=>n(),{once:!0}),document.head.appendChild(i)}),P))}class A{terminal;fitAddon;container;constructor(n,i,r){this.container=n,this.terminal=i,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(n)}static async create(n,i,r){const[{Terminal:a},{FitAddon:c}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Ft()]),d=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new A(n,d,new c)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(n){return new Promise(i=>this.terminal.write(n,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(n){this.terminal.scrollToLine(Math.max(0,n))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const n=ot(this.container);return n.width<=0||n.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(n){this.terminal.paste(n)}input(n){this.terminal.input(n,!1)}onData(n){this.terminal.onData(n),this.terminal.onBinary(n)}onResize(n){this.terminal.onResize(n)}onScroll(n){this.terminal.onScroll(n)}attachCustomKeyEventHandler(n){this.terminal.attachCustomKeyEventHandler(n)}isFocused(){const n=document.activeElement;return!!n&&(n===this.container||this.container.contains(n))}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(n,i){const r=this.terminal.buffer.active,[a,c]=n<=i?[n,i]:[i,n],d=r.viewportY,l=[];for(let m=a;m<=c;m++){const u=r.getLine(d+m);l.push(u?u.translateToString(!0):"")}return l.join(`
2
+ `)}selectRows(n,i){const r=this.terminal.buffer.active,[a,c]=n<=i?[n,i]:[i,n];this.terminal.selectLines(r.viewportY+a,r.viewportY+c)}hasSelectionSupport(){return!0}}class X{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(n,i){this.container=n,this.terminal=i,this.terminal.open(n)}static async create(n,i,r){const a=await import(Yt);await a.init();const c=new a.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new X(n,c)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(n){return new Promise(i=>this.terminal.write(n,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(n){this.terminal.scrollToLine?.(Math.max(0,n))}isNearScrollbackTop(){const n=this.terminal.buffer?.active;return n?n.viewportY>=n.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){const n=ot(this.container);if(n.width<=0||n.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);const i=Math.floor(n.width/this.charW),r=Math.floor(n.height/this.charH);if(i<10||r<5)return!1;if(i!==this.colsValue||r!==this.rowsValue){this.colsValue=i,this.rowsValue=r,this.terminal.resize(i,r);for(const a of this.resizeCallbacks)a({cols:i,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(n){const i=this.terminal.getMode?.(2004)??!1;this.input(i?"\x1B[200~"+n+"\x1B[201~":n)}input(n){this.terminal.input?.(n,!1)}onData(n){this.terminal.onData(i=>n(i))}onResize(n){this.resizeCallbacks.push(n)}onScroll(n){this.terminal.onScroll(n)}attachCustomKeyEventHandler(n){this.terminal.attachCustomKeyEventHandler(n)}isFocused(){const n=document.activeElement;return!!n&&(n===this.container||n===this.terminal.textarea||this.container.contains(n))}getScreenText(){return this.getRowsText(0,this.rowsValue-1)}getRowsText(n,i){try{const r=this.terminal.buffer?.active;if(!r||typeof r.getLine!="function")return"";const[a,c]=n<=i?[n,i]:[i,n],d=typeof r.viewportY=="number"?r.viewportY:0,l=[];for(let m=a;m<=c;m++){const u=r.getLine(d+m);l.push(typeof u?.translateToString=="function"?u.translateToString(!0):"")}return l.join(`
3
+ `)}catch{return""}}selectRows(n,i){try{if(typeof this.terminal?.selectLines!="function")return;const r=this.terminal.buffer?.active,a=typeof r?.viewportY=="number"?r.viewportY:0,[c,d]=n<=i?[n,i]:[i,n];this.terminal.selectLines(a+c,a+d)}catch{}}hasSelectionSupport(){return typeof this.terminal?.selectLines=="function"}updateCellMetrics(n=!1){const i=this.container.querySelector("canvas");if(i instanceof HTMLElement&&i.offsetWidth>0&&i.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){const r=i.offsetWidth/this.colsValue,a=i.offsetHeight/this.rowsValue;(n||!this.charW||!this.charH)&&(this.charW=r,this.charH=a)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}}function ot(s){const n=s.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(n.width,i.width),height:Math.max(0,Math.min(n.height,i.height-Math.max(0,n.top)))}:n}function Bt(s,n,i){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{let r;if(i.renderer==="ghostty")try{r=await X.create(s,i.scrollback,i.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),r=await A.create(s,i.scrollback,i.theme)}else r=await A.create(s,i.scrollback,i.theme);let a,c=0,d,l=null,m=0,u="connecting",F=0,B=!1,H=[],g="",v=!1,T=1e3;const st=location.protocol==="https:"?"wss:":"ws:",at=i.wsBase||"/ws",lt=st+"//"+location.host+at+"/"+encodeURIComponent(n),ct="/api/session/"+encodeURIComponent(n)+"/upload",ut=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function ft(){return H.join("")+g}async function ht(t,e=0){const o=t?r.viewportY():0,h=t?r.baseY():0,w=Y(ft());if(r.reset(),!w){t||r.scrollToBottom();return}await r.write(w),t?r.scrollToLine(h+o+e):r.scrollToBottom()}function x(t){a?.readyState===WebSocket.OPEN&&a.send(JSON.stringify(t))}function dt(t){let e;try{e=JSON.parse(t)}catch{u==="live"&&(g+=t,r.write(Y(t)));return}if(e.type==="auth.required"){const o=localStorage.getItem("tmux-web-token");o?x({type:"auth.token",token:o}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="attach_failed"&&typeof e.message=="string"){z=!0,u="dead",r.reset(),r.write(`\r
4
+ \x1B[31m`+Y(e.message)+`\x1B[0m\r
5
+ `).then(()=>{K();try{a?.close()}catch{}});return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),T=1e3,E(),x({type:"resize",cols:r.cols,rows:r.rows}),f();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 o=Y(e.data);H=[o],g="",F=typeof e.lines=="number"?e.lines:i.terminal.initialLines,u="live",r.reset(),r.write(o).then(()=>r.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){const o=new URL(location.href);o.searchParams.get("window")!==String(e.activeIndex)&&(o.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",o)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){B=!1,e.lines>0&&e.data&&(H.unshift(e.data),F+=e.lines,ht(!0,e.lines));return}if(e.type==="data"&&typeof e.data=="string"){const o=Y(e.data);if(u==="connecting"){u="live",g=o,r.write(o);return}u==="live"&&(g+=o,r.write(o))}}let z=!1;function j(){if(z)return;let t=lt;const e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),a=new WebSocket(t),a.onopen=()=>{u="connecting",F=0,B=!1,H=[],g="",r.reset(),T=1e3},a.onmessage=o=>{typeof o.data=="string"&&dt(o.data)},a.onclose=()=>{v||(u="connecting",!z&&setTimeout(()=>{T=Math.min(T*2,1e4),!v&&!z&&j()},T))},a.onerror=()=>a?.close()}function I(t){x({type:"input",data:t})}function E(){return r.fit()}function K(){s.classList.remove("terminal-pending")}function f(){c&&cancelAnimationFrame(c),d&&clearTimeout(d),E(),c=requestAnimationFrame(()=>{c=0,E()&&K()}),d=setTimeout(()=>{E()&&K()},120)}function mt(){f(),setTimeout(f,50),setTimeout(f,150),setTimeout(f,300)}function q(t){return typeof t=="string"&&t.startsWith("image/")}function N(t){return t?.files?.length?t.files[0]??null:null}function pt(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){const o=t.items[e];if(o?.kind==="file"&&q(o.type))return o.getAsFile()}return null}async function wt(t){const e=new FormData,o=t instanceof File&&t.name?t.name:"upload";e.append("file",t,o);const h=localStorage.getItem("tmux-web-token"),w={};h&&(w.Authorization="Bearer "+h);const U=await fetch(ct,{method:"POST",body:e,headers:w});if(!U.ok){let rt="upload failed";try{const it=await U.json();it.error&&(rt=it.error)}catch{}throw new Error(rt)}const nt=await U.json();if(!nt.path)throw new Error("no path in response");return nt.path}async function W(t){try{const e=await wt(t);I(e)}catch{}}r.onData(t=>I(t)),r.onResize(({cols:t,rows:e})=>x({type:"resize",cols:t,rows:e})),r.onScroll(()=>{u!=="live"||B||!r.isNearScrollbackTop()||(B=!0,x({type:"load_history",before:F}))});let L=!1,S=!1,p=null,y=null;const D=document.createElement("div");D.className="terminal-copy-banner",D.innerHTML='<strong>Copy mode</strong><span class="hint">drag to select \xB7 Esc exits</span>',D.hidden=!0,s.appendChild(D);const b=document.createElement("div");b.className="terminal-copy-banner terminal-copy-toast",b.textContent="Copied",b.hidden=!0,s.appendChild(b);function _(t){const e=s.getBoundingClientRect(),o=e.height>0&&r.rows>0?e.height/r.rows:18;return Math.max(0,Math.min(r.rows-1,Math.floor((t-e.top)/o)))}async function J(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 C;function G(){b.hidden=!1,C&&clearTimeout(C),C=setTimeout(()=>{b.hidden=!0},1e3)}function M(t){L=t,D.hidden=!t,s.classList.toggle("terminal-copy-mode-active",t),t&&r.focus()}function Q(){M(!L)}function yt(){return p===null||y===null?null:p<=y?[p,y]:[y,p]}function gt(t){v||t.button!==0||S||(L||t.shiftKey)&&(t.preventDefault(),t.stopPropagation(),S=!0,p=_(t.clientY),y=p,r.selectRows(p,p),document.addEventListener("mousemove",R,!0),document.addEventListener("mouseup",O,!0))}function R(t){v||!S||p===null||(t.preventDefault(),t.stopPropagation(),y=_(t.clientY),r.selectRows(p,y))}function O(t){if(v||!S||p===null)return;t.preventDefault(),t.stopPropagation();const e=yt();document.removeEventListener("mousemove",R,!0),document.removeEventListener("mouseup",O,!0),S=!1,M(!1),e&&(J(r.getRowsText(e[0],e[1])).then(G),r.focus())}s.addEventListener("mousedown",gt,!0),r.attachCustomKeyEventHandler(t=>{if(t.type!=="keydown")return!0;if(L){if(t.key==="Escape")return t.preventDefault(),M(!1),!1;if(t.key==="Enter"||(t.ctrlKey||t.metaKey)&&t.code==="KeyC")return t.preventDefault(),J(r.getScreenText()).then(G),M(!1),!1}return t.altKey&&t.code==="KeyC"&&!t.ctrlKey&&!t.metaKey?(t.preventDefault(),Q(),!1):(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){const e=await navigator.clipboard.read();for(const o of e)for(const h of o.types)if(q(h)){const w=await o.getType(h);await W(w);return}}}catch{}try{const e=await navigator.clipboard?.readText();e&&r.pasteText(Z(e))}catch{}})(),!1):!0});let V=0;function Z(t){return t.split(`\r
4
6
  `).join(`
5
7
  `).split(`
6
- `).join("\r")}async function gt(t){const n=mt(t.clipboardData);if(n){t.preventDefault(),t.stopPropagation();const w=Date.now();if(w-z<50)return;z=w,await A(n);return}const o=t.clipboardData?.getData("text/plain");if(!o)return;t.preventDefault(),t.stopPropagation();const h=Date.now();h-z<50||(z=h,r.pasteText(Q(o)))}let k=0;function vt(t){t.preventDefault(),k++,q(t.dataTransfer)&&s.classList.add("terminal-drag-over")}function bt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function Tt(t){t.preventDefault(),k--,k<=0&&(k=0,s.classList.remove("terminal-drag-over"))}async function xt(t){t.preventDefault(),k=0,s.classList.remove("terminal-drag-over");const n=q(t.dataTransfer);n&&await A(n)}function Z(t){!ct||t.key!=="Escape"||L||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||r.isFocused()&&(t.preventDefault(),t.stopPropagation(),H("\x1B"))}s.addEventListener("paste",gt),s.addEventListener("dragenter",vt),s.addEventListener("dragover",bt),s.addEventListener("dragleave",Tt),s.addEventListener("drop",xt),document.addEventListener("keydown",Z,!0);function Et(t){if(t.touches.length!==1){l=null;return}const n=t.touches[0];n&&(l={startX:n.clientX,startY:n.clientY,lastX:n.clientX,lastY:n.clientY,scrolling:!1},t.stopPropagation())}function Lt(t){if(!l||t.touches.length!==1)return;const n=t.touches[0];if(!n)return;const o=n.clientY-l.startY,h=n.clientX-l.startX;if(!l.scrolling){if(Math.abs(o)<8||Math.abs(o)<Math.abs(h))return;l.scrolling=!0,m=Date.now()+500}t.preventDefault(),t.stopPropagation(),Mt(-(n.clientY-l.lastY),n.clientX,n.clientY),l.lastX=n.clientX,l.lastY=n.clientY}function St(t){if(!l)return;const n=l.scrolling;l=null,t.stopPropagation(),n?(m=Date.now()+500,t.preventDefault()):r.focus()}function Ct(t){l=null,t.stopPropagation()}function Dt(t){t.pointerType==="touch"&&Date.now()<m&&(t.preventDefault(),t.stopPropagation())}function Mt(t,n,o){(s.querySelector(".xterm-screen")??s).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:n,clientY:o,bubbles:!0,cancelable:!0}))}s.addEventListener("touchstart",Et,{passive:!0,capture:!0}),s.addEventListener("touchmove",Lt,{passive:!1,capture:!0}),s.addEventListener("touchend",St,{passive:!1,capture:!0}),s.addEventListener("touchcancel",Ct,{passive:!0,capture:!0}),s.addEventListener("pointerup",Dt,!0);function $(){f()}window.addEventListener("resize",$),window.visualViewport?.addEventListener("resize",f),window.visualViewport?.addEventListener("scroll",f);const tt=new ResizeObserver(()=>f());return tt.observe(s),document.fonts?.ready.then(()=>{f(),setTimeout(f,50),setTimeout(f,200)}),s.addEventListener("focusin",dt,!0),requestAnimationFrame(()=>{E()&&V(),X(),f()}),{destroy(){if(v=!0,M(!1),D&&clearTimeout(D),document.removeEventListener("mousemove",W,!0),document.removeEventListener("mouseup",R,!0),a){try{a.close(1e3,"session switch")}catch{}a=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",$),tt.disconnect(),document.removeEventListener("keydown",Z,!0)},focus(){r.focus()},sendInput(t){H(t)},toggleCopyMode(){G()}}})()}export{Ft as initTerminal};
8
+ `).join("\r")}async function vt(t){const e=pt(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();const w=Date.now();if(w-V<50)return;V=w,await W(e);return}const o=t.clipboardData?.getData("text/plain");if(!o)return;t.preventDefault(),t.stopPropagation();const h=Date.now();h-V<50||(V=h,r.pasteText(Z(o)))}let k=0;function bt(t){t.preventDefault(),k++,N(t.dataTransfer)&&s.classList.add("terminal-drag-over")}function Tt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function xt(t){t.preventDefault(),k--,k<=0&&(k=0,s.classList.remove("terminal-drag-over"))}async function Et(t){t.preventDefault(),k=0,s.classList.remove("terminal-drag-over");const e=N(t.dataTransfer);e&&await W(e)}function $(t){!ut||t.key!=="Escape"||L||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||r.isFocused()&&(t.preventDefault(),t.stopPropagation(),I("\x1B"))}s.addEventListener("paste",vt),s.addEventListener("dragenter",bt),s.addEventListener("dragover",Tt),s.addEventListener("dragleave",xt),s.addEventListener("drop",Et),document.addEventListener("keydown",$,!0);function Lt(t){if(t.touches.length!==1){l=null;return}const e=t.touches[0];e&&(l={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,scrolling:!1},t.stopPropagation())}function St(t){if(!l||t.touches.length!==1)return;const e=t.touches[0];if(!e)return;const o=e.clientY-l.startY,h=e.clientX-l.startX;if(!l.scrolling){if(Math.abs(o)<8||Math.abs(o)<Math.abs(h))return;l.scrolling=!0,m=Date.now()+500}t.preventDefault(),t.stopPropagation(),kt(-(e.clientY-l.lastY),e.clientX,e.clientY),l.lastX=e.clientX,l.lastY=e.clientY}function Dt(t){if(!l)return;const e=l.scrolling;l=null,t.stopPropagation(),e?(m=Date.now()+500,t.preventDefault()):r.focus()}function Ct(t){l=null,t.stopPropagation()}function Mt(t){t.pointerType==="touch"&&Date.now()<m&&(t.preventDefault(),t.stopPropagation())}function kt(t,e,o){(s.querySelector(".xterm-screen")??s).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:e,clientY:o,bubbles:!0,cancelable:!0}))}s.addEventListener("touchstart",Lt,{passive:!0,capture:!0}),s.addEventListener("touchmove",St,{passive:!1,capture:!0}),s.addEventListener("touchend",Dt,{passive:!1,capture:!0}),s.addEventListener("touchcancel",Ct,{passive:!0,capture:!0}),s.addEventListener("pointerup",Mt,!0);function tt(){f()}window.addEventListener("resize",tt),window.visualViewport?.addEventListener("resize",f),window.visualViewport?.addEventListener("scroll",f);const et=new ResizeObserver(()=>f());return et.observe(s),document.fonts?.ready.then(()=>{f(),setTimeout(f,50),setTimeout(f,200)}),s.addEventListener("focusin",mt,!0),requestAnimationFrame(()=>{E()&&K(),j(),f()}),{destroy(){if(v=!0,M(!1),C&&clearTimeout(C),document.removeEventListener("mousemove",R,!0),document.removeEventListener("mouseup",O,!0),a){try{a.close(1e3,"session switch")}catch{}a=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",tt),et.disconnect(),document.removeEventListener("keydown",$,!0)},focus(){r.focus()},sendInput(t){I(t)},toggleCopyMode(){Q()}}})()}export{Bt as initTerminal};
@@ -1 +1 @@
1
- import{WebSocket as k}from"ws";import{attachTerminal as J}from"./attach-terminal.js";import{encodeBinaryFrame as j,decodeBinaryFrame as x,BIN_KIND_HTTP_BODY as A}from"./agent-channel.js";const K=parseInt(process.env.TMUX_WEB_AGENT_HEARTBEAT_MS||"15000",10),W=parseInt(process.env.TMUX_WEB_AGENT_SESSIONS_POLL_MS||"10000",10),F=new Set(["content-type","content-disposition","location","cache-control","etag"]);function Q(o){const T=o.log??(e=>console.log(`[agent] ${e}`)),g=e=>console.error(`[agent] ${e}`);let s=null,S=1e3,B=null,m=!1;const d=new Map,f=new Map,h=new Map,b=new Set;function $(e){return b.has(e)?Promise.resolve():new Promise(t=>{h.set(e,t),setTimeout(()=>{h.delete(e)&&t()},3e4)})}let p=null,y=null,E="",w=null;const _={state:"idle",agentId:null,hub:o.hub,name:o.name};function u(e,t){t!==void 0&&(_.lastError=t),_.state=e,_.agentId=w,o.onStatus?.({..._})}function c(e){s&&s.readyState===k.OPEN&&s.send(JSON.stringify(e))}function C(){let e;try{e=o.listSessions()}catch{return}const t=JSON.stringify(e);t!==E&&(E=t,c({type:"sessions",sessions:e}))}function v(){p||(p=setInterval(C,W)),y||(y=setInterval(()=>c({type:"ping",ts:Date.now()}),K))}function H(){p&&(clearInterval(p),p=null),y&&(clearInterval(y),y=null)}async function O(e){let t;if(e.hasBody){await $(e.id),b.delete(e.id);const a=f.get(e.id);f.delete(e.id),t=a&&a.length?Buffer.concat(a):Buffer.alloc(0)}const n=new URL(e.path,"http://agent.invalid");let i;try{const a={method:e.method,headers:e.headers};t&&t.length&&(a.body=t),i=await Promise.race([o.app.request(new Request(n,a)),new Promise((G,D)=>setTimeout(()=>D(new Error("app.request timed out")),3e4))])}catch(a){g(`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 I={};i.headers.forEach((a,l)=>{F.has(l.toLowerCase())&&(I[l]=a)});const r=Buffer.from(await i.arrayBuffer());if(c({type:"http_resp",id:e.id,status:i.status,headers:I,hasBody:r.length>0}),r.length>0){for(let l=0;l<r.length;l+=65536){if(!s||s.readyState!==k.OPEN)return;s.send(j(A,e.id,r.subarray(l,l+65536)))}c({type:"http_body_end",id:e.id})}}function L(e){const t=e.session;let n=!1;try{n=o.listSessions().some(r=>r.name===t)}catch{n=!1}if(!n){c({type:"attach_err",connId:e.connId,session:t,message:"no such session"});return}let i=null;const I=J(t,{send:r=>c({type:"ws_to_hub",connId:e.connId,msg:r}),onMessage:r=>{i=r},onClose:()=>{}},{terminalBufferConfig:o.terminalBufferConfig,getSessionPaneTarget:o.getSessionPaneTarget,capturePaneTail:o.capturePaneTail,capturePaneHistoryChunk:o.capturePaneHistoryChunk,toCrlf:o.toCrlf,handleClientMessage:o.handleClientMessage,acquireControlClient:o.acquireControlClient,onPtyExit:(r,a)=>{c({type:"ws_close",connId:e.connId,code:1e3,reason:`tmux exited (${a})`})},onSpawnError:(r,a)=>{c({type:"attach_err",connId:e.connId,session:t,message:a})},onAttached:r=>{c({type:"attach_ok",connId:e.connId,session:t})}});d.set(e.connId,{onMessage:r=>i?.(r),dispose:()=>{d.delete(e.connId),I.dispose()}})}function R(e){let t;try{t=JSON.parse(e)}catch{return}switch(t.type){case"hello_ok":w=t.agentId,S=1e3,T(`connected to hub as "${w}"`),u("connected"),C(),v();break;case"hello_err":if(g(`hub rejected us: ${t.reason}`),u("error",t.reason),s)try{s.close(4003,"hello rejected")}catch{}break;case"sessions_req":C();break;case"http_req":O(t);break;case"http_body_end":{b.add(t.id);const n=h.get(t.id);n&&(h.delete(t.id),n());break}case"attach":L(t);break;case"ws_to_agent":{const n=d.get(t.connId);if(n)try{n.onMessage(JSON.stringify(t.msg))}catch{}break}case"detach":{const n=d.get(t.connId);n&&n.dispose();break}case"pong":break;default:break}}function q(e){const t=x(e);if(t&&t.kind===A){const n=f.get(t.id)??[];n.push(t.payload),f.set(t.id,n)}}function M(){H();for(const e of d.values())try{e.dispose()}catch{}d.clear(),f.clear(),h.clear(),b.clear(),E=""}function P(){if(s){try{s.removeAllListeners()}catch{}s=null}if(M(),m)return;u("connecting");const e=S+Math.random()*500;T(`reconnecting in ${Math.round(e)}ms\u2026`),B=setTimeout(N,e),S=Math.min(S*2,6e4)}function N(){if(m||s)return;const e=o.hub.replace(/\/+$/,"").replace(/^http:/,"ws:").replace(/^https:/,"wss:")+"/agent/ws";let t;try{t=new k(e)}catch(n){g(`bad hub URL: ${n.message} (${e})`),u("error",`bad hub URL: ${n.message}`);return}s=t,u("connecting"),t.on("open",()=>{T(`connecting to ${e}\u2026`),c({type:"hello",token:o.token,name:o.name,version:o.version})}),t.on("message",(n,i)=>{i?q(Array.isArray(n)?Buffer.concat(n):Buffer.isBuffer(n)?n:Buffer.from(n)):R(typeof n=="string"?n:Buffer.from(n).toString("utf-8"))}),t.on("close",()=>{s===t&&P()}),t.on("error",n=>{g(`connection error: ${n.message}`),s===t&&P()})}function U(){if(!m){if(m=!0,B&&clearTimeout(B),M(),s){try{s.removeAllListeners(),s.close(1e3,"shutdown")}catch{}s=null}w=null,u("idle")}}return N(),{stop:U,status:()=>({..._})}}export{Q as startAgentClient};
1
+ import{WebSocket as k}from"ws";import{attachTerminal as J}from"./attach-terminal.js";import{encodeBinaryFrame as j,decodeBinaryFrame as x,BIN_KIND_HTTP_BODY as $}from"./agent-channel.js";const K=parseInt(process.env.TMUX_WEB_AGENT_HEARTBEAT_MS||"15000",10),W=parseInt(process.env.TMUX_WEB_AGENT_SESSIONS_POLL_MS||"10000",10),F=new Set(["content-type","content-disposition","location","cache-control","etag"]);function Q(o){const T=o.log??(e=>console.log(`[agent] ${e}`)),f=e=>console.error(`[agent] ${e}`);let s=null,S=1e3,B=null,m=!1;const d=new Map,h=new Map,p=new Map,b=new Set;function A(e){return b.has(e)?Promise.resolve():new Promise(t=>{p.set(e,t),setTimeout(()=>{p.delete(e)&&t()},3e4)})}let y=null,_=null,E="",w=null;const g={state:"idle",agentId:null,hub:o.hub,name:o.name};function u(e,t){t!==void 0&&(g.lastError=t),g.state=e,g.agentId=w,o.onStatus?.({...g})}function c(e){s&&s.readyState===k.OPEN&&s.send(JSON.stringify(e))}function C(){let e;try{e=o.listSessions()}catch{return}const t=JSON.stringify(e);t!==E&&(E=t,c({type:"sessions",sessions:e}))}function v(){y||(y=setInterval(C,W)),_||(_=setInterval(()=>c({type:"ping",ts:Date.now()}),K))}function H(){y&&(clearInterval(y),y=null),_&&(clearInterval(_),_=null)}async function O(e){let t;if(e.hasBody){await A(e.id),b.delete(e.id);const r=h.get(e.id);h.delete(e.id),t=r&&r.length?Buffer.concat(r):Buffer.alloc(0)}const n=new URL(e.path,"http://agent.invalid");let i;try{const r={method:e.method,headers:e.headers};t&&t.length&&(r.body=t),i=await Promise.race([o.app.request(new Request(n,r)),new Promise((G,D)=>setTimeout(()=>D(new Error("app.request timed out")),3e4))])}catch(r){f(`tunnel request failed: ${e.method} ${e.path} ${r.message}`),c({type:"http_resp",id:e.id,status:500,headers:{"content-type":"application/json"},hasBody:!1});return}const I={};i.headers.forEach((r,l)=>{F.has(l.toLowerCase())&&(I[l]=r)});const a=Buffer.from(await i.arrayBuffer());if(c({type:"http_resp",id:e.id,status:i.status,headers:I,hasBody:a.length>0}),a.length>0){for(let l=0;l<a.length;l+=65536){if(!s||s.readyState!==k.OPEN)return;s.send(j($,e.id,a.subarray(l,l+65536)))}c({type:"http_body_end",id:e.id})}}function L(e){const t=e.session;let n=!1;try{n=o.listSessions().some(a=>a.name===t)}catch{n=!1}if(!n){c({type:"attach_err",connId:e.connId,session:t,message:"no such session"});return}let i=null;const I=J(t,{send:a=>c({type:"ws_to_hub",connId:e.connId,msg:a}),onMessage:a=>{i=a},onClose:()=>{}},{terminalBufferConfig:o.terminalBufferConfig,getSessionPaneTarget:o.getSessionPaneTarget,capturePaneTail:o.capturePaneTail,capturePaneHistoryChunk:o.capturePaneHistoryChunk,toCrlf:o.toCrlf,handleClientMessage:o.handleClientMessage,acquireControlClient:o.acquireControlClient,onPtyExit:(a,r)=>{c({type:"ws_close",connId:e.connId,code:1e3,reason:`tmux exited (${r})`})},onSpawnError:(a,r)=>{f(`attach failed for session "${e.session}": ${r}`),c({type:"attach_err",connId:e.connId,session:t,message:r})},onAttached:a=>{c({type:"attach_ok",connId:e.connId,session:t})}});d.set(e.connId,{onMessage:a=>i?.(a),dispose:()=>{d.delete(e.connId),I.dispose()}})}function R(e){let t;try{t=JSON.parse(e)}catch{return}switch(t.type){case"hello_ok":w=t.agentId,S=1e3,T(`connected to hub as "${w}"`),u("connected"),C(),v();break;case"hello_err":if(f(`hub rejected us: ${t.reason}`),u("error",t.reason),s)try{s.close(4003,"hello rejected")}catch{}break;case"sessions_req":C();break;case"http_req":O(t);break;case"http_body_end":{b.add(t.id);const n=p.get(t.id);n&&(p.delete(t.id),n());break}case"attach":L(t);break;case"ws_to_agent":{const n=d.get(t.connId);if(n)try{n.onMessage(JSON.stringify(t.msg))}catch{}break}case"detach":{const n=d.get(t.connId);n&&n.dispose();break}case"pong":break;default:break}}function q(e){const t=x(e);if(t&&t.kind===$){const n=h.get(t.id)??[];n.push(t.payload),h.set(t.id,n)}}function M(){H();for(const e of d.values())try{e.dispose()}catch{}d.clear(),h.clear(),p.clear(),b.clear(),E=""}function P(){if(s){try{s.removeAllListeners()}catch{}s=null}if(M(),m)return;u("connecting");const e=S+Math.random()*500;T(`reconnecting in ${Math.round(e)}ms\u2026`),B=setTimeout(N,e),S=Math.min(S*2,6e4)}function N(){if(m||s)return;const e=o.hub.replace(/\/+$/,"").replace(/^http:/,"ws:").replace(/^https:/,"wss:")+"/agent/ws";let t;try{t=new k(e)}catch(n){f(`bad hub URL: ${n.message} (${e})`),u("error",`bad hub URL: ${n.message}`);return}s=t,u("connecting"),t.on("open",()=>{T(`connecting to ${e}\u2026`),c({type:"hello",token:o.token,name:o.name,version:o.version})}),t.on("message",(n,i)=>{i?q(Array.isArray(n)?Buffer.concat(n):Buffer.isBuffer(n)?n:Buffer.from(n)):R(typeof n=="string"?n:Buffer.from(n).toString("utf-8"))}),t.on("close",()=>{s===t&&P()}),t.on("error",n=>{f(`connection error: ${n.message}`),s===t&&P()})}function U(){if(!m){if(m=!0,B&&clearTimeout(B),M(),s){try{s.removeAllListeners(),s.close(1e3,"shutdown")}catch{}s=null}w=null,u("idle")}}return N(),{stop:U,status:()=>({...g})}}export{Q as startAgentClient};
@@ -1,5 +1,3 @@
1
- import{WebSocket as r}from"ws";import{decodeBinaryFrame as f,encodeBinaryFrame as g,BIN_KIND_TERM as u,BIN_KIND_HTTP_BODY as h}from"./agent-channel.js";const b=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 O{ws;agentId;pending=new Map;relays=new Map;onSessions;onClose;disposed=!1;constructor(t,e,s){this.ws=t,this.agentId=e,this.onSessions=s.onSessions,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){return this.send({type:"attach",connId:t,session:e})}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"})}async httpRequest(t,e,s,n){const a=w++,o=!!n,c=new Promise((d,i)=>{const l=setTimeout(()=>{this.pending.delete(a),i(new Error("tunnel request timed out"))},_);this.pending.set(a,{id:a,resolve:d,reject:i,status:null,headers:null,body:[],hasBody:o,timer:l})});if(!this.send({type:"http_req",id:a,method:t,path:e,headers:s,hasBody:o}))return this.pending.delete(a),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(g(h,a,y))}this.send({type:"http_body_end",id:a})})().catch(()=>{this.send({type:"http_body_end",id:a})})}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=f(e);if(s){if(s.kind===u){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"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:"data",data:`\r
2
- \x1B[31mAgent "${this.agentId}": cannot attach to session "${e.session}": ${e.message}\x1B[0m\r
3
- `})),s.ws.close(1e3,"attach failed")),this.relays.delete(e.connId);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??{},a=Buffer.concat(e.body),o={};for(const[c,d]of Object.entries(n)){const i=c.toLowerCase();(i==="content-type"||i==="content-disposition"||i==="cache-control"||i==="etag"||i==="location")&&(o[c]=d)}e.resolve(new Response(a,{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
1
+ import{WebSocket as r}from"ws";import{decodeBinaryFrame as f,encodeBinaryFrame as g,BIN_KIND_TERM as u,BIN_KIND_HTTP_BODY as h}from"./agent-channel.js";const b=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 O{ws;agentId;pending=new Map;relays=new Map;onSessions;onClose;disposed=!1;constructor(t,e,s){this.ws=t,this.agentId=e,this.onSessions=s.onSessions,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){return this.send({type:"attach",connId:t,session:e})}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"})}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(g(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=f(e);if(s){if(s.kind===u){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"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
4
2
  \x1B[31m--- agent disconnected (${t}) ---\x1B[0m\r
5
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{O as AgentChannel,b as MAX_RELAY_CONNS_PER_AGENT,_ as TUNNEL_REQUEST_TIMEOUT_MS};
@@ -1,6 +1,6 @@
1
- import*as _ from"node-pty";const f=new Set;function $(){for(const n of[...f])try{n()}catch{}f.clear()}function B(){return f.size}function H(n,r,e){const{initialLines:T,historyChunk:w,syncIdleMs:b,syncMaxMs:S}=e.terminalBufferConfig,P=e.spawn??(l=>_.spawn("tmux",["attach-session","-t",l],{name:"xterm-256color",cols:80,rows:24,cwd:process.env.HOME||"/",env:process.env}));let a=null,y=n,d=null,h=!0,m="",i=null,p=null,c=!1;const g=()=>{i&&(clearTimeout(i),i=null),p&&(clearTimeout(p),p=null)},C=()=>{if(!(!h||c)){g(),h=!1;try{y=e.getSessionPaneTarget(n)}catch{y=n}try{const l=e.capturePaneTail(y,T);r.send({type:"snapshot",data:e.toCrlf(l),lines:T})}catch{r.send({type:"data",data:`\r
2
- `})}m&&(r.send({type:"data",data:m}),m="")}},A=()=>{h&&(i&&clearTimeout(i),i=setTimeout(C,b))};function k(){if(c)return;try{a=P(n)}catch(t){const s=t?.message??String(t);r.send({type:"data",data:`\r
3
- \x1B[31mFailed to attach to tmux session "${n}": ${s}\x1B[0m\r
4
- `}),e.onSpawnError?.(n,s);return}let l=-1,o="";d=e.acquireControlClient(n,({activeIndex:t,windows:s})=>{r.send({type:"window_changed",activeIndex:t,windows:s});const x=s.map(v=>v.index).join(","),E=t!==l||x!==o;l=t,o=x}),p=setTimeout(C,S),e.onAttached?.(n),r.onMessage(M),a.onData(t=>{if(!c){if(h){m+=t,A();return}r.send({type:"data",data:t})}}),a.onExit(({exitCode:t})=>{g(),a&&(a=null),c||(e.onPtyExit?.(n,t),r.send({type:"data",data:`\r
1
+ import*as O from"node-pty";const f=new Set;function _(){for(const n of[...f])try{n()}catch{}f.clear()}function B(){return f.size}function H(n,r,e){const{initialLines:M,historyChunk:A,syncIdleMs:P,syncMaxMs:w}=e.terminalBufferConfig,E=e.spawn??(c=>O.spawn("tmux",["attach-session","-t",c],{name:"xterm-256color",cols:80,rows:24,cwd:process.env.HOME||"/",env:process.env}));let a=null,y=n,d=null,g=0,T="",h=!0,p="",i=null,m=null,o=!1;const S=()=>{i&&(clearTimeout(i),i=null),m&&(clearTimeout(m),m=null)},b=()=>{if(!(!h||o)){S(),h=!1;try{y=e.getSessionPaneTarget(n)}catch{y=n}try{const c=e.capturePaneTail(y,M);r.send({type:"snapshot",data:e.toCrlf(c),lines:M})}catch{r.send({type:"data",data:`\r
2
+ `})}p&&(r.send({type:"data",data:p}),p="")}},k=()=>{h&&(i&&clearTimeout(i),i=setTimeout(b,P))};function $(){if(o)return;try{a=E(n)}catch(t){const l=t?.message??String(t);r.send({type:"data",data:`\r
3
+ \x1B[31mFailed to attach to tmux session "${n}": ${l}\x1B[0m\r
4
+ `}),e.onSpawnError?.(n,l);return}let c=-1,s="";d=e.acquireControlClient(n,({activeIndex:t,windows:l})=>{r.send({type:"window_changed",activeIndex:t,windows:l});const x=l.map(D=>D.index).join(","),v=t!==c||x!==s;c=t,s=x}),g=Date.now(),m=setTimeout(b,w),e.onAttached?.(n),r.onMessage(C),a.onData(t=>{if(!o){if(Date.now()-g<w&&T.length<2048&&(T+=t),h){p+=t,k();return}r.send({type:"data",data:t})}}),a.onExit(({exitCode:t})=>{if(S(),a&&(a=null),!o){if(t!==0&&Date.now()-g<w){const l=T.trim();e.onSpawnError?.(n,l?`tmux attach-session failed: ${l}`:`tmux attach-session exited with code ${t}`);return}e.onPtyExit?.(n,t),r.send({type:"data",data:`\r
5
5
  \x1B[2m--- tmux exited (code ${t}) ---\x1B[0m\r
6
- `}),u())})}function u(){if(!c){if(c=!0,g(),d&&(d(),d=null),a){try{a.kill()}catch{}a=null}f.delete(u)}}r.onClose(u);const M=l=>{if(c||!a)return;try{if(e.handleClientMessage(l,a))return}catch{return}let o;try{o=JSON.parse(l)}catch{return}if(o.type==="load_history"&&typeof o.before=="number"){const t=Math.max(0,Math.floor(o.before));try{const{data:s,lines:x}=e.capturePaneHistoryChunk(y,t,w);r.send({type:"history",data:e.toCrlf(s),before:t,lines:x})}catch{r.send({type:"history",data:"",before:t,lines:0})}}};return k(),f.add(u),{onMessage:M,dispose:u}}export{B as _liveAttachmentCount,H as attachTerminal,$ as killAllAttachedPtys};
6
+ `}),u()}})}function u(){if(!o){if(o=!0,S(),d&&(d(),d=null),a){try{a.kill()}catch{}a=null}f.delete(u)}}r.onClose(u);const C=c=>{if(o||!a)return;try{if(e.handleClientMessage(c,a))return}catch{return}let s;try{s=JSON.parse(c)}catch{return}if(s.type==="load_history"&&typeof s.before=="number"){const t=Math.max(0,Math.floor(s.before));try{const{data:l,lines:x}=e.capturePaneHistoryChunk(y,t,A);r.send({type:"history",data:e.toCrlf(l),before:t,lines:x})}catch{r.send({type:"history",data:"",before:t,lines:0})}}};return $(),f.add(u),{onMessage:C,dispose:u}}export{B as _liveAttachmentCount,H as attachTerminal,_ as killAllAttachedPtys};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {