tmux-weblink 2.0.3 → 2.0.5

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 E}from"ws";import{attachTerminal as q}from"./attach-terminal.js";import{encodeBinaryFrame as U,decodeBinaryFrame as D,BIN_KIND_HTTP_BODY as N}from"./agent-channel.js";const J=parseInt(process.env.TMUX_WEB_AGENT_HEARTBEAT_MS||"15000",10),j=parseInt(process.env.TMUX_WEB_AGENT_SESSIONS_POLL_MS||"10000",10),x=new Set(["content-type","content-disposition","location","cache-control","etag"]);function X(o){const I=o.log??(e=>console.log(`[agent] ${e}`)),_=e=>console.error(`[agent] ${e}`);let s=null,g=1e3,T=null,S=!1;const u=new Map,f=new Map;let h=null,p=null,w="",m=null;const y={state:"idle",agentId:null,hub:o.hub,name:o.name};function d(e,t){t!==void 0&&(y.lastError=t),y.state=e,y.agentId=m,o.onStatus?.({...y})}function c(e){s&&s.readyState===E.OPEN&&s.send(JSON.stringify(e))}function B(){let e;try{e=o.listSessions()}catch{return}const t=JSON.stringify(e);t!==w&&(w=t,c({type:"sessions",sessions:e}))}function P(){h||(h=setInterval(B,j)),p||(p=setInterval(()=>c({type:"ping",ts:Date.now()}),J))}function A(){h&&(clearInterval(h),h=null),p&&(clearInterval(p),p=null)}async function $(e){let t;if(e.hasBody){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((K,R)=>setTimeout(()=>R(new Error("app.request timed out")),3e4))])}catch(a){_(`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 b={};i.headers.forEach((a,l)=>{x.has(l.toLowerCase())&&(b[l]=a)});const r=Buffer.from(await i.arrayBuffer());if(c({type:"http_resp",id:e.id,status:i.status,headers:b,hasBody:r.length>0}),r.length>0){for(let l=0;l<r.length;l+=65536){if(!s||s.readyState!==E.OPEN)return;s.send(U(N,e.id,r.subarray(l,l+65536)))}c({type:"http_body_end",id:e.id})}}function v(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 b=q(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})}});u.set(e.connId,{onMessage:r=>i?.(r),dispose:()=>{u.delete(e.connId),b.dispose()}})}function H(e){let t;try{t=JSON.parse(e)}catch{return}switch(t.type){case"hello_ok":m=t.agentId,g=1e3,I(`connected to hub as "${m}"`),d("connected"),B(),P();break;case"hello_err":if(_(`hub rejected us: ${t.reason}`),d("error",t.reason),s)try{s.close(4003,"hello rejected")}catch{}break;case"sessions_req":B();break;case"http_req":$(t);break;case"http_body_end":break;case"attach":v(t);break;case"ws_to_agent":{const n=u.get(t.connId);if(n)try{n.onMessage(JSON.stringify(t.msg))}catch{}break}case"detach":{const n=u.get(t.connId);n&&n.dispose();break}case"pong":break;default:break}}function O(e){const t=D(e);if(t&&t.kind===N){const n=f.get(t.id)??[];n.push(t.payload),f.set(t.id,n)}}function C(){A();for(const e of u.values())try{e.dispose()}catch{}u.clear(),f.clear(),w=""}function k(){if(s){try{s.removeAllListeners()}catch{}s=null}if(C(),S)return;d("connecting");const e=g+Math.random()*500;I(`reconnecting in ${Math.round(e)}ms\u2026`),T=setTimeout(M,e),g=Math.min(g*2,6e4)}function M(){if(S||s)return;const e=o.hub.replace(/\/+$/,"").replace(/^http:/,"ws:").replace(/^https:/,"wss:")+"/agent/ws";let t;try{t=new E(e)}catch(n){_(`bad hub URL: ${n.message} (${e})`),d("error",`bad hub URL: ${n.message}`);return}s=t,d("connecting"),t.on("open",()=>{I(`connecting to ${e}\u2026`),c({type:"hello",token:o.token,name:o.name,version:o.version})}),t.on("message",(n,i)=>{i?O(Array.isArray(n)?Buffer.concat(n):Buffer.isBuffer(n)?n:Buffer.from(n)):H(typeof n=="string"?n:Buffer.from(n).toString("utf-8"))}),t.on("close",()=>{s===t&&k()}),t.on("error",n=>{_(`connection error: ${n.message}`),s===t&&k()})}function L(){if(!S){if(S=!0,T&&clearTimeout(T),C(),s){try{s.removeAllListeners(),s.close(1e3,"shutdown")}catch{}s=null}m=null,d("idle")}}return M(),{stop:L,status:()=>({...y})}}export{X 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};
@@ -770,9 +770,9 @@ window.__TMUX_WEB_SHELL__ = ${f};
770
770
  await import('/assets/shell-client.js');
771
771
  ${t?m(a,[]):""}
772
772
  // Wrapper: refresh sidebar list, then open session
773
- window.__onSessionCreated = async function(name) {
773
+ window.__onSessionCreated = async function(name, agentId) {
774
774
  await window.__refreshSidebar();
775
- window.__openSession(name);
775
+ window.__openSession(name, agentId || undefined);
776
776
  };
777
777
  ${h("__onSessionCreated")}
778
778
  </script>
@@ -1,4 +1,4 @@
1
- import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}from"./html.js";import{icon as a,iconPath as o}from"./icons.js";const d=[{id:"vscode",name:"VS Code",dot:"#007acc"},{id:"ghostty",name:"Ghostty",dot:"#ff5f00"},{id:"warm-clay",name:"Warm Clay",dot:"#b86b52"},{id:"dark-cove",name:"Dark Cove",dot:"#7aa2f7"}];function r(t="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${t}; outline: none;`}function y(t=""){return`
1
+ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}from"./html.js";import{icon as a,iconPath as n}from"./icons.js";const d=[{id:"vscode",name:"VS Code",dot:"#007acc"},{id:"ghostty",name:"Ghostty",dot:"#ff5f00"},{id:"warm-clay",name:"Warm Clay",dot:"#b86b52"},{id:"dark-cove",name:"Dark Cove",dot:"#7aa2f7"}];function r(t="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${t}; outline: none;`}function y(t=""){return`
2
2
  @media (prefers-reduced-motion: reduce) {
3
3
  *, *::before, *::after {
4
4
  animation-duration: 0.01ms !important;
@@ -7,15 +7,15 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
7
7
  scroll-behavior: auto !important;
8
8
  }
9
9
  ${t}
10
- }`}function k(t="vscode"){const n=d.find(e=>e.id===t)??d[0],i=d.map(e=>{const s=e.id===t;return`<button type="button" class="theme-option${s?" active":""}" data-theme="${e.id}" role="menuitem">
10
+ }`}function k(t="vscode"){const o=d.find(e=>e.id===t)??d[0],i=d.map(e=>{const s=e.id===t;return`<button type="button" class="theme-option${s?" active":""}" data-theme="${e.id}" role="menuitem">
11
11
  <span class="theme-dot" style="background:${e.dot}"></span>
12
12
  ${c(e.name)}
13
13
  ${s?a("check",'class="theme-check" aria-hidden="true"'):""}
14
14
  </button>`}).join(`
15
15
  `);return`<div class="theme-switcher" id="theme-switcher">
16
16
  <button type="button" class="theme-switcher-btn" aria-label="Theme" aria-haspopup="true" aria-expanded="false">
17
- <span class="theme-dot" style="background:${n.dot}"></span>
18
- <span>${c(n.name)}</span>
17
+ <span class="theme-dot" style="background:${o.dot}"></span>
18
+ <span>${c(o.name)}</span>
19
19
  </button>
20
20
  <div class="theme-switcher-popover" role="menu">
21
21
  ${i}
@@ -217,7 +217,7 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
217
217
  }
218
218
 
219
219
  ${y()}
220
- ${t}`}function T(t){const{commandbarEnabled:n,title:i="TMUX Sessions",themeTemplate:e="vscode"}=t;return`<script>
220
+ ${t}`}function A(t){const{commandbarEnabled:o,title:i="TMUX Sessions",themeTemplate:e="vscode"}=t;return`<script>
221
221
  (function() {
222
222
  const token = localStorage.getItem('tmux-web-token');
223
223
  if (token) {
@@ -236,7 +236,7 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
236
236
  <header class="fixed-header">
237
237
  <div class="brand"><a href="/" aria-label="Go to home">tmux<span>-weblink</span></a></div>
238
238
  <div class="header-actions">
239
- ${n?w("Search"):""}
239
+ ${o?w("Search"):""}
240
240
  <button class="header-btn" id="notes-toggle" title="Global notes" aria-label="Global notes">
241
241
  ${a("notes")}
242
242
  <span>Notes</span>
@@ -248,10 +248,10 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
248
248
  </a>
249
249
  </div>
250
250
  </header>
251
- <script>${I()}</script>`}function M(t){const{activePage:n,refreshHref:i}=t;function e(l,x,f,g,v=""){return`<a href="${x}" class="${l!==null&&l===n?"sidebar-btn current":"sidebar-btn"}"${v}>
251
+ <script>${I()}</script>`}function T(t){const{activePage:o,refreshHref:i}=t;function e(l,x,f,g,v=""){return`<a href="${x}" class="${l!==null&&l===o?"sidebar-btn current":"sidebar-btn"}"${v}>
252
252
  <svg viewBox="0 0 24 24" fill="currentColor">${f}</svg>
253
253
  ${g}
254
- </a>`}const s=o("sessions"),p=o("notes"),m=o("schedule"),b=o("history"),u=o("quick-commands"),h=o("file"),E=o("refresh");return`<aside class="action-sidebar">
254
+ </a>`}const s=n("sessions"),p=n("notes"),m=n("schedule"),b=n("history"),u=n("quick-commands"),h=n("file"),E=n("refresh");return`<aside class="action-sidebar">
255
255
  <p class="sidebar-label">Actions</p>
256
256
  <button class="sidebar-btn primary" id="new-session-btn">
257
257
  ${a("add")}
@@ -269,7 +269,7 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
269
269
  ${a("refresh")}
270
270
  Refresh
271
271
  </a>
272
- </aside>`}function A(){return` /* \u2500\u2500 New session modal \u2500\u2500 */
272
+ </aside>`}function M(){return` /* \u2500\u2500 New session modal \u2500\u2500 */
273
273
  .modal-backdrop {
274
274
  display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.3);
275
275
  z-index: 500; align-items: center; justify-content: center;
@@ -349,6 +349,20 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
349
349
  const errorEl = document.getElementById('ns-error');
350
350
  const onCreated = '${t??""}';
351
351
 
352
+ // The new session is created on the machine of the currently selected
353
+ // session (window.__tmuxWebScopeAgent is set by the shell client; agent
354
+ // page spaces also set it via the injected scope). Falls back to this
355
+ // machine when nothing is selected.
356
+ function selectedAgentId() {
357
+ var a = window.__tmuxWebScopeAgent;
358
+ return (typeof a === 'string' && a) ? a : '';
359
+ }
360
+
361
+ function selectedBase() {
362
+ var id = selectedAgentId();
363
+ return id ? '/a/' + encodeURIComponent(id) : '';
364
+ }
365
+
352
366
  function openModal() {
353
367
  modal.classList.add('open');
354
368
  nameInput.value = '';
@@ -412,7 +426,7 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
412
426
  if (!val) { closeDropdown(); return; }
413
427
  debounceTimer = setTimeout(async () => {
414
428
  try {
415
- const res = await fetch('/api/fs/list?path=' + encodeURIComponent(val));
429
+ const res = await fetch(selectedBase() + '/api/fs/list?path=' + encodeURIComponent(val));
416
430
  const data = await res.json();
417
431
  renderDropdown(data.dirs || []);
418
432
  } catch { closeDropdown(); }
@@ -429,7 +443,8 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
429
443
  submitBtn.textContent = 'Creating\u2026';
430
444
  errorEl.style.display = 'none';
431
445
  try {
432
- const res = await fetch('/api/sessions/new', {
446
+ const base = selectedBase();
447
+ const res = await fetch(base + '/api/sessions/new', {
433
448
  method: 'POST',
434
449
  headers: { 'Content-Type': 'application/json' },
435
450
  body: JSON.stringify({ name, dir: dir || undefined }),
@@ -437,8 +452,9 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
437
452
  const data = await res.json();
438
453
  if (!res.ok) { showError(data.error || 'Failed to create session.'); return; }
439
454
  closeModal();
440
- if (onCreated && typeof window[onCreated] === 'function') { window[onCreated](name); return; }
441
- window.location.href = '/s/' + encodeURIComponent(name);
455
+ const agentId = selectedAgentId() || null;
456
+ if (onCreated && typeof window[onCreated] === 'function') { window[onCreated](name, agentId); return; }
457
+ window.location.href = base + '/s/' + encodeURIComponent(name);
442
458
  } catch { showError('Network error. Please try again.'); }
443
459
  finally { submitBtn.disabled = false; submitBtn.textContent = 'Create'; }
444
460
  }
@@ -463,4 +479,4 @@ import{commandbarButtonHTML as w}from"./commandbar.js";import{escapeHtml as c}fr
463
479
  }
464
480
  if (e.key === 'Enter') submit();
465
481
  });
466
- })();`}export{A as newSessionModalCSS,D as newSessionModalHTML,j as newSessionModalScript,y as reducedMotion,T as sharedHeader,B as sharedLayoutCSS,M as sharedSidebar,k as themeSwitcherButtonHTML,I as themeSwitcherScript};
482
+ })();`}export{M as newSessionModalCSS,D as newSessionModalHTML,j as newSessionModalScript,y as reducedMotion,A as sharedHeader,B as sharedLayoutCSS,T as sharedSidebar,k as themeSwitcherButtonHTML,I as themeSwitcherScript};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {