tmux-weblink 2.0.26 → 2.0.28

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,24 +1,24 @@
1
- var Xt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function je(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function ae(e){return e.replace(Xt,"")}var ke;function Jt(){return ke||(document.querySelector("link[data-tmux-web-xterm-css]")?ke=Promise.resolve():(ke=new Promise(e=>{let t=document.createElement("link");t.rel="stylesheet",t.href="/assets/xterm.css",t.setAttribute("data-tmux-web-xterm-css",""),t.addEventListener("load",()=>e(),{once:!0}),t.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(t)}),ke))}var Zt=class xt{terminal;fitAddon;container;constructor(t,s,i){this.container=t,this.terminal=s,this.fitAddon=i,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,s,i){let[{Terminal:o},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Jt()]),d=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:s,convertEol:!1,smoothScrollDuration:120,theme:i});return new xt(t,d,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(t){return new Promise(s=>this.terminal.write(t,s))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(t,s=!1){this.terminal.scrollToLine(Math.max(0,t),s)}scrollLines(t){let s=this.terminal.buffer?.active;this.terminal.scrollLines(t)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let t=Gt(this.container);return t.width<=0||t.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,s){let i=this.terminal.buffer.active,[o,r]=t<=s?[t,s]:[s,t],d=i.viewportY,l=[];for(let u=o;u<=r;u++){let f=i.getLine(d+u);l.push(f?f.translateToString(!0):"")}return l.join(`
2
- `)}selectRows(t,s){let i=this.terminal.buffer.active,[o,r]=t<=s?[t,s]:[s,t];this.terminal.selectLines(i.viewportY+o,i.viewportY+r)}selectCells(t,s,i,o){let r=this.terminal.buffer.active,d=Math.min(s,o),l=Math.max(s,o);if(d===l){let u=Math.min(t,i),f=Math.max(t,i);this.terminal.select(u,r.viewportY+d,f-u+1)}else this.terminal.selectLines(r.viewportY+d,r.viewportY+l)}getCellsText(t,s,i,o){let r=this.terminal.buffer.active,d=Math.min(s,o),l=Math.max(s,o),u=s<o?t:s>o?i:Math.min(t,i),f=s<o?i:s>o?t:Math.max(t,i),R=r.viewportY,q=[];for(let N=d;N<=l;N++){let ve=r.getLine(R+N);if(!ve){q.push("");continue}let O=N===d?u:0,Pe=N===l?f:this.terminal.cols-1,K="";for(let k=O;k<=Pe;k++){let z=ve.getCell(k);z&&(K+=z.getChars())}q.push(K.replace(/\s+$/,""))}return q.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function Gt(e){let t=e.getBoundingClientRect(),s=window.visualViewport;return s?{width:Math.min(t.width,s.width),height:Math.max(0,Math.min(t.height,s.height-Math.max(0,t.top)))}:t}function Qt(e,t,s){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let i=await Zt.create(e,s.scrollback,s.theme),o,r=0,d,l=null,u=0,f=0,R=.2,q=10,N=.95,ve=.04;function O(){u&&(cancelAnimationFrame(u),u=0),f=0}function Pe(n){if(O(),Math.abs(n)<R)return;f=Math.max(-q,Math.min(q,n));let a=()=>{if(A){u=0;return}let c=e.querySelector(".xterm-rows > div"),p=c?c.getBoundingClientRect().height:16,y=p>0?Math.round(f*16/p):f>0?1:-1;y===0&&(y=f>0?1:-1);let B=i.viewportY()-y,Y=i.baseY(),se=Math.max(0,Math.min(B,Y));if(i.scrollToLine(se,!0),f*=N,se!==B){u=0;return}if(Math.abs(f)<ve){u=0;return}u=requestAnimationFrame(a)};u=requestAnimationFrame(a)}let K=0,k="connecting",z=0,he=!1,ye=[],V="",A=!1,G=1e3,kt=location.protocol==="https:"?"wss:":"ws:",Ct=s.wsBase||"/ws",Tt=kt+"//"+location.host+Ct+"/"+encodeURIComponent(t),Mt="/api/session/"+encodeURIComponent(t)+"/upload",Bt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function It(){return ye.join("")+V}async function St(n,a=0){let c=n?i.viewportY():0,p=n?i.baseY():0,y=ae(It());if(i.reset(),!y){n||i.scrollToBottom();return}await i.write(y),n?i.scrollToLine(p+c+a):i.scrollToBottom()}function F(n){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(n))}function Ht(n){let a;try{a=JSON.parse(n)}catch{k==="live"&&(V+=n,i.write(ae(n)));return}if(a.type==="auth.required"){let c=localStorage.getItem("tmux-web-token");c?F({type:"auth.token",token:c}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(a.type==="attach_failed"&&typeof a.message=="string"){ge=!0,k="dead",i.reset(),i.write(`\r
4
- \x1B[31m`+ae(a.message)+`\x1B[0m\r
5
- `).then(()=>{be();try{o?.close()}catch{}});return}if(a.type==="auth.ok"){a.token&&localStorage.setItem("tmux-web-token",a.token),G=1e3,j(),F({type:"resize",cols:i.cols,rows:i.rows}),C();return}if(a.type==="auth.failed"){if(a.permanentLock||a.retryAfterMs&&a.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(a.message);return}return}if(a.type==="snapshot"&&typeof a.data=="string"){let c=je(ae(a.data));ye=[c],V="",z=typeof a.lines=="number"?a.lines:s.terminal.initialLines,k="live",i.reset(),j(),F({type:"resize",cols:i.cols,rows:i.rows}),i.write(c).then(()=>i.scrollToBottom());return}if(a.type==="window_changed"&&typeof a.activeIndex=="number"){let c=new URL(location.href);c.searchParams.get("window")!==String(a.activeIndex)&&(c.searchParams.set("window",String(a.activeIndex)),history.replaceState(history.state,"",c)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:a.windows}));return}if(a.type==="history"&&typeof a.data=="string"){he=!1,a.lines>0&&a.data&&(ye.unshift(je(a.data)),z+=a.lines,St(!0,a.lines));return}if(a.type==="data"&&typeof a.data=="string"){let c=je(ae(a.data));if(k==="connecting"){k="live",V=c,i.write(c);return}k==="live"&&(V+=c,i.write(c))}}let ge=!1;function Ve(){if(ge)return;let n=Tt,a=localStorage.getItem("tmux-web-token");a&&(n+=(n.includes("?")?"&":"?")+"token="+encodeURIComponent(a)),n+=(n.includes("?")?"&":"?")+"cols="+i.cols+"&rows="+i.rows,o=new WebSocket(n),o.onopen=()=>{k="connecting",z=0,he=!1,ye=[],V="",i.reset(),G=1e3},o.onmessage=c=>{typeof c.data=="string"&&Ht(c.data)},o.onclose=()=>{A||(k="connecting",!ge&&setTimeout(()=>{G=Math.min(G*2,1e4),!A&&!ge&&Ve()},G))},o.onerror=()=>o?.close()}function we(n){F({type:"input",data:n})}function j(){return i.fit()}function be(){e.classList.remove("terminal-pending")}function C(){r&&cancelAnimationFrame(r),d&&clearTimeout(d),j(),r=requestAnimationFrame(()=>{r=0,j()&&be()}),d=setTimeout(()=>{j()&&be()},120)}function Dt(){C(),setTimeout(C,50),setTimeout(C,150),setTimeout(C,300)}function Xe(n){return typeof n=="string"&&n.startsWith("image/")}function Je(n){return n?.files?.length?n.files[0]??null:null}function Rt(n){if(!n?.items)return null;for(let a=0;a<n.items.length;a++){let c=n.items[a];if(c?.kind==="file"&&Xe(c.type))return c.getAsFile()}return null}async function At(n){let a=new FormData,c=n instanceof File&&n.name?n.name:"upload";a.append("file",n,c);let p=localStorage.getItem("tmux-web-token"),y={};p&&(y.Authorization="Bearer "+p);let B=await fetch(Mt,{method:"POST",body:a,headers:y});if(!B.ok){let se="upload failed";try{let it=await B.json();it.error&&(se=it.error)}catch{}throw new Error(se)}let Y=await B.json();if(!Y.path)throw new Error("no path in response");return Y.path}async function Ne(n){try{let a=await At(n);we(a)}catch{}}i.onData(n=>we(n)),i.onResize(({cols:n,rows:a})=>F({type:"resize",cols:n,rows:a})),i.onScroll(()=>{k!=="live"||he||!i.isNearScrollbackTop()||(he=!0,F({type:"load_history",before:z}))});let Q=!1,ee=!1,b=null,L=null,te=document.createElement("div");te.className="terminal-copy-banner",te.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',te.hidden=!0,e.appendChild(te);let H=document.createElement("div");H.className="terminal-copy-banner terminal-copy-toast",H.textContent="Copied",H.hidden=!0,e.appendChild(H);function Ze(n,a){let c=e.querySelector(".xterm-screen, canvas"),p=(c&&c.getBoundingClientRect().width>0?c:e).getBoundingClientRect(),y=p.height>0&&i.rows>0?p.height/i.rows:18,B=p.width>0&&i.cols>0?p.width/i.cols:9;return{row:Math.max(0,Math.min(i.rows-1,Math.floor((a-p.top)/y))),col:Math.max(0,Math.min(i.cols-1,Math.floor((n-p.left)/B)))}}async function Le(n){if(!n)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(n);return}}catch{}let a=document.createElement("textarea");a.value=n,a.style.position="fixed",a.style.opacity="0",a.style.pointerEvents="none",document.body.appendChild(a),a.focus(),a.select();try{document.execCommand("copy")}catch{}a.remove()}let ne;function Ee(){H.hidden=!1,ne&&clearTimeout(ne),ne=setTimeout(()=>{H.hidden=!0},1e3)}function X(n){Q=n,te.hidden=!n,e.classList.toggle("terminal-copy-mode-active",n),n&&i.focus()}function Ge(){X(!Q)}function $t(n){A||n.button!==0||ee||(Q||n.shiftKey)&&(n.preventDefault(),n.stopPropagation(),ee=!0,b=Ze(n.clientX,n.clientY),L=b,i.selectCells(b.col,b.row,L.col,L.row),document.addEventListener("mousemove",ze,!0),document.addEventListener("mouseup",Fe,!0))}function ze(n){A||!ee||!b||(n.preventDefault(),n.stopPropagation(),L=Ze(n.clientX,n.clientY),i.selectCells(b.col,b.row,L.col,L.row))}function Fe(n){if(A||!ee||!b||!L)return;n.preventDefault(),n.stopPropagation(),document.removeEventListener("mousemove",ze,!0),document.removeEventListener("mouseup",Fe,!0),ee=!1;let a=i.getCellsText(b.col,b.row,L.col,L.row);b=null,L=null,i.clearSelection(),X(!1),a&&(H.textContent="\u5DF2\u590D\u5236",Le(a).then(Ee)),i.focus()}e.addEventListener("mousedown",$t,!0),document.addEventListener("keydown",Pt,!0);function Pt(n){if(!(A||!i.isFocused()||n.defaultPrevented||n.isComposing)){if(Q){if(n.key==="Escape"){n.preventDefault(),n.stopPropagation(),b=null,L=null,i.clearSelection(),X(!1);return}if(n.key==="Enter"||(n.ctrlKey||n.metaKey)&&n.code==="KeyC"){n.preventDefault(),n.stopPropagation(),Le(i.getScreenText()).then(Ee),X(!1);return}}if(n.altKey&&n.code==="KeyC"&&!n.ctrlKey&&!n.metaKey){if(n.preventDefault(),n.stopPropagation(),b&&L){let a=i.getCellsText(b.col,b.row,L.col,L.row);b=null,L=null,i.clearSelection(),X(!1),a&&(H.textContent="Copied",Le(a).then(Ee))}else{let a=i.getSelection();a?(i.clearSelection(),H.textContent="Copied",Le(a).then(Ee)):Ge()}return}if((n.ctrlKey||n.metaKey)&&n.code==="KeyV"){n.preventDefault(),n.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let a=await navigator.clipboard.read();for(let c of a)for(let p of c.types)if(Xe(p)){let y=await c.getType(p);await Ne(y);return}}}catch{}try{let a=await navigator.clipboard?.readText();a&&i.pasteText(Qe(a))}catch{}})();return}}}let xe=0;function Qe(n){return n.split(`\r
1
+ var Xt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Fe(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function oe(e){return e.replace(Xt,"")}var ke;function Jt(){return ke||(document.querySelector("link[data-tmux-web-xterm-css]")?ke=Promise.resolve():(ke=new Promise(e=>{let t=document.createElement("link");t.rel="stylesheet",t.href="/assets/xterm.css",t.setAttribute("data-tmux-web-xterm-css",""),t.addEventListener("load",()=>e(),{once:!0}),t.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(t)}),ke))}var Zt=class xt{terminal;fitAddon;container;constructor(t,s,i){this.container=t,this.terminal=s,this.fitAddon=i,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,s,i){let[{Terminal:o},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Jt()]),d=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:s,convertEol:!1,smoothScrollDuration:120,theme:i});return new xt(t,d,new r)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(t){return new Promise(s=>this.terminal.write(t,s))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(t,s=!1){this.terminal.scrollToLine(Math.max(0,t),s)}scrollLines(t){let s=this.terminal.buffer?.active;this.terminal.scrollLines(t)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let t=Gt(this.container);return t.width<=0||t.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,s){let i=this.terminal.buffer.active,[o,r]=t<=s?[t,s]:[s,t],d=i.viewportY,l=[];for(let u=o;u<=r;u++){let f=i.getLine(d+u);l.push(f?f.translateToString(!0):"")}return l.join(`
2
+ `)}selectRows(t,s){let i=this.terminal.buffer.active,[o,r]=t<=s?[t,s]:[s,t];this.terminal.selectLines(i.viewportY+o,i.viewportY+r)}selectCells(t,s,i,o){let r=this.terminal.buffer.active,d=Math.min(s,o),l=Math.max(s,o);if(d===l){let u=Math.min(t,i),f=Math.max(t,i);this.terminal.select(u,r.viewportY+d,f-u+1)}else this.terminal.selectLines(r.viewportY+d,r.viewportY+l)}getCellsText(t,s,i,o){let r=this.terminal.buffer.active,d=Math.min(s,o),l=Math.max(s,o),u=s<o?t:s>o?i:Math.min(t,i),f=s<o?i:s>o?t:Math.max(t,i),R=r.viewportY,q=[];for(let N=d;N<=l;N++){let he=r.getLine(R+N);if(!he){q.push("");continue}let O=N===d?u:0,Pe=N===l?f:this.terminal.cols-1,K="";for(let k=O;k<=Pe;k++){let z=he.getCell(k);z&&(K+=z.getChars())}q.push(K.replace(/\s+$/,""))}return q.join(`
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function Gt(e){let t=e.getBoundingClientRect(),s=window.visualViewport;return s?{width:Math.min(t.width,s.width),height:Math.max(0,Math.min(t.height,s.height-Math.max(0,t.top)))}:t}function Qt(e,t,s){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let i=await Zt.create(e,s.scrollback,s.theme),o,r=0,d,l=null,u=0,f=0,R=.2,q=10,N=.95,he=.04;function O(){u&&(cancelAnimationFrame(u),u=0),f=0}function Pe(n){if(O(),Math.abs(n)<R)return;f=Math.max(-q,Math.min(q,n));let a=()=>{if(A){u=0;return}let c=e.querySelector(".xterm-rows > div"),p=c?c.getBoundingClientRect().height:16,b=p>0?Math.round(f*16/p):f>0?1:-1;b===0&&(b=f>0?1:-1);let B=i.viewportY()-b,j=i.baseY(),ae=Math.max(0,Math.min(B,j));if(i.scrollToLine(ae,!0),f*=N,ae!==B){u=0;return}if(Math.abs(f)<he){u=0;return}u=requestAnimationFrame(a)};u=requestAnimationFrame(a)}let K=0,k="connecting",z=0,G=!1,ge=[],V="",A=!1,Q=1e3,kt=location.protocol==="https:"?"wss:":"ws:",Ct=s.wsBase||"/ws",Tt=kt+"//"+location.host+Ct+"/"+encodeURIComponent(t),Mt="/api/session/"+encodeURIComponent(t)+"/upload",Bt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function It(){return ge.join("")+V}async function St(n,a=0){let c=n?i.viewportY():0,p=oe(It());if(i.reset(),!p){n||i.scrollToBottom();return}await i.write(p),n?i.scrollToLine(a+c):i.scrollToBottom()}function _(n){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(n))}function Ht(n){let a;try{a=JSON.parse(n)}catch{k==="live"&&(V+=n,i.write(oe(n)));return}if(a.type==="auth.required"){let c=localStorage.getItem("tmux-web-token");c?_({type:"auth.token",token:c}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(a.type==="attach_failed"&&typeof a.message=="string"){ye=!0,k="dead",i.reset(),i.write(`\r
4
+ \x1B[31m`+oe(a.message)+`\x1B[0m\r
5
+ `).then(()=>{be();try{o?.close()}catch{}});return}if(a.type==="auth.ok"){a.token&&localStorage.setItem("tmux-web-token",a.token),Q=1e3,F(),_({type:"resize",cols:i.cols,rows:i.rows}),C();return}if(a.type==="auth.failed"){if(a.permanentLock||a.retryAfterMs&&a.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(a.message);return}return}if(a.type==="snapshot"&&typeof a.data=="string"){let c=Fe(oe(a.data));ge=[c],V="",z=typeof a.lines=="number"?a.lines:s.terminal.initialLines,k="live",i.reset(),F(),_({type:"resize",cols:i.cols,rows:i.rows}),i.write(c).then(()=>i.scrollToBottom());return}if(a.type==="window_changed"&&typeof a.activeIndex=="number"){let c=new URL(location.href);c.searchParams.get("window")!==String(a.activeIndex)&&(c.searchParams.set("window",String(a.activeIndex)),history.replaceState(history.state,"",c)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:a.windows}));return}if(a.type==="history"&&typeof a.data=="string"){a.lines>0&&a.data?(ge.unshift(Fe(a.data)),z+=a.lines,St(!0,a.lines).finally(()=>{G=!1})):G=!1;return}if(a.type==="data"&&typeof a.data=="string"){let c=Fe(oe(a.data));if(k==="connecting"){k="live",V=c,i.write(c);return}k==="live"&&(V+=c,i.write(c))}}let ye=!1;function Ve(){if(ye)return;let n=Tt,a=localStorage.getItem("tmux-web-token");a&&(n+=(n.includes("?")?"&":"?")+"token="+encodeURIComponent(a)),n+=(n.includes("?")?"&":"?")+"cols="+i.cols+"&rows="+i.rows,o=new WebSocket(n),o.onopen=()=>{k="connecting",z=0,G=!1,ge=[],V="",i.reset(),Q=1e3},o.onmessage=c=>{typeof c.data=="string"&&Ht(c.data)},o.onclose=()=>{A||(k="connecting",!ye&&setTimeout(()=>{Q=Math.min(Q*2,1e4),!A&&!ye&&Ve()},Q))},o.onerror=()=>o?.close()}function we(n){_({type:"input",data:n})}function F(){return i.fit()}function be(){e.classList.remove("terminal-pending")}function C(){r&&cancelAnimationFrame(r),d&&clearTimeout(d),F(),r=requestAnimationFrame(()=>{r=0,F()&&be()}),d=setTimeout(()=>{F()&&be()},120)}function Dt(){C(),setTimeout(C,50),setTimeout(C,150),setTimeout(C,300)}function Xe(n){return typeof n=="string"&&n.startsWith("image/")}function Je(n){return n?.files?.length?n.files[0]??null:null}function Rt(n){if(!n?.items)return null;for(let a=0;a<n.items.length;a++){let c=n.items[a];if(c?.kind==="file"&&Xe(c.type))return c.getAsFile()}return null}async function At(n){let a=new FormData,c=n instanceof File&&n.name?n.name:"upload";a.append("file",n,c);let p=localStorage.getItem("tmux-web-token"),b={};p&&(b.Authorization="Bearer "+p);let B=await fetch(Mt,{method:"POST",body:a,headers:b});if(!B.ok){let ae="upload failed";try{let it=await B.json();it.error&&(ae=it.error)}catch{}throw new Error(ae)}let j=await B.json();if(!j.path)throw new Error("no path in response");return j.path}async function Ne(n){try{let a=await At(n);we(a)}catch{}}i.onData(n=>we(n)),i.onResize(({cols:n,rows:a})=>_({type:"resize",cols:n,rows:a})),i.onScroll(()=>{k!=="live"||G||!i.isNearScrollbackTop()||(G=!0,_({type:"load_history",before:z}))});let ee=!1,te=!1,w=null,L=null,ne=document.createElement("div");ne.className="terminal-copy-banner",ne.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',ne.hidden=!0,e.appendChild(ne);let H=document.createElement("div");H.className="terminal-copy-banner terminal-copy-toast",H.textContent="Copied",H.hidden=!0,e.appendChild(H);function Ze(n,a){let c=e.querySelector(".xterm-screen, canvas"),p=(c&&c.getBoundingClientRect().width>0?c:e).getBoundingClientRect(),b=p.height>0&&i.rows>0?p.height/i.rows:18,B=p.width>0&&i.cols>0?p.width/i.cols:9;return{row:Math.max(0,Math.min(i.rows-1,Math.floor((a-p.top)/b))),col:Math.max(0,Math.min(i.cols-1,Math.floor((n-p.left)/B)))}}async function Le(n){if(!n)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(n);return}}catch{}let a=document.createElement("textarea");a.value=n,a.style.position="fixed",a.style.opacity="0",a.style.pointerEvents="none",document.body.appendChild(a),a.focus(),a.select();try{document.execCommand("copy")}catch{}a.remove()}let ie;function Ee(){H.hidden=!1,ie&&clearTimeout(ie),ie=setTimeout(()=>{H.hidden=!0},1e3)}function X(n){ee=n,ne.hidden=!n,e.classList.toggle("terminal-copy-mode-active",n),n&&i.focus()}function Ge(){X(!ee)}function $t(n){A||n.button!==0||te||(ee||n.shiftKey)&&(n.preventDefault(),n.stopPropagation(),te=!0,w=Ze(n.clientX,n.clientY),L=w,i.selectCells(w.col,w.row,L.col,L.row),document.addEventListener("mousemove",ze,!0),document.addEventListener("mouseup",_e,!0))}function ze(n){A||!te||!w||(n.preventDefault(),n.stopPropagation(),L=Ze(n.clientX,n.clientY),i.selectCells(w.col,w.row,L.col,L.row))}function _e(n){if(A||!te||!w||!L)return;n.preventDefault(),n.stopPropagation(),document.removeEventListener("mousemove",ze,!0),document.removeEventListener("mouseup",_e,!0),te=!1;let a=i.getCellsText(w.col,w.row,L.col,L.row);w=null,L=null,i.clearSelection(),X(!1),a&&(H.textContent="\u5DF2\u590D\u5236",Le(a).then(Ee)),i.focus()}e.addEventListener("mousedown",$t,!0),document.addEventListener("keydown",Pt,!0);function Pt(n){if(!(A||!i.isFocused()||n.defaultPrevented||n.isComposing)){if(ee){if(n.key==="Escape"){n.preventDefault(),n.stopPropagation(),w=null,L=null,i.clearSelection(),X(!1);return}if(n.key==="Enter"||(n.ctrlKey||n.metaKey)&&n.code==="KeyC"){n.preventDefault(),n.stopPropagation(),Le(i.getScreenText()).then(Ee),X(!1);return}}if(n.altKey&&n.code==="KeyC"&&!n.ctrlKey&&!n.metaKey){if(n.preventDefault(),n.stopPropagation(),w&&L){let a=i.getCellsText(w.col,w.row,L.col,L.row);w=null,L=null,i.clearSelection(),X(!1),a&&(H.textContent="Copied",Le(a).then(Ee))}else{let a=i.getSelection();a?(i.clearSelection(),H.textContent="Copied",Le(a).then(Ee)):Ge()}return}if((n.ctrlKey||n.metaKey)&&n.code==="KeyV"){n.preventDefault(),n.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let a=await navigator.clipboard.read();for(let c of a)for(let p of c.types)if(Xe(p)){let b=await c.getType(p);await Ne(b);return}}}catch{}try{let a=await navigator.clipboard?.readText();a&&i.pasteText(Qe(a))}catch{}})();return}}}let xe=0;function Qe(n){return n.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function Nt(n){let a=Rt(n.clipboardData);if(a){n.preventDefault(),n.stopPropagation();let y=Date.now();if(y-xe<50)return;xe=y,await Ne(a);return}let c=n.clipboardData?.getData("text/plain");if(!c)return;n.preventDefault(),n.stopPropagation();let p=Date.now();p-xe<50||(xe=p,i.pasteText(Qe(c)))}let ie=0;function zt(n){n.preventDefault(),ie++,Je(n.dataTransfer)&&e.classList.add("terminal-drag-over")}function Ft(n){n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="copy")}function jt(n){n.preventDefault(),ie--,ie<=0&&(ie=0,e.classList.remove("terminal-drag-over"))}async function Yt(n){n.preventDefault(),ie=0,e.classList.remove("terminal-drag-over");let a=Je(n.dataTransfer);a&&await Ne(a)}function et(n){!Bt||n.key!=="Escape"||Q||n.defaultPrevented||n.repeat||n.metaKey||n.ctrlKey||n.altKey||i.isFocused()&&(n.preventDefault(),n.stopPropagation(),we("\x1B"))}e.addEventListener("paste",Nt),e.addEventListener("dragenter",zt),e.addEventListener("dragover",Ft),e.addEventListener("dragleave",jt),e.addEventListener("drop",Yt),document.addEventListener("keydown",et,!0);function _t(n){if(n.touches.length!==1){l=null;return}let a=n.touches[0];a&&(O(),l={startX:a.clientX,startY:a.clientY,lastX:a.clientX,lastY:a.clientY,lastT:performance.now(),velocity:0,scrolling:!1},n.stopPropagation())}function Wt(n){if(!l||n.touches.length!==1)return;let a=n.touches[0];if(!a)return;let c=a.clientY-l.startY,p=a.clientX-l.startX;if(!l.scrolling){if(Math.abs(c)<8||Math.abs(c)<Math.abs(p))return;l.scrolling=!0,K=Date.now()+500}n.preventDefault(),n.stopPropagation();let y=performance.now(),B=a.clientY-l.lastY,Y=y-l.lastT;Y>0&&(l.velocity=B/Y),Kt(-B),l.lastX=a.clientX,l.lastY=a.clientY,l.lastT=y}function Ut(n){if(!l)return;let a=l.scrolling,c=l.velocity;if(l=null,n.stopPropagation(),!a){i.focus();return}K=Date.now()+500,n.preventDefault(),c&&Pe(c)}function qt(n){l=null,O(),n.stopPropagation()}function Ot(n){n.pointerType==="touch"&&Date.now()<K&&(n.preventDefault(),n.stopPropagation())}function Kt(n){let a=e.querySelector(".xterm-rows > div"),c=a?a.getBoundingClientRect().height:16,p=c>0?Math.round(n/c):n>0?1:-1;p===0&&(p=n>0?1:-1),i.scrollLines(p)}e.addEventListener("touchstart",_t,{passive:!0,capture:!0}),e.addEventListener("touchmove",Wt,{passive:!1,capture:!0}),e.addEventListener("touchend",Ut,{passive:!1,capture:!0}),e.addEventListener("touchcancel",qt,{passive:!0,capture:!0}),e.addEventListener("pointerup",Ot,!0);function tt(){C()}window.addEventListener("resize",tt),window.visualViewport?.addEventListener("resize",C),window.visualViewport?.addEventListener("scroll",C);let nt=new ResizeObserver(()=>C());nt.observe(e),document.fonts?.ready.then(()=>{C(),setTimeout(C,50),setTimeout(C,200)}),e.addEventListener("focusin",Dt,!0);async function Vt(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(n=>setTimeout(n,1e3))])}catch{}}return requestAnimationFrame(()=>{j()&&be(),(async()=>(await Vt(),j()&&F({type:"resize",cols:i.cols,rows:i.rows}),Ve(),C()))()}),{destroy(){if(A=!0,O(),X(!1),ne&&clearTimeout(ne),document.removeEventListener("mousemove",ze,!0),document.removeEventListener("mouseup",Fe,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",tt),nt.disconnect(),document.removeEventListener("keydown",et,!0)},focus(){i.focus()},sendInput(n){we(n)},toggleCopyMode(){Ge()}}})()}var oe=window.__TMUX_WEB_SHELL__,h=null,w=null,st=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,x=st;window.__tmuxWebScopeAgent=st;var Ye="sessions",$="",Ce="",Te="",at="";function T(e){return x?"/a/"+encodeURIComponent(x)+e:e}var v=document.getElementById("sidebar-content"),_=document.getElementById("main-placeholder"),M=document.getElementById("terminal-container"),le=document.getElementById("file-editor"),en=document.getElementById("fe-path"),I=document.getElementById("fe-status"),re=document.getElementById("fe-content"),W=document.getElementById("fe-save"),tn=document.getElementById("fe-delete"),nn=document.getElementById("fe-back"),ot=document.getElementById("fe-new-name"),sn=document.getElementById("fe-new-btn"),_e=document.getElementById("settings-backdrop"),lt=document.getElementById("settings-popover"),rt=document.querySelector(".sidebar");function an(){return window.matchMedia("(max-width: 640px)").matches}function We(){an()&&rt.classList.add("collapsed")}function ct(){rt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{ct(),dt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{ct(),dt("files")});function dt(e){Ye=e,document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?(Ue="",J()):vn()}var Ue="";async function J(){if(Ye!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents});if(s===Ue)return;Ue=s,on(t)}catch{v.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function on(e){v.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let t=e.recent||[],s=document.createElement("div");s.className="sidebar-section-label"+(x?" clickable":""),s.textContent=x?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",x&&(s.title="Back to hub",s.addEventListener("click",()=>{window.location.href="/"})),v.appendChild(s);for(let o of t){let r=document.createElement("div");r.className="session-item"+(o.name===w&&!x?" 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 Nt(n){let a=Rt(n.clipboardData);if(a){n.preventDefault(),n.stopPropagation();let b=Date.now();if(b-xe<50)return;xe=b,await Ne(a);return}let c=n.clipboardData?.getData("text/plain");if(!c)return;n.preventDefault(),n.stopPropagation();let p=Date.now();p-xe<50||(xe=p,i.pasteText(Qe(c)))}let se=0;function zt(n){n.preventDefault(),se++,Je(n.dataTransfer)&&e.classList.add("terminal-drag-over")}function _t(n){n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="copy")}function Ft(n){n.preventDefault(),se--,se<=0&&(se=0,e.classList.remove("terminal-drag-over"))}async function jt(n){n.preventDefault(),se=0,e.classList.remove("terminal-drag-over");let a=Je(n.dataTransfer);a&&await Ne(a)}function et(n){!Bt||n.key!=="Escape"||ee||n.defaultPrevented||n.repeat||n.metaKey||n.ctrlKey||n.altKey||i.isFocused()&&(n.preventDefault(),n.stopPropagation(),we("\x1B"))}e.addEventListener("paste",Nt),e.addEventListener("dragenter",zt),e.addEventListener("dragover",_t),e.addEventListener("dragleave",Ft),e.addEventListener("drop",jt),document.addEventListener("keydown",et,!0);function Yt(n){if(n.touches.length!==1){l=null;return}let a=n.touches[0];a&&(O(),l={startX:a.clientX,startY:a.clientY,lastX:a.clientX,lastY:a.clientY,lastT:performance.now(),velocity:0,scrolling:!1},n.stopPropagation())}function Ut(n){if(!l||n.touches.length!==1)return;let a=n.touches[0];if(!a)return;let c=a.clientY-l.startY,p=a.clientX-l.startX;if(!l.scrolling){if(Math.abs(c)<8||Math.abs(c)<Math.abs(p))return;l.scrolling=!0,K=Date.now()+500}n.preventDefault(),n.stopPropagation();let b=performance.now(),B=a.clientY-l.lastY,j=b-l.lastT;j>0&&(l.velocity=B/j),Kt(-B),l.lastX=a.clientX,l.lastY=a.clientY,l.lastT=b}function Wt(n){if(!l)return;let a=l.scrolling,c=l.velocity;if(l=null,n.stopPropagation(),!a){i.focus();return}K=Date.now()+500,n.preventDefault(),c&&Pe(c)}function qt(n){l=null,O(),n.stopPropagation()}function Ot(n){n.pointerType==="touch"&&Date.now()<K&&(n.preventDefault(),n.stopPropagation())}function Kt(n){let a=e.querySelector(".xterm-rows > div"),c=a?a.getBoundingClientRect().height:16,p=c>0?Math.round(n/c):n>0?1:-1;p===0&&(p=n>0?1:-1),i.scrollLines(p)}e.addEventListener("touchstart",Yt,{passive:!0,capture:!0}),e.addEventListener("touchmove",Ut,{passive:!1,capture:!0}),e.addEventListener("touchend",Wt,{passive:!1,capture:!0}),e.addEventListener("touchcancel",qt,{passive:!0,capture:!0}),e.addEventListener("pointerup",Ot,!0);function tt(){C()}window.addEventListener("resize",tt),window.visualViewport?.addEventListener("resize",C),window.visualViewport?.addEventListener("scroll",C);let nt=new ResizeObserver(()=>C());nt.observe(e),document.fonts?.ready.then(()=>{C(),setTimeout(C,50),setTimeout(C,200)}),e.addEventListener("focusin",Dt,!0);async function Vt(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(n=>setTimeout(n,1e3))])}catch{}}return requestAnimationFrame(()=>{F()&&be(),(async()=>(await Vt(),F()&&_({type:"resize",cols:i.cols,rows:i.rows}),Ve(),C()))()}),{destroy(){if(A=!0,O(),X(!1),ie&&clearTimeout(ie),document.removeEventListener("mousemove",ze,!0),document.removeEventListener("mouseup",_e,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",tt),nt.disconnect(),document.removeEventListener("keydown",et,!0)},focus(){i.focus()},sendInput(n){we(n)},toggleCopyMode(){Ge()}}})()}var le=window.__TMUX_WEB_SHELL__,h=null,y=null,st=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,x=st;window.__tmuxWebScopeAgent=st;var je="sessions",$="",Ce="",Te="",at="";function T(e){return x?"/a/"+encodeURIComponent(x)+e:e}var v=document.getElementById("sidebar-content"),Y=document.getElementById("main-placeholder"),M=document.getElementById("terminal-container"),re=document.getElementById("file-editor"),en=document.getElementById("fe-path"),I=document.getElementById("fe-status"),ce=document.getElementById("fe-content"),U=document.getElementById("fe-save"),tn=document.getElementById("fe-delete"),nn=document.getElementById("fe-back"),ot=document.getElementById("fe-new-name"),sn=document.getElementById("fe-new-btn"),Ye=document.getElementById("settings-backdrop"),lt=document.getElementById("settings-popover"),rt=document.querySelector(".sidebar");function an(){return window.matchMedia("(max-width: 640px)").matches}function Ue(){an()&&rt.classList.add("collapsed")}function ct(){rt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{ct(),dt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{ct(),dt("files")});function dt(e){je=e,document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?(We="",J()):vn()}var We="";async function J(){if(je!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents});if(s===We)return;We=s,on(t)}catch{v.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>{let t=window.__openNewSessionModal;typeof t=="function"?t():document.getElementById("new-session-modal")?.classList.add("open")})}function on(e){v.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let t=e.recent||[],s=document.createElement("div");s.className="sidebar-section-label"+(x?" clickable":""),s.textContent=x?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",x&&(s.title="Back to hub",s.addEventListener("click",()=>{window.location.href="/"})),v.appendChild(s);for(let o of t){let r=document.createElement("div");r.className="session-item"+(o.name===y&&!x?" 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>
9
9
  <span>${m(o.name)}</span>
10
10
  <button class="session-edit-btn" data-session="${m(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>
11
- `,r.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||ce(o.name)}),v.appendChild(r);let d=r.querySelector(".session-edit-btn");d&&d.addEventListener("click",l=>{l.stopPropagation(),bn(o.name,d)})}let i=e.agents||[];for(let o of i){if(!o.sessions||o.sessions.length===0)continue;let r=o.agentId===x,d=document.createElement("div");d.className="sidebar-section-label machine-label"+(o.online?"":" agent-offline-label")+(!r&&o.online?" clickable":""),d.textContent=o.agentName+(o.online?"":" (offline)"),!r&&o.online&&(d.title="\u6253\u5F00 "+o.agentName,d.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(o.agentId)+"/"})),v.appendChild(d);for(let l of o.sessions){let u=document.createElement("div");u.className="session-item"+(l.name===w&&x===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>${m(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",f=>{f.target.closest(".session-edit-btn")||ce(l.name,o.agentId)}),v.appendChild(u)}}}function m(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function ce(e,t){if(!(w===e&&h&&x===(t||null))){le.style.display="none",_.style.display="none",M.style.display="",M.classList.add("terminal-pending"),We(),h&&(Ae(),h.destroy(),h=null),w=e,x=t||null,window.__tmuxWebScopeAgent=x;for(let s of v.querySelectorAll(".session-item"))s.classList.toggle("active",s.getAttribute("data-session")===e&&(s.getAttribute("data-agent")||null)===x);try{h=await Qt(M,e,{terminal:oe.terminal,scrollback:oe.scrollback,theme:oe.theme,wsBase:x?"/ws/a/"+encodeURIComponent(x):oe.wsBase||"/ws"}),bt()}catch(s){console.error("[shell] terminal init failed:",s);let i=s&&typeof s=="object"&&"message"in s?s.message:String(s);M.textContent="Failed to open terminal for "+e+": "+i,M.classList.remove("terminal-pending")}}}var Me=[],g=null,ut=!1;function ln(e){if(!g||!g.files.length)return null;let t=g.repoRoot;if(!t||!e.startsWith(t))return null;let s=e.slice(t.length+1);for(let i of g.files)if(i.path===s)return i.status;return null}async function rn(e){ut=!0;try{let t=await fetch(T("/api/git/status?path="+encodeURIComponent(e)));t.ok?g=await t.json():g=null}catch{g=null}finally{ut=!1}}function cn(){if(!g||!g.branch)return"";let{branch:e,linesAdded:t,linesRemoved:s}=g,i=`<div class="sidebar-git"><div class="git-branch-badge">
11
+ `,r.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||de(o.name)}),v.appendChild(r);let d=r.querySelector(".session-edit-btn");d&&d.addEventListener("click",l=>{l.stopPropagation(),bn(o.name,d)})}let i=e.agents||[];for(let o of i){if(!o.sessions||o.sessions.length===0)continue;let r=o.agentId===x,d=document.createElement("div");d.className="sidebar-section-label machine-label"+(o.online?"":" agent-offline-label")+(!r&&o.online?" clickable":""),d.textContent=o.agentName+(o.online?"":" (offline)"),!r&&o.online&&(d.title="\u6253\u5F00 "+o.agentName,d.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(o.agentId)+"/"})),v.appendChild(d);for(let l of o.sessions){let u=document.createElement("div");u.className="session-item"+(l.name===y&&x===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>${m(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",f=>{f.target.closest(".session-edit-btn")||de(l.name,o.agentId)}),v.appendChild(u)}}}function m(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function de(e,t){if(!(y===e&&h&&x===(t||null))){re.style.display="none",Y.style.display="none",M.style.display="",M.classList.add("terminal-pending"),Ue(),h&&(Ae(),h.destroy(),h=null),y=e,x=t||null,window.__tmuxWebScopeAgent=x,window.__tmuxWebCurrentSession=y;for(let s of v.querySelectorAll(".session-item"))s.classList.toggle("active",s.getAttribute("data-session")===e&&(s.getAttribute("data-agent")||null)===x);try{h=await Qt(M,e,{terminal:le.terminal,scrollback:le.scrollback,theme:le.theme,wsBase:x?"/ws/a/"+encodeURIComponent(x):le.wsBase||"/ws"}),bt()}catch(s){console.error("[shell] terminal init failed:",s);let i=s&&typeof s=="object"&&"message"in s?s.message:String(s);M.textContent="Failed to open terminal for "+e+": "+i,M.classList.remove("terminal-pending")}}}var Me=[],g=null,ut=!1;function ln(e){if(!g||!g.files.length)return null;let t=g.repoRoot;if(!t||!e.startsWith(t))return null;let s=e.slice(t.length+1);for(let i of g.files)if(i.path===s)return i.status;return null}async function rn(e){ut=!0;try{let t=await fetch(T("/api/git/status?path="+encodeURIComponent(e)));t.ok?g=await t.json():g=null}catch{g=null}finally{ut=!1}}function cn(){if(!g||!g.branch)return"";let{branch:e,linesAdded:t,linesRemoved:s}=g,i=`<div class="sidebar-git"><div class="git-branch-badge">
13
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>
14
- ${m(e)}</div>`;return(t>0||s>0)&&(i+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${s}</span></div>`),i+"</div>"}v.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&Z&&g&&(e.stopPropagation(),ht())});var de=document.getElementById("header-git"),pt=document.getElementById("git-diff-view"),ft=document.getElementById("gd-path"),Be=document.getElementById("gd-status"),Ie=document.getElementById("gd-content"),dn=document.getElementById("gd-back"),Z=null;function ue(e){if(!e||!e.branch){de.style.display="none",Z=null;return}Z=e.repoRoot;let{branch:t,linesAdded:s,linesRemoved:i}=e,o=`<span class="branch">${m(t)}</span>`;(s>0||i>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${s}</span> <span class="diff-del">-${i}</span>`),de.innerHTML=o,de.style.display="block"}de.addEventListener("click",e=>{Z&&g&&(e.stopPropagation(),ht())});var E=document.getElementById("git-popover"),Se=document.getElementById("git-popover-backdrop");function qe(){let e=de.getBoundingClientRect();E.style.top=e.bottom+4+"px",E.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function mt(){let e=Z,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(De(),e){Ye="files",document.querySelectorAll(".mode-btn").forEach(i=>i.classList.remove("active"));let s=document.getElementById("mode-files");s&&s.classList.add("active"),P(e)}})}var vt="";function ht(){if(!g)return;let e=g.files||[];if(!e.length)E.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
14
+ ${m(e)}</div>`;return(t>0||s>0)&&(i+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${s}</span></div>`),i+"</div>"}v.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&Z&&g&&(e.stopPropagation(),ht())});var ue=document.getElementById("header-git"),pt=document.getElementById("git-diff-view"),ft=document.getElementById("gd-path"),Be=document.getElementById("gd-status"),Ie=document.getElementById("gd-content"),dn=document.getElementById("gd-back"),Z=null;function pe(e){if(!e||!e.branch){ue.style.display="none",Z=null;return}Z=e.repoRoot;let{branch:t,linesAdded:s,linesRemoved:i}=e,o=`<span class="branch">${m(t)}</span>`;(s>0||i>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${s}</span> <span class="diff-del">-${i}</span>`),ue.innerHTML=o,ue.style.display="block"}ue.addEventListener("click",e=>{Z&&g&&(e.stopPropagation(),ht())});var E=document.getElementById("git-popover"),Se=document.getElementById("git-popover-backdrop");function qe(){let e=ue.getBoundingClientRect();E.style.top=e.bottom+4+"px",E.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function mt(){let e=Z,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(De(),e){je="files",document.querySelectorAll(".mode-btn").forEach(i=>i.classList.remove("active"));let s=document.getElementById("mode-files");s&&s.classList.add("active"),P(e)}})}var vt="";function ht(){if(!g)return;let e=g.files||[];if(!e.length)E.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
15
15
  <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let t=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let s=0;s<e.length;s++){let i=e[s],o=i.additions||0,r=i.deletions||0,d="";switch(i.status){case"M":d='<span class="git-file-status M">M</span>';break;case"A":d='<span class="git-file-status A">A</span>';break;case"D":d='<span class="git-file-status D">D</span>';break;case"?":d='<span class="git-file-status ?">?</span>';break;case"R":d='<span class="git-file-status R">R</span>';break}t+=`<div class="git-popover-item" data-idx="${s}">
16
16
  ${d}<span class="file-path">${m(i.path)}</span>
17
17
  <span class="file-add">${o>0?"+"+o:""}</span>
18
18
  <span class="file-del">${r>0?"-"+r:""}</span>
19
- </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',vt=t,E.innerHTML=t,E.querySelectorAll(".git-popover-item").forEach(s=>{let i=parseInt(s.dataset.idx||"0",10),o=e[i];!o||o.status==="D"||(s.classList.add("clickable"),s.style.cursor="pointer",s.addEventListener("click",()=>gt(o.path)))})}qe(),E.classList.add("open"),Se.classList.add("open"),mt()}function un(){return window.innerWidth>=1024}function yt(e,t){let s=(e+(t&&e?`
19
+ </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',vt=t,E.innerHTML=t,E.querySelectorAll(".git-popover-item").forEach(s=>{let i=parseInt(s.dataset.idx||"0",10),o=e[i];!o||o.status==="D"||(s.classList.add("clickable"),s.style.cursor="pointer",s.addEventListener("click",()=>yt(o.path)))})}qe(),E.classList.add("open"),Se.classList.add("open"),mt()}function un(){return window.innerWidth>=1024}function gt(e,t){let s=(e+(t&&e?`
20
20
  `:"")+t).split(`
21
- `),i="",o=0,r=0;for(let d of s){let l=d;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">${m(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">${m(l)}</span></div>`);continue}if(l.startsWith("+"))r++,i+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${m(l)}</span></div>`;else if(l.startsWith("-"))o++,i+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${m(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${m(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">${m(l)}</span></div>`}}return{html:i,lineCount:s.length}}var He=null;async function gt(e){let t=Z;if(t)if(De(),un()){M.style.display="none",h&&(Ae(),h.destroy(),h=null,He=w,w=null),ue(null),le.style.display="none",_.style.display="none",pt.style.display="flex",ft.textContent=e,Be.textContent="Loading\u2026",Ie.innerHTML="";try{let s=await fetch(T("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!s.ok)throw new Error(await s.text());let i=await s.json();fn(e,i.diff,i.stagedDiff)}catch(s){let i=s instanceof Error?s.message:String(s);Be.textContent="Error",Ie.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${m(i)}</div>`}}else{E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
21
+ `),i="",o=0,r=0;for(let d of s){let l=d;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">${m(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">${m(l)}</span></div>`);continue}if(l.startsWith("+"))r++,i+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${m(l)}</span></div>`;else if(l.startsWith("-"))o++,i+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${m(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${m(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">${m(l)}</span></div>`}}return{html:i,lineCount:s.length}}var He=null;async function yt(e){let t=Z;if(t)if(De(),un()){M.style.display="none",h&&(Ae(),h.destroy(),h=null,He=y,y=null),pe(null),re.style.display="none",Y.style.display="none",pt.style.display="flex",ft.textContent=e,Be.textContent="Loading\u2026",Ie.innerHTML="";try{let s=await fetch(T("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!s.ok)throw new Error(await s.text());let i=await s.json();fn(e,i.diff,i.stagedDiff)}catch(s){let i=s instanceof Error?s.message:String(s);Be.textContent="Error",Ie.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${m(i)}</div>`}}else{E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
22
22
  <button class="diff-back">\u2190 Back</button>
23
23
  <span class="diff-filename">${m(e)}</span>
24
24
  <span class="diff-loading">Loading\u2026</span>
@@ -28,11 +28,11 @@ var Xt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
28
28
  </div><div class="diff-empty">Error loading diff: ${m(i)}</div></div>`}}}function pn(e,t,s){if(!t&&!s){E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
29
29
  <button class="diff-back">\u2190 Back</button>
30
30
  <span class="diff-filename">${m(e)}</span>
31
- </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:i,lineCount:o}=yt(t,s);E.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}=gt(t,s);E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
32
32
  <button class="diff-back">\u2190 Back</button>
33
33
  <span class="diff-filename">${m(e)}</span>
34
34
  <span class="diff-filesize">${o} lines</span>
35
- </div><div class="diff-content">${i}</div></div>`,E.querySelector(".diff-back")?.addEventListener("click",()=>{E.innerHTML=vt,qe(),g&&E.querySelectorAll(".git-popover-item").forEach(r=>{let d=parseInt(r.dataset.idx||"0",10),l=g.files[d];!l||l.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>gt(l.path)))}),mt()})}function fn(e,t,s){if(!t&&!s){Be.textContent="No diff",Ie.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}=yt(t,s);ft.textContent=e,Be.textContent=o+" lines",Ie.innerHTML=i}function mn(){pt.style.display="none",He?(M.style.display="",M.classList.add("terminal-pending"),ce(He),He=null):w?(M.style.display="",M.classList.add("terminal-pending"),bt()):_.style.display="flex"}dn.addEventListener("click",mn);function De(){E.classList.remove("open"),Se.classList.remove("open")}Se.addEventListener("click",De),document.addEventListener("keydown",e=>{e.key==="Escape"&&(De(),$e())});async function wt(e){try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){let s=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(s.ok){let i=await s.json();g=i,ue(i);return}}}catch{}g=null,ue(null)}var Re=null;function bt(){Ae(),w&&(wt(w),Re=setInterval(()=>{w&&wt(w)},3e4))}function Ae(){Re&&(clearInterval(Re),Re=null)}function vn(){if(Me=oe.fsRoots||[],Me.length===0){if(w){hn(w);return}v.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}v.innerHTML="";for(let e of Me){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${m(e)}`,t.addEventListener("click",()=>P(e)),v.appendChild(t)}}async function hn(e){v.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(T("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?P(t.path):v.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{v.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function P(e){$=e,v.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=rn(e);try{let s=await(await fetch(T("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,v.innerHTML="",ue(g),v.innerHTML+=cn();let i=yn(e);if(i&&!Me.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>P(i)),v.appendChild(r)}if(s.dirs)for(let r of s.dirs){let d=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> ${m(d)}`,l.addEventListener("click",()=>P(r)),v.appendChild(l)}else v.innerHTML="";let o=s.files||[];for(let r of o){let d=r.split("/").pop()||r.split("\\").pop(),l=ln(r),u="",f="";if(l)switch(l){case"M":u="git-mod",f='<span class="git-file-status M">M</span>';break;case"A":u="git-add",f='<span class="git-file-status A">A</span>';break;case"D":u="git-del",f='<span class="git-file-status D">D</span>';break;case"?":u="git-untracked",f='<span class="git-file-status ?">?</span>';break;case"R":u="git-mod",f='<span class="git-file-status R">R</span>';break}let R=document.createElement("div");R.className="file-tree-item file"+(u?" "+u:""),R.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${f}${m(d)}`,R.addEventListener("click",()=>gn(r)),v.appendChild(R)}}catch{v.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function yn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function gn(e){Ce=e,M.style.display="none",h&&(Ae(),h.destroy(),h=null,w=null),ue(null),We(),_.style.display="none",le.style.display="flex",en.textContent=e,I.textContent="Loading...",W.style.display="none";try{let t=await fetch(T("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let i=await t.json();I.textContent=i.error||"Failed to load",re.value="";return}let s=await t.json();re.value=s.content,Te=s.content,at=s.content,I.textContent=s.size+" bytes",W.style.display=""}catch{I.textContent="Failed to load",re.value=""}}re.addEventListener("input",()=>{Te=re.value}),W.addEventListener("click",async()=>{W.textContent="Saving...",W.disabled=!0;try{let e=await fetch(T("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ce,content:Te})});if(!e.ok){let t=await e.json();I.textContent=t.error||"Save failed";return}at=Te,I.textContent="Saved"}catch{I.textContent="Save failed"}finally{W.disabled=!1,W.textContent="Save"}}),tn.addEventListener("click",async()=>{if(confirm("Delete "+Ce+"?"))try{let e=await fetch(T("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ce})});if(!e.ok){let t=await e.json();I.textContent=t.error||"Delete failed";return}le.style.display="none",_.style.display="flex",$&&P($)}catch{I.textContent="Delete failed"}}),nn.addEventListener("click",()=>{le.style.display="none",_.style.display="flex",$&&P($)}),sn.addEventListener("click",async()=>{let e=ot.value.trim();if(!e)return;let t=$+"/"+e;try{let s=await fetch(T("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!s.ok){let i=await s.json();I.textContent=i.error||"Create failed";return}ot.value="",$&&P($)}catch{I.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=lt.classList.toggle("open");_e.classList.toggle("open",e)}),_e.addEventListener("click",()=>{lt.classList.remove("open"),_e.classList.remove("open")});var U="",pe=document.createElement("div");pe.className="session-popover-backdrop",document.body.appendChild(pe);var D=document.createElement("div");D.id="session-popover",D.className="session-popover",D.style.display="none",D.innerHTML=`<div class="sp-field">
35
+ </div><div class="diff-content">${i}</div></div>`,E.querySelector(".diff-back")?.addEventListener("click",()=>{E.innerHTML=vt,qe(),g&&E.querySelectorAll(".git-popover-item").forEach(r=>{let d=parseInt(r.dataset.idx||"0",10),l=g.files[d];!l||l.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>yt(l.path)))}),mt()})}function fn(e,t,s){if(!t&&!s){Be.textContent="No diff",Ie.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}=gt(t,s);ft.textContent=e,Be.textContent=o+" lines",Ie.innerHTML=i}function mn(){pt.style.display="none",He?(M.style.display="",M.classList.add("terminal-pending"),de(He),He=null):y?(M.style.display="",M.classList.add("terminal-pending"),bt()):Y.style.display="flex"}dn.addEventListener("click",mn);function De(){E.classList.remove("open"),Se.classList.remove("open")}Se.addEventListener("click",De),document.addEventListener("keydown",e=>{e.key==="Escape"&&(De(),$e())});async function wt(e){try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){let s=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(s.ok){let i=await s.json();g=i,pe(i);return}}}catch{}g=null,pe(null)}var Re=null;function bt(){Ae(),y&&(wt(y),Re=setInterval(()=>{y&&wt(y)},3e4))}function Ae(){Re&&(clearInterval(Re),Re=null)}function vn(){if(Me=le.fsRoots||[],Me.length===0){if(y){hn(y);return}v.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}v.innerHTML="";for(let e of Me){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${m(e)}`,t.addEventListener("click",()=>P(e)),v.appendChild(t)}}async function hn(e){v.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(T("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?P(t.path):v.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{v.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function P(e){$=e,v.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=rn(e);try{let s=await(await fetch(T("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,v.innerHTML="",pe(g),v.innerHTML+=cn();let i=gn(e);if(i&&!Me.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>P(i)),v.appendChild(r)}if(s.dirs)for(let r of s.dirs){let d=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> ${m(d)}`,l.addEventListener("click",()=>P(r)),v.appendChild(l)}else v.innerHTML="";let o=s.files||[];for(let r of o){let d=r.split("/").pop()||r.split("\\").pop(),l=ln(r),u="",f="";if(l)switch(l){case"M":u="git-mod",f='<span class="git-file-status M">M</span>';break;case"A":u="git-add",f='<span class="git-file-status A">A</span>';break;case"D":u="git-del",f='<span class="git-file-status D">D</span>';break;case"?":u="git-untracked",f='<span class="git-file-status ?">?</span>';break;case"R":u="git-mod",f='<span class="git-file-status R">R</span>';break}let R=document.createElement("div");R.className="file-tree-item file"+(u?" "+u:""),R.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${f}${m(d)}`,R.addEventListener("click",()=>yn(r)),v.appendChild(R)}}catch{v.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function gn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function yn(e){Ce=e,M.style.display="none",h&&(Ae(),h.destroy(),h=null,y=null),pe(null),Ue(),Y.style.display="none",re.style.display="flex",en.textContent=e,I.textContent="Loading...",U.style.display="none";try{let t=await fetch(T("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let i=await t.json();I.textContent=i.error||"Failed to load",ce.value="";return}let s=await t.json();ce.value=s.content,Te=s.content,at=s.content,I.textContent=s.size+" bytes",U.style.display=""}catch{I.textContent="Failed to load",ce.value=""}}ce.addEventListener("input",()=>{Te=ce.value}),U.addEventListener("click",async()=>{U.textContent="Saving...",U.disabled=!0;try{let e=await fetch(T("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ce,content:Te})});if(!e.ok){let t=await e.json();I.textContent=t.error||"Save failed";return}at=Te,I.textContent="Saved"}catch{I.textContent="Save failed"}finally{U.disabled=!1,U.textContent="Save"}}),tn.addEventListener("click",async()=>{if(confirm("Delete "+Ce+"?"))try{let e=await fetch(T("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Ce})});if(!e.ok){let t=await e.json();I.textContent=t.error||"Delete failed";return}re.style.display="none",Y.style.display="flex",$&&P($)}catch{I.textContent="Delete failed"}}),nn.addEventListener("click",()=>{re.style.display="none",Y.style.display="flex",$&&P($)}),sn.addEventListener("click",async()=>{let e=ot.value.trim();if(!e)return;let t=$+"/"+e;try{let s=await fetch(T("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!s.ok){let i=await s.json();I.textContent=i.error||"Create failed";return}ot.value="",$&&P($)}catch{I.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=lt.classList.toggle("open");Ye.classList.toggle("open",e)}),Ye.addEventListener("click",()=>{lt.classList.remove("open"),Ye.classList.remove("open")});var W="",fe=document.createElement("div");fe.className="session-popover-backdrop",document.body.appendChild(fe);var D=document.createElement("div");D.id="session-popover",D.className="session-popover",D.style.display="none",D.innerHTML=`<div class="sp-field">
36
36
  <label>Rename session</label>
37
37
  <input type="text" id="sp-name" />
38
38
  </div>
@@ -41,5 +41,5 @@ var Xt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
41
41
  <button class="btn" id="sp-cancel">Cancel</button>
42
42
  </div>
43
43
  <hr class="sp-divider" />
44
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(D);function wn(e){let t=e.getBoundingClientRect();D.style.top=t.bottom+4+"px",D.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function bn(e,t){U=e;let s=document.getElementById("sp-name");s.value=e,pe.classList.add("open"),D.style.display="block",wn(t),setTimeout(()=>s.focus(),50)}function fe(){pe.classList.remove("open"),D.style.display="none"}pe.addEventListener("click",fe),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===U){fe();return}try{if(!(await fetch(T("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:U,newName:e})})).ok)return;fe(),J(),w===U&&ce(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+U+'"?'))try{if(!(await fetch(T("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:U})})).ok)return;fe(),w===U&&(h&&(h.destroy(),h=null),w=null,M.style.display="none",_.style.display="flex"),J()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",fe);var Ln={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!h)return;let s=Ln[t.dataset.key];s&&(h.sendInput(s),h.focus())});var S=document.getElementById("mk-input"),Lt=document.getElementById("mk-send");function Et(){if(!S||!h)return;let e=S.value;e&&(h.sendInput(e+"\r"),S.value="",S.rows=1,h.focus())}if(S&&(S.addEventListener("input",()=>{S.rows=1;let e=S.value.split(`
45
- `).length;S.rows=Math.min(e,4)}),S.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Et())})),Lt&&Lt.addEventListener("click",Et),window.visualViewport){let e=document.querySelector(".app-layout"),t=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",t),window.addEventListener("orientationchange",()=>setTimeout(t,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Oe=document.getElementById("process-panel"),me=document.getElementById("proc-list"),En=document.getElementById("proc-close"),Ke=document.getElementById("proc-backdrop"),xn=document.getElementById("header-ram");function kn(){me.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(T("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){me.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let s of e){let i=s.rss,o=i<1048576?(i/1024).toFixed(0)+"K":(i/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+s.pid+'"><span class="proc-mem">'+s.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+m(s.command)+'">'+m(s.command)+'</span><button class="proc-kill" title="Kill PID '+s.pid+'">&times;</button></div>'}me.innerHTML=t;for(let s of me.querySelectorAll(".proc-row")){let i=s.querySelector(".proc-kill");i&&i.addEventListener("click",o=>{let r=o.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(T("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(d=>d.json()).then(d=>{d.ok?r.style.opacity="0.3":alert("Failed: "+(d.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{me.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Cn(){Oe.classList.add("open"),Ke.classList.add("open"),kn()}function $e(){Oe.classList.remove("open"),Ke.classList.remove("open")}xn.addEventListener("click",e=>{e.stopPropagation(),Oe.classList.contains("open")?$e():Cn()}),En.addEventListener("click",$e),Ke.addEventListener("click",$e),window.__openSession=ce,window.__refreshSidebar=J,J(),We(),setInterval(()=>{document.visibilityState==="visible"&&J()},1e4);
44
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(D);function wn(e){let t=e.getBoundingClientRect();D.style.top=t.bottom+4+"px",D.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function bn(e,t){W=e;let s=document.getElementById("sp-name");s.value=e,fe.classList.add("open"),D.style.display="block",wn(t),setTimeout(()=>s.focus(),50)}function me(){fe.classList.remove("open"),D.style.display="none"}fe.addEventListener("click",me),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===W){me();return}try{if(!(await fetch(T("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:W,newName:e})})).ok)return;me(),J(),y===W&&de(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+W+'"?'))try{if(!(await fetch(T("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:W})})).ok)return;me(),y===W&&(h&&(h.destroy(),h=null),y=null,M.style.display="none",Y.style.display="flex"),J()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",me);var Ln={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!h)return;let s=Ln[t.dataset.key];s&&(h.sendInput(s),h.focus())});var S=document.getElementById("mk-input"),Lt=document.getElementById("mk-send");function Et(){if(!S||!h)return;let e=S.value;e&&(h.sendInput(e+"\r"),S.value="",S.rows=1,h.focus())}if(S&&(S.addEventListener("input",()=>{S.rows=1;let e=S.value.split(`
45
+ `).length;S.rows=Math.min(e,4)}),S.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Et())})),Lt&&Lt.addEventListener("click",Et),window.visualViewport){let e=document.querySelector(".app-layout"),t=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",t),window.addEventListener("orientationchange",()=>setTimeout(t,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Oe=document.getElementById("process-panel"),ve=document.getElementById("proc-list"),En=document.getElementById("proc-close"),Ke=document.getElementById("proc-backdrop"),xn=document.getElementById("header-ram");function kn(){ve.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(T("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){ve.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let s of e){let i=s.rss,o=i<1048576?(i/1024).toFixed(0)+"K":(i/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+s.pid+'"><span class="proc-mem">'+s.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+m(s.command)+'">'+m(s.command)+'</span><button class="proc-kill" title="Kill PID '+s.pid+'">&times;</button></div>'}ve.innerHTML=t;for(let s of ve.querySelectorAll(".proc-row")){let i=s.querySelector(".proc-kill");i&&i.addEventListener("click",o=>{let r=o.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(T("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(d=>d.json()).then(d=>{d.ok?r.style.opacity="0.3":alert("Failed: "+(d.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{ve.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Cn(){Oe.classList.add("open"),Ke.classList.add("open"),kn()}function $e(){Oe.classList.remove("open"),Ke.classList.remove("open")}xn.addEventListener("click",e=>{e.stopPropagation(),Oe.classList.contains("open")?$e():Cn()}),En.addEventListener("click",$e),Ke.addEventListener("click",$e),window.__openSession=de,window.__refreshSidebar=J,J(),Ue(),setInterval(()=>{document.visibilityState==="visible"&&J()},1e4);
@@ -1,31 +1,31 @@
1
- import{initTerminal as ke}from"./terminal-core.js";const T=window.__TMUX_WEB_SHELL__;let p=null,u=null;const ie=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null;let v=ie;window.__tmuxWebScopeAgent=ie;let Y="sessions",E="",j="",W="",oe="";function y(e){return v?"/a/"+encodeURIComponent(v)+e:e}const r=document.getElementById("sidebar-content"),C=document.getElementById("main-placeholder"),h=document.getElementById("terminal-container"),H=document.getElementById("file-editor"),Ce=document.getElementById("fe-path"),b=document.getElementById("fe-status"),$=document.getElementById("fe-content"),I=document.getElementById("fe-save"),Ie=document.getElementById("fe-delete"),Be=document.getElementById("fe-back"),ae=document.getElementById("fe-new-name"),xe=document.getElementById("fe-new-btn"),Z=document.getElementById("settings-backdrop"),le=document.getElementById("settings-popover"),ce=document.querySelector(".sidebar");function Me(){return window.matchMedia("(max-width: 640px)").matches}function Q(){Me()&&ce.classList.add("collapsed")}function de(){ce.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{de(),re("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{de(),re("files")});function re(e){Y=e,document.querySelectorAll(".mode-btn").forEach(t=>t.classList.remove("active"));const n=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");n&&n.classList.add("active"),e==="sessions"?(ee="",M()):_e()}let ee="";async function M(){if(Y!=="sessions")return;try{const t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents});if(s===ee)return;ee=s,Se(t)}catch{r.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function Se(e){r.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';const n=e.recent||[],t=document.createElement("div");t.className="sidebar-section-label"+(v?" clickable":""),t.textContent=v?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",v&&(t.title="Back to hub",t.addEventListener("click",()=>{window.location.href="/"})),r.appendChild(t);for(const i of n){const l=document.createElement("div");l.className="session-item"+(i.name===u&&!v?" active":""),l.setAttribute("data-session",i.name),l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${i.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>
1
+ import{initTerminal as ke}from"./terminal-core.js";const T=window.__TMUX_WEB_SHELL__;let p=null,u=null;const ie=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null;let v=ie;window.__tmuxWebScopeAgent=ie;let Y="sessions",E="",j="",W="",oe="";function y(e){return v?"/a/"+encodeURIComponent(v)+e:e}const r=document.getElementById("sidebar-content"),C=document.getElementById("main-placeholder"),h=document.getElementById("terminal-container"),H=document.getElementById("file-editor"),Ce=document.getElementById("fe-path"),b=document.getElementById("fe-status"),$=document.getElementById("fe-content"),I=document.getElementById("fe-save"),Ie=document.getElementById("fe-delete"),xe=document.getElementById("fe-back"),ae=document.getElementById("fe-new-name"),Be=document.getElementById("fe-new-btn"),Z=document.getElementById("settings-backdrop"),le=document.getElementById("settings-popover"),ce=document.querySelector(".sidebar");function Me(){return window.matchMedia("(max-width: 640px)").matches}function Q(){Me()&&ce.classList.add("collapsed")}function de(){ce.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{de(),re("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{de(),re("files")});function re(e){Y=e,document.querySelectorAll(".mode-btn").forEach(t=>t.classList.remove("active"));const n=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");n&&n.classList.add("active"),e==="sessions"?(ee="",M()):Ae()}let ee="";async function M(){if(Y!=="sessions")return;try{const t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents});if(s===ee)return;ee=s,Se(t)}catch{r.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>{const n=window.__openNewSessionModal;typeof n=="function"?n():document.getElementById("new-session-modal")?.classList.add("open")})}function Se(e){r.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';const n=e.recent||[],t=document.createElement("div");t.className="sidebar-section-label"+(v?" clickable":""),t.textContent=v?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",v&&(t.title="Back to hub",t.addEventListener("click",()=>{window.location.href="/"})),r.appendChild(t);for(const i of n){const l=document.createElement("div");l.className="session-item"+(i.name===u&&!v?" active":""),l.setAttribute("data-session",i.name),l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${i.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>
2
2
  <span>${d(i.name)}</span>
3
3
  <button class="session-edit-btn" data-session="${d(i.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>
4
4
  `,l.addEventListener("click",c=>{c.target.closest(".session-edit-btn")||R(i.name)}),r.appendChild(l);const o=l.querySelector(".session-edit-btn");o&&o.addEventListener("click",c=>{c.stopPropagation(),Oe(i.name,o)})}const s=e.agents||[];for(const i of s){if(!i.sessions||i.sessions.length===0)continue;const l=i.agentId===v,o=document.createElement("div");o.className="sidebar-section-label machine-label"+(i.online?"":" agent-offline-label")+(!l&&i.online?" clickable":""),o.textContent=i.agentName+(i.online?"":" (offline)"),!l&&i.online&&(o.title="\u6253\u5F00 "+i.agentName,o.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(i.agentId)+"/"})),r.appendChild(o);for(const c of i.sessions){const a=document.createElement("div");a.className="session-item"+(c.name===u&&v===i.agentId?" active":"")+(i.online?"":" session-offline"),a.setAttribute("data-session",c.name),a.setAttribute("data-agent",i.agentId),a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${c.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>
5
- <span>${d(c.name)}</span><span class="meta">${i.online?"":"offline"}</span>`,i.online&&a.addEventListener("click",g=>{g.target.closest(".session-edit-btn")||R(c.name,i.agentId)}),r.appendChild(a)}}}function d(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function R(e,n){if(!(u===e&&p&&v===(n||null))){H.style.display="none",C.style.display="none",h.style.display="",h.classList.add("terminal-pending"),Q(),p&&(K(),p.destroy(),p=null),u=e,v=n||null,window.__tmuxWebScopeAgent=v;for(const t of r.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e&&(t.getAttribute("data-agent")||null)===v);try{p=await ke(h,e,{terminal:T.terminal,scrollback:T.scrollback,theme:T.theme,wsBase:v?"/ws/a/"+encodeURIComponent(v):T.wsBase||"/ws"}),we()}catch(t){console.error("[shell] terminal init failed:",t);const s=t&&typeof t=="object"&&"message"in t?t.message:String(t);h.textContent="Failed to open terminal for "+e+": "+s,h.classList.remove("terminal-pending")}}}let z=[],f=null,pe=!1;function Te(e){if(!f||!f.files.length)return null;const n=f.repoRoot;if(!n||!e.startsWith(n))return null;const t=e.slice(n.length+1);for(const s of f.files)if(s.path===t)return s.status;return null}async function He(e){pe=!0;try{const n=await fetch(y("/api/git/status?path="+encodeURIComponent(e)));n.ok?f=await n.json():f=null}catch{f=null}finally{pe=!1}}function $e(){if(!f||!f.branch)return"";const{branch:e,linesAdded:n,linesRemoved:t}=f;let s=`<div class="sidebar-git"><div class="git-branch-badge">
5
+ <span>${d(c.name)}</span><span class="meta">${i.online?"":"offline"}</span>`,i.online&&a.addEventListener("click",g=>{g.target.closest(".session-edit-btn")||R(c.name,i.agentId)}),r.appendChild(a)}}}function d(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function R(e,n){if(!(u===e&&p&&v===(n||null))){H.style.display="none",C.style.display="none",h.style.display="",h.classList.add("terminal-pending"),Q(),p&&(K(),p.destroy(),p=null),u=e,v=n||null,window.__tmuxWebScopeAgent=v,window.__tmuxWebCurrentSession=u;for(const t of r.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e&&(t.getAttribute("data-agent")||null)===v);try{p=await ke(h,e,{terminal:T.terminal,scrollback:T.scrollback,theme:T.theme,wsBase:v?"/ws/a/"+encodeURIComponent(v):T.wsBase||"/ws"}),we()}catch(t){console.error("[shell] terminal init failed:",t);const s=t&&typeof t=="object"&&"message"in t?t.message:String(t);h.textContent="Failed to open terminal for "+e+": "+s,h.classList.remove("terminal-pending")}}}let z=[],f=null,pe=!1;function Te(e){if(!f||!f.files.length)return null;const n=f.repoRoot;if(!n||!e.startsWith(n))return null;const t=e.slice(n.length+1);for(const s of f.files)if(s.path===t)return s.status;return null}async function He(e){pe=!0;try{const n=await fetch(y("/api/git/status?path="+encodeURIComponent(e)));n.ok?f=await n.json():f=null}catch{f=null}finally{pe=!1}}function $e(){if(!f||!f.branch)return"";const{branch:e,linesAdded:n,linesRemoved:t}=f;let s=`<div class="sidebar-git"><div class="git-branch-badge">
6
6
  <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>
7
- ${d(e)}</div>`;return(n>0||t>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${n}</span> <span class="del">-${t}</span></div>`),s+"</div>"}r.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&S&&f&&(e.stopPropagation(),ge())});const N=document.getElementById("header-git"),fe=document.getElementById("git-diff-view"),ue=document.getElementById("gd-path"),O=document.getElementById("gd-status"),U=document.getElementById("gd-content"),Re=document.getElementById("gd-back");let S=null;function D(e){if(!e||!e.branch){N.style.display="none",S=null;return}S=e.repoRoot;const{branch:n,linesAdded:t,linesRemoved:s}=e;let i=`<span class="branch">${d(n)}</span>`;(t>0||s>0)&&(i+=`<span class="sep">\xB7</span>+<span class="diff-add">${t}</span> <span class="diff-del">-${s}</span>`),N.innerHTML=i,N.style.display="block"}N.addEventListener("click",e=>{S&&f&&(e.stopPropagation(),ge())});const m=document.getElementById("git-popover"),G=document.getElementById("git-popover-backdrop");function te(){const e=N.getBoundingClientRect();m.style.top=e.bottom+4+"px",m.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function me(){const e=S,n=document.getElementById("git-popover-browse");n&&n.addEventListener("click",()=>{if(q(),e){Y="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));const t=document.getElementById("mode-files");t&&t.classList.add("active"),k(e)}})}let ve="";function ge(){if(!f)return;const e=f.files||[];if(!e.length)m.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
7
+ ${d(e)}</div>`;return(n>0||t>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${n}</span> <span class="del">-${t}</span></div>`),s+"</div>"}r.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&S&&f&&(e.stopPropagation(),ge())});const N=document.getElementById("header-git"),fe=document.getElementById("git-diff-view"),ue=document.getElementById("gd-path"),O=document.getElementById("gd-status"),U=document.getElementById("gd-content"),Re=document.getElementById("gd-back");let S=null;function _(e){if(!e||!e.branch){N.style.display="none",S=null;return}S=e.repoRoot;const{branch:n,linesAdded:t,linesRemoved:s}=e;let i=`<span class="branch">${d(n)}</span>`;(t>0||s>0)&&(i+=`<span class="sep">\xB7</span>+<span class="diff-add">${t}</span> <span class="diff-del">-${s}</span>`),N.innerHTML=i,N.style.display="block"}N.addEventListener("click",e=>{S&&f&&(e.stopPropagation(),ge())});const m=document.getElementById("git-popover"),G=document.getElementById("git-popover-backdrop");function te(){const e=N.getBoundingClientRect();m.style.top=e.bottom+4+"px",m.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function me(){const e=S,n=document.getElementById("git-popover-browse");n&&n.addEventListener("click",()=>{if(q(),e){Y="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));const t=document.getElementById("mode-files");t&&t.classList.add("active"),k(e)}})}let ve="";function ge(){if(!f)return;const e=f.files||[];if(!e.length)m.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
8
8
  <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let n=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let t=0;t<e.length;t++){const s=e[t],i=s.additions||0,l=s.deletions||0;let o="";switch(s.status){case"M":o='<span class="git-file-status M">M</span>';break;case"A":o='<span class="git-file-status A">A</span>';break;case"D":o='<span class="git-file-status D">D</span>';break;case"?":o='<span class="git-file-status ?">?</span>';break;case"R":o='<span class="git-file-status R">R</span>';break}n+=`<div class="git-popover-item" data-idx="${t}">
9
9
  ${o}<span class="file-path">${d(s.path)}</span>
10
10
  <span class="file-add">${i>0?"+"+i:""}</span>
11
11
  <span class="file-del">${l>0?"-"+l:""}</span>
12
12
  </div>`}n+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',ve=n,m.innerHTML=n,m.querySelectorAll(".git-popover-item").forEach(t=>{const s=parseInt(t.dataset.idx||"0",10),i=e[s];!i||i.status==="D"||(t.classList.add("clickable"),t.style.cursor="pointer",t.addEventListener("click",()=>he(i.path)))})}te(),m.classList.add("open"),G.classList.add("open"),me()}function Ne(){return window.innerWidth>=1024}function ye(e,n){const s=(e+(n&&e?`
13
13
  `:"")+n).split(`
14
- `);let i="",l=0,o=0;for(const c of s){const a=c;if(a.startsWith("@@")){const g=a.match(/@@\s+-(\d+)[^+]*\+(\d+)/);g&&(l=parseInt(g[1],10)-1,o=parseInt(g[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${d(a)}</span></div>`;continue}if(a.startsWith("diff --git")||a.startsWith("index ")||a.startsWith("--- ")||a.startsWith("+++ ")||a.startsWith("new file")||a.startsWith("deleted file")){a.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${d(a)}</span></div>`);continue}if(a.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${d(a)}</span></div>`;else if(a.startsWith("-"))l++,i+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${d(a)}</span></div>`;else if(a.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${d(a)}</span></div>`;else{if(!a&&!l&&!o)continue;l++,o++,i+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${o}</span><span class="ln-body">${d(a)}</span></div>`}}return{html:i,lineCount:s.length}}let V=null;async function he(e){const n=S;if(n)if(q(),Ne()){h.style.display="none",p&&(K(),p.destroy(),p=null,V=u,u=null),D(null),H.style.display="none",C.style.display="none",fe.style.display="flex",ue.textContent=e,O.textContent="Loading\u2026",U.innerHTML="";try{const t=await fetch(y("/api/git/diff?path="+encodeURIComponent(n)+"&file="+encodeURIComponent(e)));if(!t.ok)throw new Error(await t.text());const s=await t.json();Pe(e,s.diff,s.stagedDiff)}catch(t){const s=t instanceof Error?t.message:String(t);O.textContent="Error",U.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${d(s)}</div>`}}else{m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
14
+ `);let i="",l=0,o=0;for(const c of s){const a=c;if(a.startsWith("@@")){const g=a.match(/@@\s+-(\d+)[^+]*\+(\d+)/);g&&(l=parseInt(g[1],10)-1,o=parseInt(g[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${d(a)}</span></div>`;continue}if(a.startsWith("diff --git")||a.startsWith("index ")||a.startsWith("--- ")||a.startsWith("+++ ")||a.startsWith("new file")||a.startsWith("deleted file")){a.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${d(a)}</span></div>`);continue}if(a.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${d(a)}</span></div>`;else if(a.startsWith("-"))l++,i+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${d(a)}</span></div>`;else if(a.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${d(a)}</span></div>`;else{if(!a&&!l&&!o)continue;l++,o++,i+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${o}</span><span class="ln-body">${d(a)}</span></div>`}}return{html:i,lineCount:s.length}}let V=null;async function he(e){const n=S;if(n)if(q(),Ne()){h.style.display="none",p&&(K(),p.destroy(),p=null,V=u,u=null),_(null),H.style.display="none",C.style.display="none",fe.style.display="flex",ue.textContent=e,O.textContent="Loading\u2026",U.innerHTML="";try{const t=await fetch(y("/api/git/diff?path="+encodeURIComponent(n)+"&file="+encodeURIComponent(e)));if(!t.ok)throw new Error(await t.text());const s=await t.json();De(e,s.diff,s.stagedDiff)}catch(t){const s=t instanceof Error?t.message:String(t);O.textContent="Error",U.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${d(s)}</div>`}}else{m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
15
15
  <button class="diff-back">\u2190 Back</button>
16
16
  <span class="diff-filename">${d(e)}</span>
17
17
  <span class="diff-loading">Loading\u2026</span>
18
- </div></div>`,te(),m.classList.add("open"),G.classList.add("open");try{const t=await fetch(y("/api/git/diff?path="+encodeURIComponent(n)+"&file="+encodeURIComponent(e)));if(!t.ok)throw new Error(await t.text());const s=await t.json();De(e,s.diff,s.stagedDiff)}catch(t){const s=t instanceof Error?t.message:String(t);m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
18
+ </div></div>`,te(),m.classList.add("open"),G.classList.add("open");try{const t=await fetch(y("/api/git/diff?path="+encodeURIComponent(n)+"&file="+encodeURIComponent(e)));if(!t.ok)throw new Error(await t.text());const s=await t.json();_e(e,s.diff,s.stagedDiff)}catch(t){const s=t instanceof Error?t.message:String(t);m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
19
19
  <button class="diff-back">\u2190 Back</button>
20
20
  <span class="diff-filename">${d(e)}</span>
21
- </div><div class="diff-empty">Error loading diff: ${d(s)}</div></div>`}}}function De(e,n,t){if(!n&&!t){m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
21
+ </div><div class="diff-empty">Error loading diff: ${d(s)}</div></div>`}}}function _e(e,n,t){if(!n&&!t){m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
22
22
  <button class="diff-back">\u2190 Back</button>
23
23
  <span class="diff-filename">${d(e)}</span>
24
24
  </div><div class="diff-empty">No diff content available</div></div>`;return}const{html:s,lineCount:i}=ye(n,t);m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
25
25
  <button class="diff-back">\u2190 Back</button>
26
26
  <span class="diff-filename">${d(e)}</span>
27
27
  <span class="diff-filesize">${i} lines</span>
28
- </div><div class="diff-content">${s}</div></div>`,m.querySelector(".diff-back")?.addEventListener("click",()=>{m.innerHTML=ve,te(),f&&m.querySelectorAll(".git-popover-item").forEach(l=>{const o=parseInt(l.dataset.idx||"0",10),c=f.files[o];!c||c.status==="D"||(l.style.cursor="pointer",l.addEventListener("click",()=>he(c.path)))}),me()})}function Pe(e,n,t){if(!n&&!t){O.textContent="No diff",U.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}const{html:s,lineCount:i}=ye(n,t);ue.textContent=e,O.textContent=i+" lines",U.innerHTML=s}function Ae(){fe.style.display="none",V?(h.style.display="",h.classList.add("terminal-pending"),R(V),V=null):u?(h.style.display="",h.classList.add("terminal-pending"),we()):C.style.display="flex"}Re.addEventListener("click",Ae);function q(){m.classList.remove("open"),G.classList.remove("open")}G.addEventListener("click",q),document.addEventListener("keydown",e=>{e.key==="Escape"&&(q(),X())});async function be(e){try{const t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){const s=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(s.ok){const i=await s.json();f=i,D(i);return}}}catch{}f=null,D(null)}let J=null;function we(){K(),u&&(be(u),J=setInterval(()=>{u&&be(u)},3e4))}function K(){J&&(clearInterval(J),J=null)}function _e(){if(z=T.fsRoots||[],z.length===0){if(u){Fe(u);return}r.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}r.innerHTML="";for(const e of z){const n=document.createElement("div");n.className="file-tree-item",n.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> ${d(e)}`,n.addEventListener("click",()=>k(e)),r.appendChild(n)}}async function Fe(e){r.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const t=await(await fetch(y("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?k(t.path):r.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{r.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function k(e){E=e,r.innerHTML='<div class="file-tree-empty">Loading...</div>';const n=He(e);try{const s=await(await fetch(y("/api/fs/list?path="+encodeURIComponent(e)))).json();await n,r.innerHTML="",D(f),r.innerHTML+=$e();const i=je(e);if(i&&!z.some(c=>e===c)){const c=document.createElement("div");c.className="file-tree-item",c.innerHTML="..",c.addEventListener("click",()=>k(i)),r.appendChild(c)}if(s.dirs)for(const o of s.dirs){const c=o.split("/").pop()||o.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item",a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${d(c)}`,a.addEventListener("click",()=>k(o)),r.appendChild(a)}else r.innerHTML="";const l=s.files||[];for(const o of l){const c=o.split("/").pop()||o.split("\\").pop(),a=Te(o);let g="",x="";if(a)switch(a){case"M":g="git-mod",x='<span class="git-file-status M">M</span>';break;case"A":g="git-add",x='<span class="git-file-status A">A</span>';break;case"D":g="git-del",x='<span class="git-file-status D">D</span>';break;case"?":g="git-untracked",x='<span class="git-file-status ?">?</span>';break;case"R":g="git-mod",x='<span class="git-file-status R">R</span>';break}const F=document.createElement("div");F.className="file-tree-item file"+(g?" "+g:""),F.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${x}${d(c)}`,F.addEventListener("click",()=>We(o)),r.appendChild(F)}}catch{r.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function je(e){const n=e.lastIndexOf("/");return n<=0?null:e.substring(0,n)}async function We(e){j=e,h.style.display="none",p&&(K(),p.destroy(),p=null,u=null),D(null),Q(),C.style.display="none",H.style.display="flex",Ce.textContent=e,b.textContent="Loading...",I.style.display="none";try{const n=await fetch(y("/api/file?path="+encodeURIComponent(e)));if(!n.ok){const s=await n.json();b.textContent=s.error||"Failed to load",$.value="";return}const t=await n.json();$.value=t.content,W=t.content,oe=t.content,b.textContent=t.size+" bytes",I.style.display=""}catch{b.textContent="Failed to load",$.value=""}}$.addEventListener("input",()=>{W=$.value}),I.addEventListener("click",async()=>{I.textContent="Saving...",I.disabled=!0;try{const e=await fetch(y("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:j,content:W})});if(!e.ok){const n=await e.json();b.textContent=n.error||"Save failed";return}oe=W,b.textContent="Saved"}catch{b.textContent="Save failed"}finally{I.disabled=!1,I.textContent="Save"}}),Ie.addEventListener("click",async()=>{if(confirm("Delete "+j+"?"))try{const e=await fetch(y("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:j})});if(!e.ok){const n=await e.json();b.textContent=n.error||"Delete failed";return}H.style.display="none",C.style.display="flex",E&&k(E)}catch{b.textContent="Delete failed"}}),Be.addEventListener("click",()=>{H.style.display="none",C.style.display="flex",E&&k(E)}),xe.addEventListener("click",async()=>{const e=ae.value.trim();if(!e)return;const n=E+"/"+e;try{const t=await fetch(y("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:n})});if(!t.ok){const s=await t.json();b.textContent=s.error||"Create failed";return}ae.value="",E&&k(E)}catch{b.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=le.classList.toggle("open");Z.classList.toggle("open",e)}),Z.addEventListener("click",()=>{le.classList.remove("open"),Z.classList.remove("open")});let B="";const P=document.createElement("div");P.className="session-popover-backdrop",document.body.appendChild(P);const L=document.createElement("div");L.id="session-popover",L.className="session-popover",L.style.display="none",L.innerHTML=`<div class="sp-field">
28
+ </div><div class="diff-content">${s}</div></div>`,m.querySelector(".diff-back")?.addEventListener("click",()=>{m.innerHTML=ve,te(),f&&m.querySelectorAll(".git-popover-item").forEach(l=>{const o=parseInt(l.dataset.idx||"0",10),c=f.files[o];!c||c.status==="D"||(l.style.cursor="pointer",l.addEventListener("click",()=>he(c.path)))}),me()})}function De(e,n,t){if(!n&&!t){O.textContent="No diff",U.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}const{html:s,lineCount:i}=ye(n,t);ue.textContent=e,O.textContent=i+" lines",U.innerHTML=s}function Pe(){fe.style.display="none",V?(h.style.display="",h.classList.add("terminal-pending"),R(V),V=null):u?(h.style.display="",h.classList.add("terminal-pending"),we()):C.style.display="flex"}Re.addEventListener("click",Pe);function q(){m.classList.remove("open"),G.classList.remove("open")}G.addEventListener("click",q),document.addEventListener("keydown",e=>{e.key==="Escape"&&(q(),X())});async function be(e){try{const t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){const s=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(s.ok){const i=await s.json();f=i,_(i);return}}}catch{}f=null,_(null)}let J=null;function we(){K(),u&&(be(u),J=setInterval(()=>{u&&be(u)},3e4))}function K(){J&&(clearInterval(J),J=null)}function Ae(){if(z=T.fsRoots||[],z.length===0){if(u){Fe(u);return}r.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}r.innerHTML="";for(const e of z){const n=document.createElement("div");n.className="file-tree-item",n.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> ${d(e)}`,n.addEventListener("click",()=>k(e)),r.appendChild(n)}}async function Fe(e){r.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const t=await(await fetch(y("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?k(t.path):r.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{r.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function k(e){E=e,r.innerHTML='<div class="file-tree-empty">Loading...</div>';const n=He(e);try{const s=await(await fetch(y("/api/fs/list?path="+encodeURIComponent(e)))).json();await n,r.innerHTML="",_(f),r.innerHTML+=$e();const i=je(e);if(i&&!z.some(c=>e===c)){const c=document.createElement("div");c.className="file-tree-item",c.innerHTML="..",c.addEventListener("click",()=>k(i)),r.appendChild(c)}if(s.dirs)for(const o of s.dirs){const c=o.split("/").pop()||o.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item",a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${d(c)}`,a.addEventListener("click",()=>k(o)),r.appendChild(a)}else r.innerHTML="";const l=s.files||[];for(const o of l){const c=o.split("/").pop()||o.split("\\").pop(),a=Te(o);let g="",B="";if(a)switch(a){case"M":g="git-mod",B='<span class="git-file-status M">M</span>';break;case"A":g="git-add",B='<span class="git-file-status A">A</span>';break;case"D":g="git-del",B='<span class="git-file-status D">D</span>';break;case"?":g="git-untracked",B='<span class="git-file-status ?">?</span>';break;case"R":g="git-mod",B='<span class="git-file-status R">R</span>';break}const F=document.createElement("div");F.className="file-tree-item file"+(g?" "+g:""),F.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${B}${d(c)}`,F.addEventListener("click",()=>We(o)),r.appendChild(F)}}catch{r.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function je(e){const n=e.lastIndexOf("/");return n<=0?null:e.substring(0,n)}async function We(e){j=e,h.style.display="none",p&&(K(),p.destroy(),p=null,u=null),_(null),Q(),C.style.display="none",H.style.display="flex",Ce.textContent=e,b.textContent="Loading...",I.style.display="none";try{const n=await fetch(y("/api/file?path="+encodeURIComponent(e)));if(!n.ok){const s=await n.json();b.textContent=s.error||"Failed to load",$.value="";return}const t=await n.json();$.value=t.content,W=t.content,oe=t.content,b.textContent=t.size+" bytes",I.style.display=""}catch{b.textContent="Failed to load",$.value=""}}$.addEventListener("input",()=>{W=$.value}),I.addEventListener("click",async()=>{I.textContent="Saving...",I.disabled=!0;try{const e=await fetch(y("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:j,content:W})});if(!e.ok){const n=await e.json();b.textContent=n.error||"Save failed";return}oe=W,b.textContent="Saved"}catch{b.textContent="Save failed"}finally{I.disabled=!1,I.textContent="Save"}}),Ie.addEventListener("click",async()=>{if(confirm("Delete "+j+"?"))try{const e=await fetch(y("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:j})});if(!e.ok){const n=await e.json();b.textContent=n.error||"Delete failed";return}H.style.display="none",C.style.display="flex",E&&k(E)}catch{b.textContent="Delete failed"}}),xe.addEventListener("click",()=>{H.style.display="none",C.style.display="flex",E&&k(E)}),Be.addEventListener("click",async()=>{const e=ae.value.trim();if(!e)return;const n=E+"/"+e;try{const t=await fetch(y("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:n})});if(!t.ok){const s=await t.json();b.textContent=s.error||"Create failed";return}ae.value="",E&&k(E)}catch{b.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=le.classList.toggle("open");Z.classList.toggle("open",e)}),Z.addEventListener("click",()=>{le.classList.remove("open"),Z.classList.remove("open")});let x="";const D=document.createElement("div");D.className="session-popover-backdrop",document.body.appendChild(D);const L=document.createElement("div");L.id="session-popover",L.className="session-popover",L.style.display="none",L.innerHTML=`<div class="sp-field">
29
29
  <label>Rename session</label>
30
30
  <input type="text" id="sp-name" />
31
31
  </div>
@@ -34,5 +34,5 @@ import{initTerminal as ke}from"./terminal-core.js";const T=window.__TMUX_WEB_SHE
34
34
  <button class="btn" id="sp-cancel">Cancel</button>
35
35
  </div>
36
36
  <hr class="sp-divider" />
37
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(L);function ze(e){const n=e.getBoundingClientRect();L.style.top=n.bottom+4+"px",L.style.left=Math.max(8,Math.min(n.left,window.innerWidth-240))+"px"}function Oe(e,n){B=e;const t=document.getElementById("sp-name");t.value=e,P.classList.add("open"),L.style.display="block",ze(n),setTimeout(()=>t.focus(),50)}function A(){P.classList.remove("open"),L.style.display="none"}P.addEventListener("click",A),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===B){A();return}try{if(!(await fetch(y("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:B,newName:e})})).ok)return;A(),M(),u===B&&R(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+B+'"?'))try{if(!(await fetch(y("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B})})).ok)return;A(),u===B&&(p&&(p.destroy(),p=null),u=null,h.style.display="none",C.style.display="flex"),M()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",A);const Ue={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const n=e.target.closest("button[data-key]");if(!n||!p)return;const t=Ue[n.dataset.key];t&&(p.sendInput(t),p.focus())});const w=document.getElementById("mk-input"),Le=document.getElementById("mk-send");function Ee(){if(!w||!p)return;const e=w.value;e&&(p.sendInput(e+"\r"),w.value="",w.rows=1,p.focus())}if(w&&(w.addEventListener("input",()=>{w.rows=1;const e=w.value.split(`
38
- `).length;w.rows=Math.min(e,4)}),w.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Ee())})),Le&&Le.addEventListener("click",Ee),window.visualViewport){const e=document.querySelector(".app-layout"),n=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",n),window.addEventListener("orientationchange",()=>setTimeout(n,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}const ne=document.getElementById("process-panel"),_=document.getElementById("proc-list"),Ge=document.getElementById("proc-close"),se=document.getElementById("proc-backdrop"),Ve=document.getElementById("header-ram");function qe(){_.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(y("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){_.innerHTML='<div class="proc-empty">No processes</div>';return}let n="";for(const t of e){const s=t.rss,i=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";n+='<div class="proc-row" data-pid="'+t.pid+'"><span class="proc-mem">'+t.mem+'%</span><span class="proc-rss">'+i+'</span><span class="proc-cmd" title="'+d(t.command)+'">'+d(t.command)+'</span><button class="proc-kill" title="Kill PID '+t.pid+'">&times;</button></div>'}_.innerHTML=n;for(const t of _.querySelectorAll(".proc-row")){const s=t.querySelector(".proc-kill");s&&s.addEventListener("click",i=>{const l=i.target.closest(".proc-row");!l||!confirm("Kill PID "+l.dataset.pid+"?")||fetch(y("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(l.dataset.pid,10)})}).then(o=>o.json()).then(o=>{o.ok?l.style.opacity="0.3":alert("Failed: "+(o.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{_.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Je(){ne.classList.add("open"),se.classList.add("open"),qe()}function X(){ne.classList.remove("open"),se.classList.remove("open")}Ve.addEventListener("click",e=>{e.stopPropagation(),ne.classList.contains("open")?X():Je()}),Ge.addEventListener("click",X),se.addEventListener("click",X),window.__openSession=R,window.__refreshSidebar=M,M(),Q(),setInterval(()=>{document.visibilityState==="visible"&&M()},1e4);
37
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(L);function ze(e){const n=e.getBoundingClientRect();L.style.top=n.bottom+4+"px",L.style.left=Math.max(8,Math.min(n.left,window.innerWidth-240))+"px"}function Oe(e,n){x=e;const t=document.getElementById("sp-name");t.value=e,D.classList.add("open"),L.style.display="block",ze(n),setTimeout(()=>t.focus(),50)}function P(){D.classList.remove("open"),L.style.display="none"}D.addEventListener("click",P),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===x){P();return}try{if(!(await fetch(y("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:x,newName:e})})).ok)return;P(),M(),u===x&&R(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+x+'"?'))try{if(!(await fetch(y("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:x})})).ok)return;P(),u===x&&(p&&(p.destroy(),p=null),u=null,h.style.display="none",C.style.display="flex"),M()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",P);const Ue={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const n=e.target.closest("button[data-key]");if(!n||!p)return;const t=Ue[n.dataset.key];t&&(p.sendInput(t),p.focus())});const w=document.getElementById("mk-input"),Le=document.getElementById("mk-send");function Ee(){if(!w||!p)return;const e=w.value;e&&(p.sendInput(e+"\r"),w.value="",w.rows=1,p.focus())}if(w&&(w.addEventListener("input",()=>{w.rows=1;const e=w.value.split(`
38
+ `).length;w.rows=Math.min(e,4)}),w.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Ee())})),Le&&Le.addEventListener("click",Ee),window.visualViewport){const e=document.querySelector(".app-layout"),n=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",n),window.addEventListener("orientationchange",()=>setTimeout(n,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}const ne=document.getElementById("process-panel"),A=document.getElementById("proc-list"),Ge=document.getElementById("proc-close"),se=document.getElementById("proc-backdrop"),Ve=document.getElementById("header-ram");function qe(){A.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(y("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){A.innerHTML='<div class="proc-empty">No processes</div>';return}let n="";for(const t of e){const s=t.rss,i=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";n+='<div class="proc-row" data-pid="'+t.pid+'"><span class="proc-mem">'+t.mem+'%</span><span class="proc-rss">'+i+'</span><span class="proc-cmd" title="'+d(t.command)+'">'+d(t.command)+'</span><button class="proc-kill" title="Kill PID '+t.pid+'">&times;</button></div>'}A.innerHTML=n;for(const t of A.querySelectorAll(".proc-row")){const s=t.querySelector(".proc-kill");s&&s.addEventListener("click",i=>{const l=i.target.closest(".proc-row");!l||!confirm("Kill PID "+l.dataset.pid+"?")||fetch(y("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(l.dataset.pid,10)})}).then(o=>o.json()).then(o=>{o.ok?l.style.opacity="0.3":alert("Failed: "+(o.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{A.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Je(){ne.classList.add("open"),se.classList.add("open"),qe()}function X(){ne.classList.remove("open"),se.classList.remove("open")}Ve.addEventListener("click",e=>{e.stopPropagation(),ne.classList.contains("open")?X():Je()}),Ge.addEventListener("click",X),se.addEventListener("click",X),window.__openSession=R,window.__refreshSidebar=M,M(),Q(),setInterval(()=>{document.visibilityState==="visible"&&M()},1e4);
@@ -1,8 +1,8 @@
1
- const Oe=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function ne(s){return s.replace(/\x1b\[\?1049[hl]/g,"")}function N(s){return s.replace(Oe,"")}let R;function Ne(){return R||(document.querySelector("link[data-tmux-web-xterm-css]")?R=Promise.resolve():(R=new Promise(o=>{const i=document.createElement("link");i.rel="stylesheet",i.href="/assets/xterm.css",i.setAttribute("data-tmux-web-xterm-css",""),i.addEventListener("load",()=>o(),{once:!0}),i.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(i)}),R))}class oe{terminal;fitAddon;container;constructor(o,i,n){this.container=o,this.terminal=i,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(o)}static async create(o,i,n){const[{Terminal:a},{FitAddon:f}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Ne()]),h=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,smoothScrollDuration:120,theme:n});return new oe(o,h,new f)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(o){return new Promise(i=>this.terminal.write(o,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(o,i=!1){this.terminal.scrollToLine(Math.max(0,o),i)}scrollLines(o){const i=this.terminal.buffer?.active;this.terminal.scrollLines(o)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const o=Re(this.container);return o.width<=0||o.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(o){this.terminal.paste(o)}input(o){this.terminal.input(o,!1)}onData(o){this.terminal.onData(o),this.terminal.onBinary(o)}onResize(o){this.terminal.onResize(o)}onScroll(o){this.terminal.onScroll(o)}attachCustomKeyEventHandler(o){this.terminal.attachCustomKeyEventHandler(o)}isFocused(){const o=document.activeElement;return o?o===this.container||o===this.terminal.textarea||this.container.contains(o)||!!o.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(o,i){const n=this.terminal.buffer.active,[a,f]=o<=i?[o,i]:[i,o],h=n.viewportY,l=[];for(let d=a;d<=f;d++){const g=n.getLine(h+d);l.push(g?g.translateToString(!0):"")}return l.join(`
2
- `)}selectRows(o,i){const n=this.terminal.buffer.active,[a,f]=o<=i?[o,i]:[i,o];this.terminal.selectLines(n.viewportY+a,n.viewportY+f)}selectCells(o,i,n,a){const f=this.terminal.buffer.active,h=Math.min(i,a),l=Math.max(i,a);if(h===l){const d=Math.min(o,n),g=Math.max(o,n);this.terminal.select(d,f.viewportY+h,g-d+1)}else this.terminal.selectLines(f.viewportY+h,f.viewportY+l)}getCellsText(o,i,n,a){const f=this.terminal.buffer.active,h=Math.min(i,a),l=Math.max(i,a),d=i<a?o:i>a?n:Math.min(o,n),g=i<a?n:i>a?o:Math.max(o,n),$=f.viewportY,S=[];for(let E=h;E<=l;E++){const X=f.getLine($+E);if(!X){S.push("");continue}const D=E===h?d:0,Q=E===l?g:this.terminal.cols-1;let P="";for(let w=D;w<=Q;w++){const L=X.getCell(w);L&&(P+=L.getChars())}S.push(P.replace(/\s+$/,""))}return S.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function Re(s){const o=s.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(o.width,i.width),height:Math.max(0,Math.min(o.height,i.height-Math.max(0,o.top)))}:o}function Xe(s,o,i){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{const n=await oe.create(s,i.scrollback,i.theme);let a,f=0,h,l=null,d=0,g=0;const $=.2,S=10,E=.95,X=.04;function D(){d&&(cancelAnimationFrame(d),d=0),g=0}function Q(e){if(D(),Math.abs(e)<$)return;g=Math.max(-S,Math.min(S,e));const t=()=>{if(b){d=0;return}const r=s.querySelector(".xterm-rows > div"),u=r?r.getBoundingClientRect().height:16;let c=u>0?Math.round(g*16/u):g>0?1:-1;c===0&&(c=g>0?1:-1);const v=n.viewportY()-c,G=n.baseY(),O=Math.max(0,Math.min(v,G));if(n.scrollToLine(O,!0),g*=E,O!==v){d=0;return}if(Math.abs(g)<X){d=0;return}d=requestAnimationFrame(t)};d=requestAnimationFrame(t)}let P=0,w="connecting",L=0,q=!1,U=[],I="",b=!1,Y=1e3;const me=location.protocol==="https:"?"wss:":"ws:",pe=i.wsBase||"/ws",he=me+"//"+location.host+pe+"/"+encodeURIComponent(o),ge="/api/session/"+encodeURIComponent(o)+"/upload",we=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function ye(){return U.join("")+I}async function ve(e,t=0){const r=e?n.viewportY():0,u=e?n.baseY():0,c=N(ye());if(n.reset(),!c){e||n.scrollToBottom();return}await n.write(c),e?n.scrollToLine(u+r+t):n.scrollToBottom()}function C(e){a?.readyState===WebSocket.OPEN&&a.send(JSON.stringify(e))}function Te(e){let t;try{t=JSON.parse(e)}catch{w==="live"&&(I+=e,n.write(N(e)));return}if(t.type==="auth.required"){const r=localStorage.getItem("tmux-web-token");r?C({type:"auth.token",token:r}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(t.type==="attach_failed"&&typeof t.message=="string"){_=!0,w="dead",n.reset(),n.write(`\r
4
- \x1B[31m`+N(t.message)+`\x1B[0m\r
5
- `).then(()=>{H();try{a?.close()}catch{}});return}if(t.type==="auth.ok"){t.token&&localStorage.setItem("tmux-web-token",t.token),Y=1e3,M(),C({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(t.type==="auth.failed"){if(t.permanentLock||t.retryAfterMs&&t.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(t.message);return}return}if(t.type==="snapshot"&&typeof t.data=="string"){const r=ne(N(t.data));U=[r],I="",L=typeof t.lines=="number"?t.lines:i.terminal.initialLines,w="live",n.reset(),M(),C({type:"resize",cols:n.cols,rows:n.rows}),n.write(r).then(()=>n.scrollToBottom());return}if(t.type==="window_changed"&&typeof t.activeIndex=="number"){const r=new URL(location.href);r.searchParams.get("window")!==String(t.activeIndex)&&(r.searchParams.set("window",String(t.activeIndex)),history.replaceState(history.state,"",r)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:t.windows}));return}if(t.type==="history"&&typeof t.data=="string"){q=!1,t.lines>0&&t.data&&(U.unshift(ne(t.data)),L+=t.lines,ve(!0,t.lines));return}if(t.type==="data"&&typeof t.data=="string"){const r=ne(N(t.data));if(w==="connecting"){w="live",I=r,n.write(r);return}w==="live"&&(I+=r,n.write(r))}}let _=!1;function re(){if(_)return;let e=he;const t=localStorage.getItem("tmux-web-token");t&&(e+=(e.includes("?")?"&":"?")+"token="+encodeURIComponent(t)),e+=(e.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,a=new WebSocket(e),a.onopen=()=>{w="connecting",L=0,q=!1,U=[],I="",n.reset(),Y=1e3},a.onmessage=r=>{typeof r.data=="string"&&Te(r.data)},a.onclose=()=>{b||(w="connecting",!_&&setTimeout(()=>{Y=Math.min(Y*2,1e4),!b&&!_&&re()},Y))},a.onerror=()=>a?.close()}function j(e){C({type:"input",data:e})}function M(){return n.fit()}function H(){s.classList.remove("terminal-pending")}function y(){f&&cancelAnimationFrame(f),h&&clearTimeout(h),M(),f=requestAnimationFrame(()=>{f=0,M()&&H()}),h=setTimeout(()=>{M()&&H()},120)}function xe(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function ie(e){return typeof e=="string"&&e.startsWith("image/")}function se(e){return e?.files?.length?e.files[0]??null:null}function be(e){if(!e?.items)return null;for(let t=0;t<e.items.length;t++){const r=e.items[t];if(r?.kind==="file"&&ie(r.type))return r.getAsFile()}return null}async function Ee(e){const t=new FormData,r=e instanceof File&&e.name?e.name:"upload";t.append("file",e,r);const u=localStorage.getItem("tmux-web-token"),c={};u&&(c.Authorization="Bearer "+u);const x=await fetch(ge,{method:"POST",body:t,headers:c});if(!x.ok){let G="upload failed";try{const O=await x.json();O.error&&(G=O.error)}catch{}throw new Error(G)}const v=await x.json();if(!v.path)throw new Error("no path in response");return v.path}async function Z(e){try{const t=await Ee(e);j(t)}catch{}}n.onData(e=>j(e)),n.onResize(({cols:e,rows:t})=>C({type:"resize",cols:e,rows:t})),n.onScroll(()=>{w!=="live"||q||!n.isNearScrollbackTop()||(q=!0,C({type:"load_history",before:L}))});let F=!1,A=!1,m=null,p=null;const K=document.createElement("div");K.className="terminal-copy-banner",K.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',K.hidden=!0,s.appendChild(K);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,s.appendChild(T);function ae(e,t){const r=s.querySelector(".xterm-screen, canvas"),c=(r&&r.getBoundingClientRect().width>0?r:s).getBoundingClientRect(),x=c.height>0&&n.rows>0?c.height/n.rows:18,v=c.width>0&&n.cols>0?c.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((t-c.top)/x))),col:Math.max(0,Math.min(n.cols-1,Math.floor((e-c.left)/v)))}}async function V(e){if(!e)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e);return}}catch{}const t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",t.style.pointerEvents="none",document.body.appendChild(t),t.focus(),t.select();try{document.execCommand("copy")}catch{}t.remove()}let B;function J(){T.hidden=!1,B&&clearTimeout(B),B=setTimeout(()=>{T.hidden=!0},1e3)}function k(e){F=e,K.hidden=!e,s.classList.toggle("terminal-copy-mode-active",e),e&&n.focus()}function le(){k(!F)}function Le(e){b||e.button!==0||A||(F||e.shiftKey)&&(e.preventDefault(),e.stopPropagation(),A=!0,m=ae(e.clientX,e.clientY),p=m,n.selectCells(m.col,m.row,p.col,p.row),document.addEventListener("mousemove",ee,!0),document.addEventListener("mouseup",te,!0))}function ee(e){b||!A||!m||(e.preventDefault(),e.stopPropagation(),p=ae(e.clientX,e.clientY),n.selectCells(m.col,m.row,p.col,p.row))}function te(e){if(b||!A||!m||!p)return;e.preventDefault(),e.stopPropagation(),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),A=!1;const t=n.getCellsText(m.col,m.row,p.col,p.row);m=null,p=null,n.clearSelection(),k(!1),t&&(T.textContent="\u5DF2\u590D\u5236",V(t).then(J)),n.focus()}s.addEventListener("mousedown",Le,!0),document.addEventListener("keydown",Ce,!0);function Ce(e){if(!(b||!n.isFocused()||e.defaultPrevented||e.isComposing)){if(F){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),m=null,p=null,n.clearSelection(),k(!1);return}if(e.key==="Enter"||(e.ctrlKey||e.metaKey)&&e.code==="KeyC"){e.preventDefault(),e.stopPropagation(),V(n.getScreenText()).then(J),k(!1);return}}if(e.altKey&&e.code==="KeyC"&&!e.ctrlKey&&!e.metaKey){if(e.preventDefault(),e.stopPropagation(),m&&p){const t=n.getCellsText(m.col,m.row,p.col,p.row);m=null,p=null,n.clearSelection(),k(!1),t&&(T.textContent="Copied",V(t).then(J))}else{const t=n.getSelection();t?(n.clearSelection(),T.textContent="Copied",V(t).then(J)):le()}return}if((e.ctrlKey||e.metaKey)&&e.code==="KeyV"){e.preventDefault(),e.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const t=await navigator.clipboard.read();for(const r of t)for(const u of r.types)if(ie(u)){const c=await r.getType(u);await Z(c);return}}}catch{}try{const t=await navigator.clipboard?.readText();t&&n.pasteText(ce(t))}catch{}})();return}}}let W=0;function ce(e){return e.split(`\r
1
+ const Oe=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function ne(s){return s.replace(/\x1b\[\?1049[hl]/g,"")}function R(s){return s.replace(Oe,"")}let X;function Ne(){return X||(document.querySelector("link[data-tmux-web-xterm-css]")?X=Promise.resolve():(X=new Promise(o=>{const i=document.createElement("link");i.rel="stylesheet",i.href="/assets/xterm.css",i.setAttribute("data-tmux-web-xterm-css",""),i.addEventListener("load",()=>o(),{once:!0}),i.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(i)}),X))}class oe{terminal;fitAddon;container;constructor(o,i,n){this.container=o,this.terminal=i,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(o)}static async create(o,i,n){const[{Terminal:a},{FitAddon:f}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Ne()]),h=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,smoothScrollDuration:120,theme:n});return new oe(o,h,new f)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(o){return new Promise(i=>this.terminal.write(o,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(o,i=!1){this.terminal.scrollToLine(Math.max(0,o),i)}scrollLines(o){const i=this.terminal.buffer?.active;this.terminal.scrollLines(o)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const o=Re(this.container);return o.width<=0||o.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(o){this.terminal.paste(o)}input(o){this.terminal.input(o,!1)}onData(o){this.terminal.onData(o),this.terminal.onBinary(o)}onResize(o){this.terminal.onResize(o)}onScroll(o){this.terminal.onScroll(o)}attachCustomKeyEventHandler(o){this.terminal.attachCustomKeyEventHandler(o)}isFocused(){const o=document.activeElement;return o?o===this.container||o===this.terminal.textarea||this.container.contains(o)||!!o.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(o,i){const n=this.terminal.buffer.active,[a,f]=o<=i?[o,i]:[i,o],h=n.viewportY,l=[];for(let d=a;d<=f;d++){const g=n.getLine(h+d);l.push(g?g.translateToString(!0):"")}return l.join(`
2
+ `)}selectRows(o,i){const n=this.terminal.buffer.active,[a,f]=o<=i?[o,i]:[i,o];this.terminal.selectLines(n.viewportY+a,n.viewportY+f)}selectCells(o,i,n,a){const f=this.terminal.buffer.active,h=Math.min(i,a),l=Math.max(i,a);if(h===l){const d=Math.min(o,n),g=Math.max(o,n);this.terminal.select(d,f.viewportY+h,g-d+1)}else this.terminal.selectLines(f.viewportY+h,f.viewportY+l)}getCellsText(o,i,n,a){const f=this.terminal.buffer.active,h=Math.min(i,a),l=Math.max(i,a),d=i<a?o:i>a?n:Math.min(o,n),g=i<a?n:i>a?o:Math.max(o,n),$=f.viewportY,M=[];for(let E=h;E<=l;E++){const q=f.getLine($+E);if(!q){M.push("");continue}const D=E===h?d:0,Q=E===l?g:this.terminal.cols-1;let P="";for(let w=D;w<=Q;w++){const L=q.getCell(w);L&&(P+=L.getChars())}M.push(P.replace(/\s+$/,""))}return M.join(`
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function Re(s){const o=s.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(o.width,i.width),height:Math.max(0,Math.min(o.height,i.height-Math.max(0,o.top)))}:o}function Xe(s,o,i){return s.innerHTML="",s.classList.add("terminal-pending"),(async()=>{const n=await oe.create(s,i.scrollback,i.theme);let a,f=0,h,l=null,d=0,g=0;const $=.2,M=10,E=.95,q=.04;function D(){d&&(cancelAnimationFrame(d),d=0),g=0}function Q(e){if(D(),Math.abs(e)<$)return;g=Math.max(-M,Math.min(M,e));const t=()=>{if(b){d=0;return}const r=s.querySelector(".xterm-rows > div"),c=r?r.getBoundingClientRect().height:16;let u=c>0?Math.round(g*16/c):g>0?1:-1;u===0&&(u=g>0?1:-1);const v=n.viewportY()-u,G=n.baseY(),N=Math.max(0,Math.min(v,G));if(n.scrollToLine(N,!0),g*=E,N!==v){d=0;return}if(Math.abs(g)<q){d=0;return}d=requestAnimationFrame(t)};d=requestAnimationFrame(t)}let P=0,w="connecting",L=0,Y=!1,U=[],I="",b=!1,F=1e3;const me=location.protocol==="https:"?"wss:":"ws:",pe=i.wsBase||"/ws",he=me+"//"+location.host+pe+"/"+encodeURIComponent(o),ge="/api/session/"+encodeURIComponent(o)+"/upload",we=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function ye(){return U.join("")+I}async function ve(e,t=0){const r=e?n.viewportY():0,c=R(ye());if(n.reset(),!c){e||n.scrollToBottom();return}await n.write(c),e?n.scrollToLine(t+r):n.scrollToBottom()}function C(e){a?.readyState===WebSocket.OPEN&&a.send(JSON.stringify(e))}function Te(e){let t;try{t=JSON.parse(e)}catch{w==="live"&&(I+=e,n.write(R(e)));return}if(t.type==="auth.required"){const r=localStorage.getItem("tmux-web-token");r?C({type:"auth.token",token:r}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(t.type==="attach_failed"&&typeof t.message=="string"){_=!0,w="dead",n.reset(),n.write(`\r
4
+ \x1B[31m`+R(t.message)+`\x1B[0m\r
5
+ `).then(()=>{H();try{a?.close()}catch{}});return}if(t.type==="auth.ok"){t.token&&localStorage.setItem("tmux-web-token",t.token),F=1e3,S(),C({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(t.type==="auth.failed"){if(t.permanentLock||t.retryAfterMs&&t.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(t.message);return}return}if(t.type==="snapshot"&&typeof t.data=="string"){const r=ne(R(t.data));U=[r],I="",L=typeof t.lines=="number"?t.lines:i.terminal.initialLines,w="live",n.reset(),S(),C({type:"resize",cols:n.cols,rows:n.rows}),n.write(r).then(()=>n.scrollToBottom());return}if(t.type==="window_changed"&&typeof t.activeIndex=="number"){const r=new URL(location.href);r.searchParams.get("window")!==String(t.activeIndex)&&(r.searchParams.set("window",String(t.activeIndex)),history.replaceState(history.state,"",r)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:t.windows}));return}if(t.type==="history"&&typeof t.data=="string"){t.lines>0&&t.data?(U.unshift(ne(t.data)),L+=t.lines,ve(!0,t.lines).finally(()=>{Y=!1})):Y=!1;return}if(t.type==="data"&&typeof t.data=="string"){const r=ne(R(t.data));if(w==="connecting"){w="live",I=r,n.write(r);return}w==="live"&&(I+=r,n.write(r))}}let _=!1;function re(){if(_)return;let e=he;const t=localStorage.getItem("tmux-web-token");t&&(e+=(e.includes("?")?"&":"?")+"token="+encodeURIComponent(t)),e+=(e.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,a=new WebSocket(e),a.onopen=()=>{w="connecting",L=0,Y=!1,U=[],I="",n.reset(),F=1e3},a.onmessage=r=>{typeof r.data=="string"&&Te(r.data)},a.onclose=()=>{b||(w="connecting",!_&&setTimeout(()=>{F=Math.min(F*2,1e4),!b&&!_&&re()},F))},a.onerror=()=>a?.close()}function j(e){C({type:"input",data:e})}function S(){return n.fit()}function H(){s.classList.remove("terminal-pending")}function y(){f&&cancelAnimationFrame(f),h&&clearTimeout(h),S(),f=requestAnimationFrame(()=>{f=0,S()&&H()}),h=setTimeout(()=>{S()&&H()},120)}function xe(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function ie(e){return typeof e=="string"&&e.startsWith("image/")}function se(e){return e?.files?.length?e.files[0]??null:null}function be(e){if(!e?.items)return null;for(let t=0;t<e.items.length;t++){const r=e.items[t];if(r?.kind==="file"&&ie(r.type))return r.getAsFile()}return null}async function Ee(e){const t=new FormData,r=e instanceof File&&e.name?e.name:"upload";t.append("file",e,r);const c=localStorage.getItem("tmux-web-token"),u={};c&&(u.Authorization="Bearer "+c);const x=await fetch(ge,{method:"POST",body:t,headers:u});if(!x.ok){let G="upload failed";try{const N=await x.json();N.error&&(G=N.error)}catch{}throw new Error(G)}const v=await x.json();if(!v.path)throw new Error("no path in response");return v.path}async function Z(e){try{const t=await Ee(e);j(t)}catch{}}n.onData(e=>j(e)),n.onResize(({cols:e,rows:t})=>C({type:"resize",cols:e,rows:t})),n.onScroll(()=>{w!=="live"||Y||!n.isNearScrollbackTop()||(Y=!0,C({type:"load_history",before:L}))});let A=!1,K=!1,m=null,p=null;const B=document.createElement("div");B.className="terminal-copy-banner",B.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',B.hidden=!0,s.appendChild(B);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,s.appendChild(T);function ae(e,t){const r=s.querySelector(".xterm-screen, canvas"),u=(r&&r.getBoundingClientRect().width>0?r:s).getBoundingClientRect(),x=u.height>0&&n.rows>0?u.height/n.rows:18,v=u.width>0&&n.cols>0?u.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((t-u.top)/x))),col:Math.max(0,Math.min(n.cols-1,Math.floor((e-u.left)/v)))}}async function V(e){if(!e)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e);return}}catch{}const t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",t.style.pointerEvents="none",document.body.appendChild(t),t.focus(),t.select();try{document.execCommand("copy")}catch{}t.remove()}let z;function J(){T.hidden=!1,z&&clearTimeout(z),z=setTimeout(()=>{T.hidden=!0},1e3)}function k(e){A=e,B.hidden=!e,s.classList.toggle("terminal-copy-mode-active",e),e&&n.focus()}function le(){k(!A)}function Le(e){b||e.button!==0||K||(A||e.shiftKey)&&(e.preventDefault(),e.stopPropagation(),K=!0,m=ae(e.clientX,e.clientY),p=m,n.selectCells(m.col,m.row,p.col,p.row),document.addEventListener("mousemove",ee,!0),document.addEventListener("mouseup",te,!0))}function ee(e){b||!K||!m||(e.preventDefault(),e.stopPropagation(),p=ae(e.clientX,e.clientY),n.selectCells(m.col,m.row,p.col,p.row))}function te(e){if(b||!K||!m||!p)return;e.preventDefault(),e.stopPropagation(),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),K=!1;const t=n.getCellsText(m.col,m.row,p.col,p.row);m=null,p=null,n.clearSelection(),k(!1),t&&(T.textContent="\u5DF2\u590D\u5236",V(t).then(J)),n.focus()}s.addEventListener("mousedown",Le,!0),document.addEventListener("keydown",Ce,!0);function Ce(e){if(!(b||!n.isFocused()||e.defaultPrevented||e.isComposing)){if(A){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),m=null,p=null,n.clearSelection(),k(!1);return}if(e.key==="Enter"||(e.ctrlKey||e.metaKey)&&e.code==="KeyC"){e.preventDefault(),e.stopPropagation(),V(n.getScreenText()).then(J),k(!1);return}}if(e.altKey&&e.code==="KeyC"&&!e.ctrlKey&&!e.metaKey){if(e.preventDefault(),e.stopPropagation(),m&&p){const t=n.getCellsText(m.col,m.row,p.col,p.row);m=null,p=null,n.clearSelection(),k(!1),t&&(T.textContent="Copied",V(t).then(J))}else{const t=n.getSelection();t?(n.clearSelection(),T.textContent="Copied",V(t).then(J)):le()}return}if((e.ctrlKey||e.metaKey)&&e.code==="KeyV"){e.preventDefault(),e.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const t=await navigator.clipboard.read();for(const r of t)for(const c of r.types)if(ie(c)){const u=await r.getType(c);await Z(u);return}}}catch{}try{const t=await navigator.clipboard?.readText();t&&n.pasteText(ce(t))}catch{}})();return}}}let W=0;function ce(e){return e.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function Me(e){const t=be(e.clipboardData);if(t){e.preventDefault(),e.stopPropagation();const c=Date.now();if(c-W<50)return;W=c,await Z(t);return}const r=e.clipboardData?.getData("text/plain");if(!r)return;e.preventDefault(),e.stopPropagation();const u=Date.now();u-W<50||(W=u,n.pasteText(ce(r)))}let z=0;function Se(e){e.preventDefault(),z++,se(e.dataTransfer)&&s.classList.add("terminal-drag-over")}function De(e){e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy")}function Pe(e){e.preventDefault(),z--,z<=0&&(z=0,s.classList.remove("terminal-drag-over"))}async function Ie(e){e.preventDefault(),z=0,s.classList.remove("terminal-drag-over");const t=se(e.dataTransfer);t&&await Z(t)}function ue(e){!we||e.key!=="Escape"||F||e.defaultPrevented||e.repeat||e.metaKey||e.ctrlKey||e.altKey||n.isFocused()&&(e.preventDefault(),e.stopPropagation(),j("\x1B"))}s.addEventListener("paste",Me),s.addEventListener("dragenter",Se),s.addEventListener("dragover",De),s.addEventListener("dragleave",Pe),s.addEventListener("drop",Ie),document.addEventListener("keydown",ue,!0);function ke(e){if(e.touches.length!==1){l=null;return}const t=e.touches[0];t&&(D(),l={startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,lastT:performance.now(),velocity:0,scrolling:!1},e.stopPropagation())}function Ye(e){if(!l||e.touches.length!==1)return;const t=e.touches[0];if(!t)return;const r=t.clientY-l.startY,u=t.clientX-l.startX;if(!l.scrolling){if(Math.abs(r)<8||Math.abs(r)<Math.abs(u))return;l.scrolling=!0,P=Date.now()+500}e.preventDefault(),e.stopPropagation();const c=performance.now(),x=t.clientY-l.lastY,v=c-l.lastT;v>0&&(l.velocity=x/v),Be(-x),l.lastX=t.clientX,l.lastY=t.clientY,l.lastT=c}function Fe(e){if(!l)return;const t=l.scrolling,r=l.velocity;if(l=null,e.stopPropagation(),!t){n.focus();return}P=Date.now()+500,e.preventDefault(),r&&Q(r)}function Ae(e){l=null,D(),e.stopPropagation()}function Ke(e){e.pointerType==="touch"&&Date.now()<P&&(e.preventDefault(),e.stopPropagation())}function Be(e){const t=s.querySelector(".xterm-rows > div"),r=t?t.getBoundingClientRect().height:16;let u=r>0?Math.round(e/r):e>0?1:-1;u===0&&(u=e>0?1:-1),n.scrollLines(u)}s.addEventListener("touchstart",ke,{passive:!0,capture:!0}),s.addEventListener("touchmove",Ye,{passive:!1,capture:!0}),s.addEventListener("touchend",Fe,{passive:!1,capture:!0}),s.addEventListener("touchcancel",Ae,{passive:!0,capture:!0}),s.addEventListener("pointerup",Ke,!0);function fe(){y()}window.addEventListener("resize",fe),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const de=new ResizeObserver(()=>y());de.observe(s),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),s.addEventListener("focusin",xe,!0);async function ze(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(e=>setTimeout(e,1e3))])}catch{}}return requestAnimationFrame(()=>{M()&&H(),(async()=>(await ze(),M()&&C({type:"resize",cols:n.cols,rows:n.rows}),re(),y()))()}),{destroy(){if(b=!0,D(),k(!1),B&&clearTimeout(B),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),a){try{a.close(1e3,"session switch")}catch{}a=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",fe),de.disconnect(),document.removeEventListener("keydown",ue,!0)},focus(){n.focus()},sendInput(e){j(e)},toggleCopyMode(){le()}}})()}export{Xe as initTerminal,ne as stripAltScreenSequences};
8
+ `).join("\r")}async function Se(e){const t=be(e.clipboardData);if(t){e.preventDefault(),e.stopPropagation();const u=Date.now();if(u-W<50)return;W=u,await Z(t);return}const r=e.clipboardData?.getData("text/plain");if(!r)return;e.preventDefault(),e.stopPropagation();const c=Date.now();c-W<50||(W=c,n.pasteText(ce(r)))}let O=0;function Me(e){e.preventDefault(),O++,se(e.dataTransfer)&&s.classList.add("terminal-drag-over")}function De(e){e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy")}function Pe(e){e.preventDefault(),O--,O<=0&&(O=0,s.classList.remove("terminal-drag-over"))}async function Ie(e){e.preventDefault(),O=0,s.classList.remove("terminal-drag-over");const t=se(e.dataTransfer);t&&await Z(t)}function ue(e){!we||e.key!=="Escape"||A||e.defaultPrevented||e.repeat||e.metaKey||e.ctrlKey||e.altKey||n.isFocused()&&(e.preventDefault(),e.stopPropagation(),j("\x1B"))}s.addEventListener("paste",Se),s.addEventListener("dragenter",Me),s.addEventListener("dragover",De),s.addEventListener("dragleave",Pe),s.addEventListener("drop",Ie),document.addEventListener("keydown",ue,!0);function ke(e){if(e.touches.length!==1){l=null;return}const t=e.touches[0];t&&(D(),l={startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,lastT:performance.now(),velocity:0,scrolling:!1},e.stopPropagation())}function Ye(e){if(!l||e.touches.length!==1)return;const t=e.touches[0];if(!t)return;const r=t.clientY-l.startY,c=t.clientX-l.startX;if(!l.scrolling){if(Math.abs(r)<8||Math.abs(r)<Math.abs(c))return;l.scrolling=!0,P=Date.now()+500}e.preventDefault(),e.stopPropagation();const u=performance.now(),x=t.clientY-l.lastY,v=u-l.lastT;v>0&&(l.velocity=x/v),Be(-x),l.lastX=t.clientX,l.lastY=t.clientY,l.lastT=u}function Fe(e){if(!l)return;const t=l.scrolling,r=l.velocity;if(l=null,e.stopPropagation(),!t){n.focus();return}P=Date.now()+500,e.preventDefault(),r&&Q(r)}function Ae(e){l=null,D(),e.stopPropagation()}function Ke(e){e.pointerType==="touch"&&Date.now()<P&&(e.preventDefault(),e.stopPropagation())}function Be(e){const t=s.querySelector(".xterm-rows > div"),r=t?t.getBoundingClientRect().height:16;let c=r>0?Math.round(e/r):e>0?1:-1;c===0&&(c=e>0?1:-1),n.scrollLines(c)}s.addEventListener("touchstart",ke,{passive:!0,capture:!0}),s.addEventListener("touchmove",Ye,{passive:!1,capture:!0}),s.addEventListener("touchend",Fe,{passive:!1,capture:!0}),s.addEventListener("touchcancel",Ae,{passive:!0,capture:!0}),s.addEventListener("pointerup",Ke,!0);function fe(){y()}window.addEventListener("resize",fe),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const de=new ResizeObserver(()=>y());de.observe(s),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),s.addEventListener("focusin",xe,!0);async function ze(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(e=>setTimeout(e,1e3))])}catch{}}return requestAnimationFrame(()=>{S()&&H(),(async()=>(await ze(),S()&&C({type:"resize",cols:n.cols,rows:n.rows}),re(),y()))()}),{destroy(){if(b=!0,D(),k(!1),z&&clearTimeout(z),document.removeEventListener("mousemove",ee,!0),document.removeEventListener("mouseup",te,!0),a){try{a.close(1e3,"session switch")}catch{}a=void 0}s.innerHTML="",s.classList.remove("terminal-pending"),window.removeEventListener("resize",fe),de.disconnect(),document.removeEventListener("keydown",ue,!0)},focus(){n.focus()},sendInput(e){j(e)},toggleCopyMode(){le()}}})()}export{Xe as initTerminal,ne as stripAltScreenSequences};
@@ -53,7 +53,9 @@ import{cssVarsStyle as d}from"../theme.js";import{newSessionModalCSS as c,newSes
53
53
  height: 100dvh;
54
54
  overflow: hidden;
55
55
  padding-top: calc(48px + env(safe-area-inset-top, 0px));
56
- transition: height 0.05s;
56
+ /* No height transition: on iOS WebKit a 50ms animated height makes
57
+ ResizeObserver fire with intermediate sizes while load_history
58
+ rewrites the buffer, corrupting line wrapping (see issue #4). */
57
59
  }
58
60
 
59
61
  /* \u2500\u2500 Sidebar \u2500\u2500 */
@@ -1,4 +1,4 @@
1
- import{escapeHtml as c}from"./html.js";import{icon as i,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 o(e="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${e}; outline: none;`}function v(e=""){return`
1
+ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as r}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 n(e="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${e}; outline: none;`}function v(e=""){return`
2
2
  @media (prefers-reduced-motion: reduce) {
3
3
  *, *::before, *::after {
4
4
  animation-duration: 0.01ms !important;
@@ -7,15 +7,15 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
7
7
  scroll-behavior: auto !important;
8
8
  }
9
9
  ${e}
10
- }`}function w(e="vscode"){const r=d.find(t=>t.id===e)??d[0],a=d.map(t=>{const s=t.id===e;return`<button type="button" class="theme-option${s?" active":""}" data-theme="${t.id}" role="menuitem">
10
+ }`}function w(e="vscode"){const o=d.find(t=>t.id===e)??d[0],a=d.map(t=>{const s=t.id===e;return`<button type="button" class="theme-option${s?" active":""}" data-theme="${t.id}" role="menuitem">
11
11
  <span class="theme-dot" style="background:${t.dot}"></span>
12
12
  ${c(t.name)}
13
13
  ${s?i("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:${r.dot}"></span>
18
- <span>${c(r.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
  ${a}
@@ -47,7 +47,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
47
47
  document.addEventListener('click', (e) => {
48
48
  if (switcher.classList.contains('open') && !switcher.contains(e.target)) close();
49
49
  });
50
- })();`}function $(e=""){return`
50
+ })();`}function S(e=""){return`
51
51
  *, *::before, *::after { box-sizing: border-box; }
52
52
  html, body { background: var(--page-bg); color: var(--page-fg); min-height: 100%; font-family: var(--font-sans); margin: 0; padding: 0; }
53
53
 
@@ -75,7 +75,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
75
75
  font-size: var(--text-sm); text-decoration: none;
76
76
  }
77
77
  .header-btn:hover, .icon-btn:hover { color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
78
- .header-btn:focus-visible, .icon-btn:focus-visible { ${o()} }
78
+ .header-btn:focus-visible, .icon-btn:focus-visible { ${n()} }
79
79
  .header-btn svg, .icon-btn svg { width: 18px; height: 18px; fill: currentColor; flex-shrink: 0; }
80
80
 
81
81
  /* \u2500\u2500 Theme switcher popover \u2500\u2500 */
@@ -88,7 +88,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
88
88
  font-size: var(--text-sm); text-decoration: none; font-family: inherit;
89
89
  }
90
90
  .theme-switcher-btn:hover { color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
91
- .theme-switcher-btn:focus-visible { ${o()} }
91
+ .theme-switcher-btn:focus-visible { ${n()} }
92
92
  .theme-switcher-btn svg { width: 18px; height: 18px; fill: currentColor; flex-shrink: 0; }
93
93
  .theme-switcher-popover {
94
94
  position: absolute; top: calc(100% + 6px); right: 0;
@@ -104,7 +104,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
104
104
  color: var(--page-fg); font-size: var(--text-sm); cursor: pointer; text-align: left; font-family: inherit;
105
105
  }
106
106
  .theme-option:hover { background: color-mix(in srgb, var(--panel-accent) 8%, transparent); color: var(--panel-accent); }
107
- .theme-option:focus-visible { ${o()} }
107
+ .theme-option:focus-visible { ${n()} }
108
108
  .theme-option.active { color: var(--panel-accent); font-weight: 500; }
109
109
  .theme-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
110
110
  .theme-check { width: 16px; height: 16px; margin-left: auto; color: var(--panel-accent); }
@@ -138,7 +138,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
138
138
  text-align: left;
139
139
  }
140
140
  .sidebar-btn:hover { background: color-mix(in srgb, var(--panel-accent) 8%, transparent); color: var(--panel-accent); }
141
- .sidebar-btn:focus-visible { ${o()} }
141
+ .sidebar-btn:focus-visible { ${n()} }
142
142
  .sidebar-btn.primary {
143
143
  background: var(--panel-accent); border-color: var(--panel-accent); color: var(--panel-accent-on);
144
144
  font-weight: 500; margin-bottom: 16px; justify-content: center;
@@ -197,7 +197,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
197
197
  color: var(--page-fg); transition: opacity 0.15s;
198
198
  }
199
199
  .modal-btn:hover { opacity: 0.85; }
200
- .modal-btn:focus-visible { ${o()} }
200
+ .modal-btn:focus-visible { ${n()} }
201
201
  .modal-btn.confirm {
202
202
  background: var(--panel-accent); border-color: var(--panel-accent);
203
203
  color: var(--panel-accent-on); font-weight: 500;
@@ -217,7 +217,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
217
217
  }
218
218
 
219
219
  ${v()}
220
- ${e}`}function S(e){const{title:r="TMUX Sessions",themeTemplate:a="vscode"}=e;return`<script>
220
+ ${e}`}function R(e){const{title:o="TMUX Sessions",themeTemplate:a="vscode"}=e;return`<script>
221
221
  (function() {
222
222
  const token = localStorage.getItem('tmux-web-token');
223
223
  if (token) {
@@ -247,10 +247,10 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
247
247
  </a>
248
248
  </div>
249
249
  </header>
250
- <script>${y()}</script>`}function z(e){const{activePage:r,refreshHref:a}=e;function t(l,h,x,f,g=""){return`<a href="${h}" class="${l!==null&&l===r?"sidebar-btn current":"sidebar-btn"}"${g}>
251
- <svg viewBox="0 0 24 24" fill="currentColor">${x}</svg>
252
- ${f}
253
- </a>`}const s=n("sessions"),p=n("notes"),m=n("history"),b=n("quick-commands"),u=n("file"),k=n("refresh");return`<aside class="action-sidebar">
250
+ <script>${y()}</script>`}function A(e){const{activePage:o,refreshHref:a}=e;function t(l,b,f,x,g=""){return`<a href="${b}" class="${l!==null&&l===o?"sidebar-btn current":"sidebar-btn"}"${g}>
251
+ <svg viewBox="0 0 24 24" fill="currentColor">${f}</svg>
252
+ ${x}
253
+ </a>`}const s=r("sessions"),p=r("notes"),h=r("history"),m=r("quick-commands"),u=r("file"),k=r("refresh");return`<aside class="action-sidebar">
254
254
  <p class="sidebar-label">Actions</p>
255
255
  <button class="sidebar-btn primary" id="new-session-btn">
256
256
  ${i("add")}
@@ -260,14 +260,14 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
260
260
  ${t("home","/",s,"Sessions")}
261
261
  ${t("notes","/notes",p,"All Notes")}
262
262
  ${t("files","/files",u,"Files")}
263
- ${t("history","/history",m,"History")}
264
- ${t("quickCommands","/quick-commands",b,"Quick Commands")}
263
+ ${t("history","/history",h,"History")}
264
+ ${t("quickCommands","/quick-commands",m,"Quick Commands")}
265
265
  <hr class="sidebar-divider">
266
266
  <a href="${a}" class="sidebar-btn">
267
267
  ${i("refresh")}
268
268
  Refresh
269
269
  </a>
270
- </aside>`}function B(){return` /* \u2500\u2500 New session modal \u2500\u2500 */
270
+ </aside>`}function T(){return` /* \u2500\u2500 New session modal \u2500\u2500 */
271
271
  .modal-backdrop {
272
272
  display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.3);
273
273
  z-index: 500; align-items: center; justify-content: center;
@@ -303,6 +303,43 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
303
303
  .modal-dropdown-item:hover, .modal-dropdown-item.active, .modal-dropdown-item:focus-visible {
304
304
  background: color-mix(in srgb, var(--panel-accent) 8%, transparent); color: var(--panel-accent);
305
305
  }
306
+ .ns-dir-wrap { position: relative; }
307
+ .ns-dir-wrap input { padding-right: 46px; }
308
+ .ns-dir-browse {
309
+ position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
310
+ width: 34px; height: 34px; border: none; border-radius: 9px;
311
+ background: transparent; color: var(--panel-muted); font-size: 14px; cursor: pointer;
312
+ display: flex; align-items: center; justify-content: center;
313
+ }
314
+ .ns-dir-browse:hover {
315
+ background: color-mix(in srgb, var(--panel-accent) 10%, transparent); color: var(--panel-accent);
316
+ }
317
+ .ns-dir-tree {
318
+ display: none; position: absolute; left: 0; right: 0; top: 100%;
319
+ margin-top: 4px; max-height: 260px; overflow-y: auto; z-index: 10;
320
+ background: var(--panel-bg); border: 1px solid var(--panel-border);
321
+ border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.12);
322
+ padding: 6px 0;
323
+ }
324
+ .ns-dir-tree.open { display: block; }
325
+ .ns-tree-row {
326
+ display: flex; align-items: center; min-height: 36px;
327
+ padding: 4px 10px 4px 0; font-size: var(--text-sm); color: var(--page-fg);
328
+ cursor: pointer; white-space: nowrap; user-select: none;
329
+ }
330
+ .ns-tree-row:hover, .ns-tree-row.active { background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
331
+ .ns-tree-row.current > .ns-tree-label { color: var(--panel-accent); font-weight: 600; }
332
+ .ns-tree-arrow {
333
+ width: 20px; height: 20px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
334
+ color: var(--panel-muted); font-size: 10px; border-radius: 5px;
335
+ }
336
+ .ns-tree-arrow:hover {
337
+ background: color-mix(in srgb, var(--panel-accent) 14%, transparent); color: var(--panel-accent);
338
+ }
339
+ .ns-tree-arrow.leaf { visibility: hidden; }
340
+ .ns-tree-icon { flex-shrink: 0; margin: 0 6px 0 2px; font-size: 13px; }
341
+ .ns-tree-label { overflow: hidden; text-overflow: ellipsis; }
342
+ .ns-tree-empty { color: var(--panel-muted); font-size: var(--text-sm); padding: 8px 12px; }
306
343
  .modal-dropdown-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--panel-accent); }
307
344
  .modal-error { font-size: var(--text-sm); color: #b91c1c; margin-bottom: 12px; display: none; }
308
345
  .modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 8px; }
@@ -312,13 +349,13 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
312
349
  color: var(--page-fg); transition: opacity 0.15s;
313
350
  }
314
351
  .modal-btn:hover { opacity: 0.85; }
315
- .modal-btn:focus-visible { ${o()} }
352
+ .modal-btn:focus-visible { ${n()} }
316
353
  .modal-btn.confirm {
317
354
  background: var(--panel-accent); border-color: var(--panel-accent);
318
355
  color: var(--panel-accent-on); font-weight: 500;
319
356
  }
320
357
  .modal-btn.confirm:hover { opacity: 0.9; }
321
- `}function A(){return`<div class="modal-backdrop" id="new-session-modal" role="dialog" aria-modal="true" aria-label="Create new tmux session">
358
+ `}function B(){return`<div class="modal-backdrop" id="new-session-modal" role="dialog" aria-modal="true" aria-label="Create new tmux session">
322
359
  <div class="modal-panel">
323
360
  <h2>New Session</h2>
324
361
  <div class="modal-field">
@@ -327,8 +364,12 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
327
364
  </div>
328
365
  <div class="modal-field">
329
366
  <label for="ns-dir">Start directory</label>
330
- <input type="text" id="ns-dir" placeholder="~" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="ns-dir-list" />
367
+ <div class="ns-dir-wrap">
368
+ <input type="text" id="ns-dir" placeholder="~" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="ns-dir-list" />
369
+ <button type="button" class="ns-dir-browse" id="ns-dir-browse" title="Browse directories" aria-label="Browse directories">&#128193;</button>
370
+ </div>
331
371
  <div class="modal-dropdown" id="ns-dir-list" role="listbox"></div>
372
+ <div class="ns-dir-tree" id="ns-dir-tree" role="tree" aria-label="Directory tree"></div>
332
373
  </div>
333
374
  <p class="modal-error" id="ns-error"></p>
334
375
  <div class="modal-actions">
@@ -336,7 +377,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
336
377
  <button class="modal-btn confirm" id="ns-submit">Create</button>
337
378
  </div>
338
379
  </div>
339
- </div>`}function T(e){return`(function() {
380
+ </div>`}function z(e){return`(function() {
340
381
  const modal = document.getElementById('new-session-modal');
341
382
  const openBtn = document.getElementById('new-session-btn');
342
383
  const cancelBtn = document.getElementById('ns-cancel');
@@ -368,10 +409,10 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
368
409
  errorEl.style.display = 'none';
369
410
  errorEl.textContent = '';
370
411
  closeDropdown();
371
- setTimeout(() => nameInput.focus(), 50);
412
+ openTreeForSession().then(() => setTimeout(() => nameInput.focus(), 50));
372
413
  }
373
414
 
374
- function closeModal() { modal.classList.remove('open'); }
415
+ function closeModal() { modal.classList.remove('open'); closeDropdown(); closeTree(); }
375
416
 
376
417
  if (openBtn) openBtn.addEventListener('click', openModal);
377
418
  cancelBtn.addEventListener('click', closeModal);
@@ -420,6 +461,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
420
461
 
421
462
  dirInput.addEventListener('input', () => {
422
463
  clearTimeout(debounceTimer);
464
+ closeTree();
423
465
  const val = dirInput.value.trim();
424
466
  if (!val) { closeDropdown(); return; }
425
467
  debounceTimer = setTimeout(async () => {
@@ -433,6 +475,181 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
433
475
 
434
476
  dirInput.addEventListener('blur', () => { setTimeout(closeDropdown, 120); });
435
477
 
478
+ // \u2500\u2500 Directory tree (expandable, lazy-loads each level) \u2500\u2500
479
+ const treePanel = document.getElementById('ns-dir-tree');
480
+ const browseBtn = document.getElementById('ns-dir-browse');
481
+ let treeCache = {}; // path -> dirs[] (full paths)
482
+ let treeRoot = '/';
483
+ let treeExpanded = new Set(); // paths currently expanded
484
+ let treeSelected = ''; // path currently selected (highlighted)
485
+ let treeRows = []; // visible rows [{path, el}] for keyboard nav
486
+ let treeActiveIdx = -1;
487
+ let treeOpen = false;
488
+
489
+ function treeEsc(s) { return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
490
+ function treeBase(p) {
491
+ if (p === '/' || p === '~') return p;
492
+ const parts = p.split('/').filter(Boolean);
493
+ return parts[parts.length - 1] || '/';
494
+ }
495
+ async function treeLoadDirs(path) {
496
+ if (treeCache[path]) return treeCache[path];
497
+ try {
498
+ const res = await fetch(selectedBase() + '/api/fs/list?path=' + encodeURIComponent(path));
499
+ const data = await res.json();
500
+ const dirs = (data.dirs || []).filter(d => d !== path).sort((a, b) => a.localeCompare(b));
501
+ treeCache[path] = dirs;
502
+ return dirs;
503
+ } catch { treeCache[path] = []; return []; }
504
+ }
505
+
506
+ function treeRowEl(path, depth) {
507
+ const row = document.createElement('div');
508
+ row.className = 'ns-tree-row' + (treeSelected === path ? ' current' : '');
509
+ row.dataset.path = path;
510
+ row.style.paddingLeft = (12 + depth * 18) + 'px';
511
+ const arrow = document.createElement('span');
512
+ arrow.className = 'ns-tree-arrow' + (treeExpanded.has(path) ? '' : ' leaf');
513
+ arrow.textContent = treeExpanded.has(path) ? '\u25BE' : '\u25B8';
514
+ const icon = document.createElement('span');
515
+ icon.className = 'ns-tree-icon';
516
+ icon.textContent = '\u{1F4C1}';
517
+ const label = document.createElement('span');
518
+ label.className = 'ns-tree-label';
519
+ label.textContent = treeBase(path);
520
+ label.title = path;
521
+ row.append(arrow, icon, label);
522
+ // Arrow toggles expansion (lazy-loads that level).
523
+ arrow.addEventListener('click', (e) => {
524
+ e.stopPropagation();
525
+ void treeToggle(path, depth);
526
+ });
527
+ // Clicking the row selects the directory.
528
+ row.addEventListener('click', () => {
529
+ dirInput.value = path;
530
+ closeTree();
531
+ dirInput.focus();
532
+ });
533
+ return row;
534
+ }
535
+
536
+ async function treeToggle(path, depth) {
537
+ if (treeExpanded.has(path)) treeExpanded.delete(path);
538
+ else treeExpanded.add(path);
539
+ await treeRender();
540
+ if (treeExpanded.has(path)) {
541
+ // After expanding, reveal any newly visible selected row.
542
+ const el = document.querySelector('.ns-tree-row.current');
543
+ if (el) el.scrollIntoView({ block: 'nearest' });
544
+ }
545
+ }
546
+
547
+ async function treeRender() {
548
+ treePanel.innerHTML = '';
549
+ treeRows = [];
550
+ const rootRow = treeRowEl(treeRoot, 0);
551
+ treePanel.appendChild(rootRow);
552
+ treeRows.push({ path: treeRoot, el: rootRow });
553
+ if (treeExpanded.has(treeRoot)) {
554
+ await treeRenderChildren(treeRoot, 0, rootRow);
555
+ }
556
+ treeRefreshActive();
557
+ if (!treeRows.length) treePanel.innerHTML = '<div class="ns-tree-empty">No directories</div>';
558
+ }
559
+
560
+ async function treeRenderChildren(path, depth, parentRow) {
561
+ if (!treeExpanded.has(path)) return;
562
+ const dirs = await treeLoadDirs(path);
563
+ const frag = document.createDocumentFragment();
564
+ for (const d of dirs) {
565
+ const row = treeRowEl(d, depth + 1);
566
+ frag.appendChild(row);
567
+ treeRows.push({ path: d, el: row });
568
+ }
569
+ parentRow.after(frag);
570
+ for (const d of dirs) {
571
+ if (treeExpanded.has(d)) {
572
+ const row = treePanel.querySelector('.ns-tree-row[data-path="' + CSS.escape(d) + '"]');
573
+ if (row) await treeRenderChildren(d, depth + 1, row);
574
+ }
575
+ }
576
+ }
577
+
578
+ // Expand the tree down to the given path (each level lazy-loads and expands).
579
+ async function treeExpandTo(path) {
580
+ treeExpanded.clear();
581
+ treeExpanded.add(treeRoot);
582
+ let cur = treeRoot;
583
+ let acc = '';
584
+ const segs = String(path).split('/').filter(Boolean);
585
+ for (const seg of segs) {
586
+ acc = acc ? acc + '/' + seg : '/' + seg;
587
+ if (cur === '~') cur = acc;
588
+ else if (cur === '/') cur = '/' + seg;
589
+ else cur = cur + '/' + seg;
590
+ await treeLoadDirs(cur);
591
+ treeExpanded.add(cur);
592
+ }
593
+ treeSelected = cur;
594
+ await treeRender();
595
+ const el = treePanel.querySelector('.ns-tree-row.current');
596
+ if (el) el.scrollIntoView({ block: 'nearest' });
597
+ }
598
+
599
+ function treeRefreshActive() {
600
+ treeActiveIdx = Math.max(-1, Math.min(treeActiveIdx, treeRows.length - 1));
601
+ treeRows.forEach((r, i) => r.el.classList.toggle('active', i === treeActiveIdx));
602
+ if (treeActiveIdx >= 0 && treeRows[treeActiveIdx]) treeRows[treeActiveIdx].el.scrollIntoView({ block: 'nearest' });
603
+ }
604
+
605
+ function closeTree() {
606
+ treePanel.classList.remove('open');
607
+ treePanel.innerHTML = '';
608
+ treeOpen = false;
609
+ treeExpanded = new Set();
610
+ treeSelected = '';
611
+ treeRows = [];
612
+ treeActiveIdx = -1;
613
+ }
614
+
615
+ async function openTree() {
616
+ closeDropdown();
617
+ const v = dirInput.value.trim();
618
+ const target = (!v || v === '~') ? '/' : (v.startsWith('/') ? v : '/' + v);
619
+ treeRoot = '/';
620
+ treePanel.classList.add('open');
621
+ treeOpen = true;
622
+ await treeExpandTo(target);
623
+ }
624
+
625
+ // On modal open, expand the tree directly to the currently selected
626
+ // session's working directory (window.__tmuxWebCurrentSession is set by
627
+ // the shell client; falls back to / otherwise).
628
+ async function openTreeForSession() {
629
+ const session = (typeof window.__tmuxWebCurrentSession === 'string' && window.__tmuxWebCurrentSession)
630
+ ? window.__tmuxWebCurrentSession : '';
631
+ let target = '/';
632
+ if (session) {
633
+ try {
634
+ const res = await fetch(selectedBase() + '/api/fs/session-path?session=' + encodeURIComponent(session));
635
+ const data = await res.json();
636
+ if (data && typeof data.path === 'string' && data.path) {
637
+ dirInput.value = data.path;
638
+ target = data.path.startsWith('/') ? data.path : '/' + data.path;
639
+ }
640
+ } catch {}
641
+ }
642
+ treeRoot = '/';
643
+ treePanel.classList.add('open');
644
+ treeOpen = true;
645
+ await treeExpandTo(target);
646
+ }
647
+
648
+ if (browseBtn) browseBtn.addEventListener('click', () => {
649
+ if (treeOpen) { closeTree(); return; }
650
+ void openTree();
651
+ });
652
+
436
653
  async function submit() {
437
654
  const name = nameInput.value.trim();
438
655
  const dir = dirInput.value.trim();
@@ -462,6 +679,29 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
462
679
  submitBtn.addEventListener('click', submit);
463
680
  nameInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') dirInput.focus(); });
464
681
  dirInput.addEventListener('keydown', (e) => {
682
+ if (treeOpen && treeRows.length) {
683
+ if (e.key === 'ArrowDown') { e.preventDefault(); treeActiveIdx = Math.min(treeActiveIdx + 1, treeRows.length - 1); treeRefreshActive(); return; }
684
+ if (e.key === 'ArrowUp') { e.preventDefault(); treeActiveIdx = Math.max(treeActiveIdx - 1, 0); treeRefreshActive(); return; }
685
+ if (e.key === 'ArrowRight') {
686
+ e.preventDefault();
687
+ const row = treeRows[Math.max(treeActiveIdx, 0)];
688
+ if (row && !treeExpanded.has(row.path)) void treeToggle(row.path, row.el.style.paddingLeft ? Math.round((parseInt(row.el.style.paddingLeft) - 12) / 18) : 0);
689
+ return;
690
+ }
691
+ if (e.key === 'ArrowLeft') {
692
+ e.preventDefault();
693
+ const row = treeRows[Math.max(treeActiveIdx, 0)];
694
+ if (row && treeExpanded.has(row.path)) void treeToggle(row.path, 0);
695
+ return;
696
+ }
697
+ if (e.key === 'Enter' && treeActiveIdx >= 0) {
698
+ e.preventDefault();
699
+ dirInput.value = treeRows[treeActiveIdx].path;
700
+ closeTree();
701
+ return;
702
+ }
703
+ if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); closeTree(); return; }
704
+ }
465
705
  const open = dirList.classList.contains('open');
466
706
  const items = dirList.querySelectorAll('.modal-dropdown-item');
467
707
  if (open && items.length) {
@@ -477,4 +717,5 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as n}from"./ico
477
717
  }
478
718
  if (e.key === 'Enter') submit();
479
719
  });
480
- })();`}export{B as newSessionModalCSS,A as newSessionModalHTML,T as newSessionModalScript,v as reducedMotion,S as sharedHeader,$ as sharedLayoutCSS,z as sharedSidebar,w as themeSwitcherButtonHTML,y as themeSwitcherScript};
720
+ window.__openNewSessionModal = openModal;
721
+ })();`}export{T as newSessionModalCSS,B as newSessionModalHTML,z as newSessionModalScript,v as reducedMotion,R as sharedHeader,S as sharedLayoutCSS,A as sharedSidebar,w as themeSwitcherButtonHTML,y as themeSwitcherScript};
@@ -1 +1 @@
1
- function e(){const n={...process.env};return!n.LC_ALL&&!n.LANG&&(n.LC_ALL=process.platform==="darwin"?"en_US.UTF-8":"C.UTF-8"),n}export{e as tmuxEnv};
1
+ function n(){const e={...process.env};return delete e.TMUX,delete e.TMUX_PANE,!e.LC_ALL&&!e.LANG&&(e.LC_ALL=process.platform==="darwin"?"en_US.UTF-8":"C.UTF-8"),e}export{n as tmuxEnv};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "2.0.26",
3
+ "version": "2.0.28",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {