tmux-weblink 2.2.23 → 2.2.25

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,26 +1,26 @@
1
- var hn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Je(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function fe(e){return e.replace(hn,"")}var Ae;function vn(){return Ae||(document.querySelector("link[data-tmux-web-xterm-css]")?Ae=Promise.resolve():(Ae=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)}),Ae))}var gn=class Ut{terminal;fitAddon;container;constructor(t,s,n){this.container=t,this.terminal=s,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,s,n){let[{Terminal:l},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),vn()]),c=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:s,convertEol:!1,smoothScrollDuration:120,theme:n});return new Ut(t,c,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=yn(this.container);if(t.width<=0||t.height<=0)return!1;let s=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!s?.width||!s?.height)return this.fitAddon.fit(),!0;let n=s.width,l=s.height,r=this.terminal.element?.querySelector(".xterm-viewport"),c=r?Math.max(0,r.offsetWidth-r.clientWidth):0,a=Math.max(2,Math.floor((t.width-c-n/2)/n)),u=Math.max(2,Math.floor(t.height/l));return(a!==this.terminal.cols||u!==this.terminal.rows)&&this.terminal.resize(a,u),!0}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,s){let n=this.terminal.buffer.active,[l,r]=t<=s?[t,s]:[s,t],c=n.viewportY,a=[];for(let u=l;u<=r;u++){let f=n.getLine(c+u);a.push(f?f.translateToString(!0):"")}return a.join(`
2
- `)}selectRows(t,s){let n=this.terminal.buffer.active,[l,r]=t<=s?[t,s]:[s,t];this.terminal.selectLines(n.viewportY+l,n.viewportY+r)}selectCells(t,s,n,l){let r=this.terminal.buffer.active,c=Math.min(s,l),a=Math.max(s,l);if(c===a){let u=Math.min(t,n),f=Math.max(t,n);this.terminal.select(u,r.viewportY+c,f-u+1)}else this.terminal.selectLines(r.viewportY+c,r.viewportY+a)}getCellsText(t,s,n,l){let r=this.terminal.buffer.active,c=Math.min(s,l),a=Math.max(s,l),u=s<l?t:s>l?n:Math.min(t,n),f=s<l?n:s>l?t:Math.max(t,n),S=r.viewportY,L=[];for(let F=c;F<=a;F++){let M=r.getLine(S+F);if(!M){L.push("");continue}let b=F===c?u:0,q=F===a?f:this.terminal.cols-1,ie="";for(let Ce=b;Ce<=q;Ce++){let O=M.getCell(Ce);O&&(ie+=O.getChars())}L.push(ie.replace(/\s+$/,""))}return L.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function yn(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 wn(e,t,s){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let n=await gn.create(e,s.scrollback,s.theme),l,r=0,c,a=null,u=0,f=0,S=.2,L=10,F=.95,M=.04,b=null,q=null;function ie(i){b||(b=document.createElement("div"),b.style.cssText=["position:absolute","left:0","width:40%","padding:10px 0","text-align:center","font:600 14px/1.4 system-ui, -apple-system, sans-serif","color:rgba(255,255,255,0.95)","background:rgba(20,30,50,0.55)","border:1px solid rgba(255,255,255,0.25)","border-radius:10px","pointer-events:none","opacity:0","transition:opacity 0.15s ease","z-index:6"].join(";"),e.appendChild(b)),b.textContent=i?"\u2191 \u4E0A\u7FFB\u4E00\u9875 (PgUp)":"\u2193 \u4E0B\u7FFB\u4E00\u9875 (PgDn)",b.style.top=i?"10%":"auto",b.style.bottom=i?"auto":"10%",b.style.opacity="1",q&&clearTimeout(q),q=setTimeout(()=>{b&&(b.style.opacity="0")},900)}function Ce(){q&&clearTimeout(q),b&&(b.style.opacity="0")}function O(){u&&(cancelAnimationFrame(u),u=0),f=0}function _t(i){if(O(),Math.abs(i)<S)return;f=Math.max(-L,Math.min(L,i));let o=()=>{if(j){u=0;return}let d=e.querySelector(".xterm-rows > div"),p=d?d.getBoundingClientRect().height:16,v=p>0?Math.round(f*16/p):f>0?1:-1;v===0&&(v=f>0?1:-1);let A=n.viewportY()-v,V=n.baseY(),pe=Math.max(0,Math.min(A,V));if(n.scrollToLine(pe,!0),f*=F,pe!==A){u=0;return}if(Math.abs(f)<M){u=0;return}u=requestAnimationFrame(o)};u=requestAnimationFrame(o)}let ke=0,R="connecting",Se=0,ae=!1,Te=[],Z="",j=!1,oe=1e3,Yt=location.protocol==="https:"?"wss:":"ws:",qt=s.wsBase||"/ws",Ot=Yt+"//"+location.host+qt+"/"+encodeURIComponent(t),Wt="/api/session/"+encodeURIComponent(t)+"/upload",Kt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Vt(){return Te.join("")+Z}async function Xt(i,o=0){let d=i?n.viewportY():0,p=fe(Vt());if(n.reset(),!p){i||n.scrollToBottom();return}await n.write(p),i?n.scrollToLine(o+d):n.scrollToBottom()}function W(i){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(i))}function Jt(i){let o;try{o=JSON.parse(i)}catch{R==="live"&&(Z+=i,n.write(fe(i)));return}if(o.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?W({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(o.type==="attach_failed"&&typeof o.message=="string"){Be=!0,R="dead",n.reset(),n.write(`\r
1
+ var hn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function Je(e){return e.replace(/\x1b\[\?1049[hl]/g,"")}function fe(e){return e.replace(hn,"")}var Ae;function vn(){return Ae||(document.querySelector("link[data-tmux-web-xterm-css]")?Ae=Promise.resolve():(Ae=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)}),Ae))}var gn=class _t{terminal;fitAddon;container;constructor(t,s,n){this.container=t,this.terminal=s,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,s,n){let[{Terminal:l},{FitAddon:r}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),vn()]),c=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:s,convertEol:!1,smoothScrollDuration:120,theme:n});return new _t(t,c,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=yn(this.container);if(t.width<=0||t.height<=0)return!1;let s=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!s?.width||!s?.height)return this.fitAddon.fit(),!0;let n=s.width,l=s.height,r=this.terminal.element?.querySelector(".xterm-viewport"),c=r?Math.max(0,r.offsetWidth-r.clientWidth):0,i=Math.max(2,Math.floor((t.width-c-n/2)/n)),u=Math.max(2,Math.floor(t.height/l));return(i!==this.terminal.cols||u!==this.terminal.rows)&&this.terminal.resize(i,u),!0}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return t?t===this.container||t===this.terminal.textarea||this.container.contains(t)||!!t.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(t,s){let n=this.terminal.buffer.active,[l,r]=t<=s?[t,s]:[s,t],c=n.viewportY,i=[];for(let u=l;u<=r;u++){let f=n.getLine(c+u);i.push(f?f.translateToString(!0):"")}return i.join(`
2
+ `)}selectRows(t,s){let n=this.terminal.buffer.active,[l,r]=t<=s?[t,s]:[s,t];this.terminal.selectLines(n.viewportY+l,n.viewportY+r)}selectCells(t,s,n,l){let r=this.terminal.buffer.active,c=Math.min(s,l),i=Math.max(s,l);if(c===i){let u=Math.min(t,n),f=Math.max(t,n);this.terminal.select(u,r.viewportY+c,f-u+1)}else this.terminal.selectLines(r.viewportY+c,r.viewportY+i)}getCellsText(t,s,n,l){let r=this.terminal.buffer.active,c=Math.min(s,l),i=Math.max(s,l),u=s<l?t:s>l?n:Math.min(t,n),f=s<l?n:s>l?t:Math.max(t,n),S=r.viewportY,L=[];for(let F=c;F<=i;F++){let M=r.getLine(S+F);if(!M){L.push("");continue}let b=F===c?u:0,q=F===i?f:this.terminal.cols-1,ae="";for(let Ce=b;Ce<=q;Ce++){let O=M.getCell(Ce);O&&(ae+=O.getChars())}L.push(ae.replace(/\s+$/,""))}return L.join(`
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function yn(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 wn(e,t,s){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let n=await gn.create(e,s.scrollback,s.theme),l,r=0,c,i=null,u=0,f=0,S=.2,L=10,F=.95,M=.04,b=null,q=null;function ae(a){b||(b=document.createElement("div"),b.style.cssText=["position:absolute","left:0","width:40%","padding:10px 0","text-align:center","font:600 14px/1.4 system-ui, -apple-system, sans-serif","color:rgba(255,255,255,0.95)","background:rgba(20,30,50,0.55)","border:1px solid rgba(255,255,255,0.25)","border-radius:10px","pointer-events:none","opacity:0","transition:opacity 0.15s ease","z-index:6"].join(";"),e.appendChild(b)),b.textContent=a?"\u2191 \u4E0A\u7FFB\u4E00\u9875 (PgUp)":"\u2193 \u4E0B\u7FFB\u4E00\u9875 (PgDn)",b.style.top=a?"10%":"auto",b.style.bottom=a?"auto":"10%",b.style.opacity="1",q&&clearTimeout(q),q=setTimeout(()=>{b&&(b.style.opacity="0")},900)}function Ce(){q&&clearTimeout(q),b&&(b.style.opacity="0")}function O(){u&&(cancelAnimationFrame(u),u=0),f=0}function Yt(a){if(O(),Math.abs(a)<S)return;f=Math.max(-L,Math.min(L,a));let o=()=>{if(j){u=0;return}let d=e.querySelector(".xterm-rows > div"),p=d?d.getBoundingClientRect().height:16,v=p>0?Math.round(f*16/p):f>0?1:-1;v===0&&(v=f>0?1:-1);let A=n.viewportY()-v,V=n.baseY(),pe=Math.max(0,Math.min(A,V));if(n.scrollToLine(pe,!0),f*=F,pe!==A){u=0;return}if(Math.abs(f)<M){u=0;return}u=requestAnimationFrame(o)};u=requestAnimationFrame(o)}let ke=0,R="connecting",Se=0,ie=!1,Te=[],Z="",j=!1,oe=1e3,qt=location.protocol==="https:"?"wss:":"ws:",lt=s.wsBase||"/ws",Ot=qt+"//"+location.host+lt+"/"+encodeURIComponent(t),Wt=(lt.replace(/^\/ws/,"")||"")+"/api/session/"+encodeURIComponent(t)+"/upload",Kt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Vt(){return Te.join("")+Z}async function Xt(a,o=0){let d=a?n.viewportY():0,p=fe(Vt());if(n.reset(),!p){a||n.scrollToBottom();return}await n.write(p),a?n.scrollToLine(o+d):n.scrollToBottom()}function W(a){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(a))}function Jt(a){let o;try{o=JSON.parse(a)}catch{R==="live"&&(Z+=a,n.write(fe(a)));return}if(o.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?W({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(o.type==="attach_failed"&&typeof o.message=="string"){Be=!0,R="dead",n.reset(),n.write(`\r
4
4
  \x1B[31m`+fe(o.message)+`\x1B[0m\r
5
- `).then(()=>{Me();try{l?.close()}catch{}});return}if(o.type==="auth.ok"){o.token&&localStorage.setItem("tmux-web-token",o.token),oe=1e3,K(),W({type:"resize",cols:n.cols,rows:n.rows}),T();return}if(o.type==="auth.failed"){if(o.permanentLock||o.retryAfterMs&&o.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(o.message);return}return}if(o.type==="snapshot"&&typeof o.data=="string"){let d=Je(fe(o.data));Te=[d],Z="",Se=typeof o.lines=="number"?o.lines:s.terminal.initialLines,R="live",n.reset(),K(),W({type:"resize",cols:n.cols,rows:n.rows}),n.write(d).then(()=>n.scrollToBottom());return}if(o.type==="window_changed"&&typeof o.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(o.activeIndex)&&(d.searchParams.set("window",String(o.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:o.windows}));return}if(o.type==="history"&&typeof o.data=="string"){o.lines>0&&o.data?(Te.unshift(Je(o.data)),Se+=o.lines,Xt(!0,o.lines).finally(()=>{ae=!1})):ae=!1;return}if(o.type==="data"&&typeof o.data=="string"){let d=Je(fe(o.data));if(R==="connecting"){R="live",Z=d,n.write(d);return}R==="live"&&(Z+=d,n.write(d))}}let Be=!1;function lt(){if(Be)return;let i=Ot,o=localStorage.getItem("tmux-web-token");o&&(i+=(i.includes("?")?"&":"?")+"token="+encodeURIComponent(o)),i+=(i.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,l=new WebSocket(i),l.onopen=()=>{R="connecting",Se=0,ae=!1,Te=[],Z="",n.reset(),oe=1e3},l.onmessage=d=>{typeof d.data=="string"&&Jt(d.data)},l.onclose=()=>{j||(R="connecting",!Be&&setTimeout(()=>{oe=Math.min(oe*2,1e4),!j&&!Be&&lt()},oe))},l.onerror=()=>l?.close()}function Q(i){W({type:"input",data:i})}function K(){return n.fit()}function Me(){e.classList.remove("terminal-pending")}function T(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),K(),r=requestAnimationFrame(()=>{r=0,K()&&Me()}),c=setTimeout(()=>{K()&&Me()},120)}function Gt(){T(),setTimeout(T,50),setTimeout(T,150),setTimeout(T,300)}function rt(i){return typeof i=="string"&&i.startsWith("image/")}function ct(i){return i?.files?.length?i.files[0]??null:null}function Zt(i){if(!i?.items)return null;for(let o=0;o<i.items.length;o++){let d=i.items[o];if(d?.kind==="file"&&rt(d.type))return d.getAsFile()}return null}async function Qt(i){let o=new FormData,d=i instanceof File&&i.name?i.name:"upload";o.append("file",i,d);let p=localStorage.getItem("tmux-web-token"),v={};p&&(v.Authorization="Bearer "+p);let A=await fetch(Wt,{method:"POST",body:o,headers:v});if(!A.ok){let pe="upload failed";try{let vt=await A.json();vt.error&&(pe=vt.error)}catch{}throw new Error(pe)}let V=await A.json();if(!V.path)throw new Error("no path in response");return V.path}async function Ke(i){try{let o=await Qt(i);Q(o)}catch{}}n.onData(i=>Q(i)),n.onResize(({cols:i,rows:o})=>W({type:"resize",cols:i,rows:o})),n.onScroll(()=>{R!=="live"||ae||!n.isNearScrollbackTop()||(ae=!0,W({type:"load_history",before:Se}))});let le=!1,re=!1,E=null,x=null,ce=document.createElement("div");ce.className="terminal-copy-banner",ce.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',ce.hidden=!0,e.appendChild(ce);let $=document.createElement("div");$.className="terminal-copy-banner terminal-copy-toast",$.textContent="Copied",$.hidden=!0,e.appendChild($);function dt(i,o){let d=e.querySelector(".xterm-screen, canvas"),p=(d&&d.getBoundingClientRect().width>0?d:e).getBoundingClientRect(),v=p.height>0&&n.rows>0?p.height/n.rows:18,A=p.width>0&&n.cols>0?p.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((o-p.top)/v))),col:Math.max(0,Math.min(n.cols-1,Math.floor((i-p.left)/A)))}}async function Ie(i){if(!i)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(i);return}}catch{}let o=document.createElement("textarea");o.value=i,o.style.position="fixed",o.style.opacity="0",o.style.pointerEvents="none",document.body.appendChild(o),o.focus(),o.select();try{document.execCommand("copy")}catch{}o.remove()}let de;function De(){$.hidden=!1,de&&clearTimeout(de),de=setTimeout(()=>{$.hidden=!0},1e3)}function ee(i){le=i,ce.hidden=!i,e.classList.toggle("terminal-copy-mode-active",i),i&&n.focus()}function ut(){ee(!le)}function en(i){j||i.button!==0||re||(le||i.shiftKey)&&(i.preventDefault(),i.stopPropagation(),re=!0,E=dt(i.clientX,i.clientY),x=E,n.selectCells(E.col,E.row,x.col,x.row),document.addEventListener("mousemove",Ve,!0),document.addEventListener("mouseup",Xe,!0))}function Ve(i){j||!re||!E||(i.preventDefault(),i.stopPropagation(),x=dt(i.clientX,i.clientY),n.selectCells(E.col,E.row,x.col,x.row))}function Xe(i){if(j||!re||!E||!x)return;i.preventDefault(),i.stopPropagation(),document.removeEventListener("mousemove",Ve,!0),document.removeEventListener("mouseup",Xe,!0),re=!1;let o=n.getCellsText(E.col,E.row,x.col,x.row);E=null,x=null,n.clearSelection(),ee(!1),o&&($.textContent="\u5DF2\u590D\u5236",Ie(o).then(De)),n.focus()}e.addEventListener("mousedown",en,!0),document.addEventListener("keydown",tn,!0);function tn(i){if(!(j||!n.isFocused()||i.defaultPrevented||i.isComposing)){if(le){if(i.key==="Escape"){i.preventDefault(),i.stopPropagation(),E=null,x=null,n.clearSelection(),ee(!1);return}if(i.key==="Enter"||(i.ctrlKey||i.metaKey)&&i.code==="KeyC"){i.preventDefault(),i.stopPropagation(),Ie(n.getScreenText()).then(De),ee(!1);return}}if(i.altKey&&i.code==="KeyC"&&!i.ctrlKey&&!i.metaKey){if(i.preventDefault(),i.stopPropagation(),E&&x){let o=n.getCellsText(E.col,E.row,x.col,x.row);E=null,x=null,n.clearSelection(),ee(!1),o&&($.textContent="Copied",Ie(o).then(De))}else{let o=n.getSelection();o?(n.clearSelection(),$.textContent="Copied",Ie(o).then(De)):ut()}return}if((i.ctrlKey||i.metaKey)&&i.code==="KeyV"){i.preventDefault(),i.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let o=await navigator.clipboard.read();for(let d of o)for(let p of d.types)if(rt(p)){let v=await d.getType(p);await Ke(v);return}}}catch{}try{let o=await navigator.clipboard?.readText();o&&n.pasteText(pt(o))}catch{}})();return}}}let Fe=0;function pt(i){return i.split(`\r
5
+ `).then(()=>{Me();try{l?.close()}catch{}});return}if(o.type==="auth.ok"){o.token&&localStorage.setItem("tmux-web-token",o.token),oe=1e3,K(),W({type:"resize",cols:n.cols,rows:n.rows}),T();return}if(o.type==="auth.failed"){if(o.permanentLock||o.retryAfterMs&&o.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(o.message);return}return}if(o.type==="snapshot"&&typeof o.data=="string"){let d=Je(fe(o.data));Te=[d],Z="",Se=typeof o.lines=="number"?o.lines:s.terminal.initialLines,R="live",n.reset(),K(),W({type:"resize",cols:n.cols,rows:n.rows}),n.write(d).then(()=>n.scrollToBottom());return}if(o.type==="window_changed"&&typeof o.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(o.activeIndex)&&(d.searchParams.set("window",String(o.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:o.windows}));return}if(o.type==="history"&&typeof o.data=="string"){o.lines>0&&o.data?(Te.unshift(Je(o.data)),Se+=o.lines,Xt(!0,o.lines).finally(()=>{ie=!1})):ie=!1;return}if(o.type==="data"&&typeof o.data=="string"){let d=Je(fe(o.data));if(R==="connecting"){R="live",Z=d,n.write(d);return}R==="live"&&(Z+=d,n.write(d))}}let Be=!1;function rt(){if(Be)return;let a=Ot,o=localStorage.getItem("tmux-web-token");o&&(a+=(a.includes("?")?"&":"?")+"token="+encodeURIComponent(o)),a+=(a.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,l=new WebSocket(a),l.onopen=()=>{R="connecting",Se=0,ie=!1,Te=[],Z="",n.reset(),oe=1e3},l.onmessage=d=>{typeof d.data=="string"&&Jt(d.data)},l.onclose=()=>{j||(R="connecting",!Be&&setTimeout(()=>{oe=Math.min(oe*2,1e4),!j&&!Be&&rt()},oe))},l.onerror=()=>l?.close()}function Q(a){W({type:"input",data:a})}function K(){return n.fit()}function Me(){e.classList.remove("terminal-pending")}function T(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),K(),r=requestAnimationFrame(()=>{r=0,K()&&Me()}),c=setTimeout(()=>{K()&&Me()},120)}function Gt(){T(),setTimeout(T,50),setTimeout(T,150),setTimeout(T,300)}function ct(a){return typeof a=="string"&&a.startsWith("image/")}function dt(a){return a?.files?.length?a.files[0]??null:null}function Zt(a){if(!a?.items)return null;for(let o=0;o<a.items.length;o++){let d=a.items[o];if(d?.kind==="file"&&ct(d.type))return d.getAsFile()}return null}async function Qt(a){let o=new FormData,d=a instanceof File&&a.name?a.name:"upload";o.append("file",a,d);let p=localStorage.getItem("tmux-web-token"),v={};p&&(v.Authorization="Bearer "+p);let A=await fetch(Wt,{method:"POST",body:o,headers:v});if(!A.ok){let pe="upload failed";try{let gt=await A.json();gt.error&&(pe=gt.error)}catch{}throw new Error(pe)}let V=await A.json();if(!V.path)throw new Error("no path in response");return V.path}async function Ke(a){try{let o=await Qt(a);Q(o)}catch{}}n.onData(a=>Q(a)),n.onResize(({cols:a,rows:o})=>W({type:"resize",cols:a,rows:o})),n.onScroll(()=>{R!=="live"||ie||!n.isNearScrollbackTop()||(ie=!0,W({type:"load_history",before:Se}))});let le=!1,re=!1,E=null,x=null,ce=document.createElement("div");ce.className="terminal-copy-banner",ce.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',ce.hidden=!0,e.appendChild(ce);let $=document.createElement("div");$.className="terminal-copy-banner terminal-copy-toast",$.textContent="Copied",$.hidden=!0,e.appendChild($);function ut(a,o){let d=e.querySelector(".xterm-screen, canvas"),p=(d&&d.getBoundingClientRect().width>0?d:e).getBoundingClientRect(),v=p.height>0&&n.rows>0?p.height/n.rows:18,A=p.width>0&&n.cols>0?p.width/n.cols:9;return{row:Math.max(0,Math.min(n.rows-1,Math.floor((o-p.top)/v))),col:Math.max(0,Math.min(n.cols-1,Math.floor((a-p.left)/A)))}}async function Ie(a){if(!a)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(a);return}}catch{}let o=document.createElement("textarea");o.value=a,o.style.position="fixed",o.style.opacity="0",o.style.pointerEvents="none",document.body.appendChild(o),o.focus(),o.select();try{document.execCommand("copy")}catch{}o.remove()}let de;function De(){$.hidden=!1,de&&clearTimeout(de),de=setTimeout(()=>{$.hidden=!0},1e3)}function ee(a){le=a,ce.hidden=!a,e.classList.toggle("terminal-copy-mode-active",a),a&&n.focus()}function pt(){ee(!le)}function en(a){j||a.button!==0||re||(le||a.shiftKey)&&(a.preventDefault(),a.stopPropagation(),re=!0,E=ut(a.clientX,a.clientY),x=E,n.selectCells(E.col,E.row,x.col,x.row),document.addEventListener("mousemove",Ve,!0),document.addEventListener("mouseup",Xe,!0))}function Ve(a){j||!re||!E||(a.preventDefault(),a.stopPropagation(),x=ut(a.clientX,a.clientY),n.selectCells(E.col,E.row,x.col,x.row))}function Xe(a){if(j||!re||!E||!x)return;a.preventDefault(),a.stopPropagation(),document.removeEventListener("mousemove",Ve,!0),document.removeEventListener("mouseup",Xe,!0),re=!1;let o=n.getCellsText(E.col,E.row,x.col,x.row);E=null,x=null,n.clearSelection(),ee(!1),o&&($.textContent="\u5DF2\u590D\u5236",Ie(o).then(De)),n.focus()}e.addEventListener("mousedown",en,!0),document.addEventListener("keydown",tn,!0);function tn(a){if(!(j||!n.isFocused()||a.defaultPrevented||a.isComposing)){if(le){if(a.key==="Escape"){a.preventDefault(),a.stopPropagation(),E=null,x=null,n.clearSelection(),ee(!1);return}if(a.key==="Enter"||(a.ctrlKey||a.metaKey)&&a.code==="KeyC"){a.preventDefault(),a.stopPropagation(),Ie(n.getScreenText()).then(De),ee(!1);return}}if(a.altKey&&a.code==="KeyC"&&!a.ctrlKey&&!a.metaKey){if(a.preventDefault(),a.stopPropagation(),E&&x){let o=n.getCellsText(E.col,E.row,x.col,x.row);E=null,x=null,n.clearSelection(),ee(!1),o&&($.textContent="Copied",Ie(o).then(De))}else{let o=n.getSelection();o?(n.clearSelection(),$.textContent="Copied",Ie(o).then(De)):pt()}return}if((a.ctrlKey||a.metaKey)&&a.code==="KeyV"){a.preventDefault(),a.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let o=await navigator.clipboard.read();for(let d of o)for(let p of d.types)if(ct(p)){let v=await d.getType(p);await Ke(v);return}}}catch{}try{let o=await navigator.clipboard?.readText();o&&n.pasteText(ft(o))}catch{}})();return}}}let Fe=0;function ft(a){return a.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function nn(i){let o=Zt(i.clipboardData);if(o){i.preventDefault(),i.stopPropagation();let v=Date.now();if(v-Fe<50)return;Fe=v,await Ke(o);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let p=Date.now();p-Fe<50||(Fe=p,n.pasteText(pt(d)))}let ue=0;function sn(i){i.preventDefault(),ue++,ct(i.dataTransfer)&&e.classList.add("terminal-drag-over")}function an(i){i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy")}function on(i){i.preventDefault(),ue--,ue<=0&&(ue=0,e.classList.remove("terminal-drag-over"))}async function ln(i){i.preventDefault(),ue=0,e.classList.remove("terminal-drag-over");let o=ct(i.dataTransfer);o&&await Ke(o)}function ft(i){!Kt||i.key!=="Escape"||le||i.defaultPrevented||i.repeat||i.metaKey||i.ctrlKey||i.altKey||n.isFocused()&&(i.preventDefault(),i.stopPropagation(),Q("\x1B"))}e.addEventListener("paste",nn),e.addEventListener("dragenter",sn),e.addEventListener("dragover",an),e.addEventListener("dragleave",on),e.addEventListener("drop",ln),document.addEventListener("keydown",ft,!0);function rn(i){if(i.touches.length!==1){a=null;return}let o=i.touches[0];if(!o)return;O();let d=e.getBoundingClientRect(),p=o.clientX-d.left<d.width*.4?"tui":"history",v=o.clientY-d.top<d.height*.5;a={startX:o.clientX,startY:o.clientY,lastX:o.clientX,lastY:o.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:p,tuiUp:v,sent:!1},i.stopPropagation()}function cn(i){if(!a||i.touches.length!==1)return;let o=i.touches[0];if(!o)return;let d=o.clientY-a.startY,p=o.clientX-a.startX;if(!a.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(p))return;a.scrolling=!0,ke=Date.now()+500}i.preventDefault(),i.stopPropagation();let v=performance.now(),A=o.clientY-a.lastY;if(a.zone==="tui"){a.sent||(a.sent=!0,Q(a.tuiUp?"\x1B[5~":"\x1B[6~"),ie(a.tuiUp)),a.lastX=o.clientX,a.lastY=o.clientY,a.lastT=v;return}let V=v-a.lastT;V>0&&(a.velocity=A/V),fn(-A),a.lastX=o.clientX,a.lastY=o.clientY,a.lastT=v}function dn(i){if(!a)return;let o=a.scrolling,d=a.zone,p=a.tuiUp,v=a.velocity;if(a=null,i.stopPropagation(),d==="tui"){o||(Q(p?"\x1B[5~":"\x1B[6~"),ie(p),ke=Date.now()+500,i.preventDefault());return}if(!o){n.focus();return}ke=Date.now()+500,i.preventDefault(),v&&_t(v)}function un(i){a=null,O(),i.stopPropagation()}function pn(i){i.pointerType==="touch"&&Date.now()<ke&&(i.preventDefault(),i.stopPropagation())}function fn(i){let o=e.querySelector(".xterm-rows > div"),d=o?o.getBoundingClientRect().height:16,p=d>0?Math.round(i/d):i>0?1:-1;p===0&&(p=i>0?1:-1),n.scrollLines(p)}e.addEventListener("touchstart",rn,{passive:!0,capture:!0}),e.addEventListener("touchmove",cn,{passive:!1,capture:!0}),e.addEventListener("touchend",dn,{passive:!1,capture:!0}),e.addEventListener("touchcancel",un,{passive:!0,capture:!0}),e.addEventListener("pointerup",pn,!0);function mt(){T()}window.addEventListener("resize",mt),window.visualViewport?.addEventListener("resize",T),window.visualViewport?.addEventListener("scroll",T);let ht=new ResizeObserver(()=>T());ht.observe(e),document.fonts?.ready.then(()=>{T(),setTimeout(T,50),setTimeout(T,200)}),e.addEventListener("focusin",Gt,!0);async function mn(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(i=>setTimeout(i,1e3))])}catch{}}return requestAnimationFrame(()=>{K()&&Me(),(async()=>(await mn(),K()&&W({type:"resize",cols:n.cols,rows:n.rows}),lt(),T()))()}),{destroy(){if(j=!0,O(),ee(!1),de&&clearTimeout(de),document.removeEventListener("mousemove",Ve,!0),document.removeEventListener("mouseup",Xe,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",mt),ht.disconnect(),document.removeEventListener("keydown",ft,!0)},focus(){n.focus()},sendInput(i){Q(i)},toggleCopyMode(){ut()}}})()}var me=window.__TMUX_WEB_SHELL__,g=null,y=null,he=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,B=he;window.__tmuxWebScopeAgent=he;var Ge="sessions",U="",He="",Pe="",gt="";function C(e){return B?"/a/"+encodeURIComponent(B)+e:e}var m=document.getElementById("sidebar-content"),X=document.getElementById("main-placeholder"),I=document.getElementById("terminal-container"),ve=document.getElementById("file-editor"),bn=document.getElementById("fe-path"),H=document.getElementById("fe-status"),ge=document.getElementById("fe-content"),J=document.getElementById("fe-save"),En=document.getElementById("fe-delete"),Ln=document.getElementById("fe-back"),yt=document.getElementById("fe-new-name"),xn=document.getElementById("fe-new-btn"),Ze=document.getElementById("settings-backdrop"),wt=document.getElementById("settings-popover"),bt=document.querySelector(".sidebar");function Cn(){return window.matchMedia("(max-width: 640px)").matches}function Qe(){Cn()&&bt.classList.add("collapsed")}function Et(){bt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{Et(),Lt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{Et(),Lt("files")});function Lt(e){Ge=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"?(et="",_()):$n()}var et="";async function _(){if(Ge!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents,tombstones:t.tombstones});if(s===et)return;et=s,Mn(t)}catch{m.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")}),ot()}var xt="tmux-web-sidebar-group-collapsed";function Ct(){try{return new Set(JSON.parse(localStorage.getItem(xt)||"[]"))}catch{return new Set}}function kn(e){try{localStorage.setItem(xt,JSON.stringify([...e]))}catch{}}function Sn(e,t,s){if(!e)return"\u2026";let n=s&&(e===s||e.startsWith(s+"/"))?"~"+e.slice(s.length):e,l=n.split("/").filter(Boolean);if(l.length===0)return e;for(let r=1;r<=l.length;r++){let c=l.slice(-r).join("/");if(!t.some(a=>!a||a===e?!1:(s&&(a===s||a.startsWith(s+"/"))?"~"+a.slice(s.length):a).split("/").filter(Boolean).slice(-r).join("/")===c))return c}return n}function kt(e,t,s,n=!0){let l=new Map;for(let a of e){let u=a.path||"";l.has(u)||l.set(u,[]),l.get(u).push(a)}let r=[...l.keys()],c=Ct();for(let[a,u]of l){let f=Sn(a,r,t),S=c.has(a),L=document.createElement("div");L.className="session-group-header"+(S?" collapsed":""),L.title=a||"No working directory",L.innerHTML=`<span class="sg-folder">&#128193;</span><span class="sg-label">${h(f)}</span>`,L.addEventListener("click",()=>{let M=Ct(),b=L.nextElementSibling;M.has(a)?(M.delete(a),L.classList.remove("collapsed"),b?.classList.remove("collapsed")):(M.add(a),L.classList.add("collapsed"),b?.classList.add("collapsed")),kn(M)});let F=document.createElement("div");F.className="session-group-body"+(S?" collapsed":"");for(let M of u)F.appendChild(s?Tn(M,s,n):Bn(M));m.appendChild(L),m.appendChild(F)}}function Tn(e,t,s){let n=document.createElement("div");return n.className="session-item"+(e.name===y&&B===t?" active":"")+(s?"":" session-offline"),n.setAttribute("data-session",e.name),n.setAttribute("data-agent",t),n.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
8
+ `).join("\r")}async function nn(a){let o=Zt(a.clipboardData);if(o){a.preventDefault(),a.stopPropagation();let v=Date.now();if(v-Fe<50)return;Fe=v,await Ke(o);return}let d=a.clipboardData?.getData("text/plain");if(!d)return;a.preventDefault(),a.stopPropagation();let p=Date.now();p-Fe<50||(Fe=p,n.pasteText(ft(d)))}let ue=0;function sn(a){a.preventDefault(),ue++,dt(a.dataTransfer)&&e.classList.add("terminal-drag-over")}function an(a){a.preventDefault(),a.dataTransfer&&(a.dataTransfer.dropEffect="copy")}function on(a){a.preventDefault(),ue--,ue<=0&&(ue=0,e.classList.remove("terminal-drag-over"))}async function ln(a){a.preventDefault(),ue=0,e.classList.remove("terminal-drag-over");let o=dt(a.dataTransfer);o&&await Ke(o)}function mt(a){!Kt||a.key!=="Escape"||le||a.defaultPrevented||a.repeat||a.metaKey||a.ctrlKey||a.altKey||n.isFocused()&&(a.preventDefault(),a.stopPropagation(),Q("\x1B"))}e.addEventListener("paste",nn),e.addEventListener("dragenter",sn),e.addEventListener("dragover",an),e.addEventListener("dragleave",on),e.addEventListener("drop",ln),document.addEventListener("keydown",mt,!0);function rn(a){if(a.touches.length!==1){i=null;return}let o=a.touches[0];if(!o)return;O();let d=e.getBoundingClientRect(),p=o.clientX-d.left<d.width*.4?"tui":"history",v=o.clientY-d.top<d.height*.5;i={startX:o.clientX,startY:o.clientY,lastX:o.clientX,lastY:o.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:p,tuiUp:v,sent:!1},a.stopPropagation()}function cn(a){if(!i||a.touches.length!==1)return;let o=a.touches[0];if(!o)return;let d=o.clientY-i.startY,p=o.clientX-i.startX;if(!i.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(p))return;i.scrolling=!0,ke=Date.now()+500}a.preventDefault(),a.stopPropagation();let v=performance.now(),A=o.clientY-i.lastY;if(i.zone==="tui"){i.sent||(i.sent=!0,Q(i.tuiUp?"\x1B[5~":"\x1B[6~"),ae(i.tuiUp)),i.lastX=o.clientX,i.lastY=o.clientY,i.lastT=v;return}let V=v-i.lastT;V>0&&(i.velocity=A/V),fn(-A),i.lastX=o.clientX,i.lastY=o.clientY,i.lastT=v}function dn(a){if(!i)return;let o=i.scrolling,d=i.zone,p=i.tuiUp,v=i.velocity;if(i=null,a.stopPropagation(),d==="tui"){o||(Q(p?"\x1B[5~":"\x1B[6~"),ae(p),ke=Date.now()+500,a.preventDefault());return}if(!o){n.focus();return}ke=Date.now()+500,a.preventDefault(),v&&Yt(v)}function un(a){i=null,O(),a.stopPropagation()}function pn(a){a.pointerType==="touch"&&Date.now()<ke&&(a.preventDefault(),a.stopPropagation())}function fn(a){let o=e.querySelector(".xterm-rows > div"),d=o?o.getBoundingClientRect().height:16,p=d>0?Math.round(a/d):a>0?1:-1;p===0&&(p=a>0?1:-1),n.scrollLines(p)}e.addEventListener("touchstart",rn,{passive:!0,capture:!0}),e.addEventListener("touchmove",cn,{passive:!1,capture:!0}),e.addEventListener("touchend",dn,{passive:!1,capture:!0}),e.addEventListener("touchcancel",un,{passive:!0,capture:!0}),e.addEventListener("pointerup",pn,!0);function ht(){T()}window.addEventListener("resize",ht),window.visualViewport?.addEventListener("resize",T),window.visualViewport?.addEventListener("scroll",T);let vt=new ResizeObserver(()=>T());vt.observe(e),document.fonts?.ready.then(()=>{T(),setTimeout(T,50),setTimeout(T,200)}),e.addEventListener("focusin",Gt,!0);async function mn(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(a=>setTimeout(a,1e3))])}catch{}}return requestAnimationFrame(()=>{K()&&Me(),(async()=>(await mn(),K()&&W({type:"resize",cols:n.cols,rows:n.rows}),rt(),T()))()}),{destroy(){if(j=!0,O(),ee(!1),de&&clearTimeout(de),document.removeEventListener("mousemove",Ve,!0),document.removeEventListener("mouseup",Xe,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",ht),vt.disconnect(),document.removeEventListener("keydown",mt,!0)},focus(){n.focus()},sendInput(a){Q(a)},toggleCopyMode(){pt()}}})()}var me=window.__TMUX_WEB_SHELL__,g=null,y=null,he=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,B=he;window.__tmuxWebScopeAgent=he;var Ge="sessions",U="",He="",Pe="",yt="";function C(e){return B?"/a/"+encodeURIComponent(B)+e:e}var m=document.getElementById("sidebar-content"),X=document.getElementById("main-placeholder"),I=document.getElementById("terminal-container"),ve=document.getElementById("file-editor"),bn=document.getElementById("fe-path"),H=document.getElementById("fe-status"),ge=document.getElementById("fe-content"),J=document.getElementById("fe-save"),En=document.getElementById("fe-delete"),Ln=document.getElementById("fe-back"),wt=document.getElementById("fe-new-name"),xn=document.getElementById("fe-new-btn"),Ze=document.getElementById("settings-backdrop"),bt=document.getElementById("settings-popover"),Et=document.querySelector(".sidebar");function Cn(){return window.matchMedia("(max-width: 640px)").matches}function Qe(){Cn()&&Et.classList.add("collapsed")}function Lt(){Et.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{Lt(),xt("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{Lt(),xt("files")});function xt(e){Ge=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"?(et="",_()):$n()}var et="";async function _(){if(Ge!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents,tombstones:t.tombstones});if(s===et)return;et=s,Mn(t)}catch{m.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")}),ot()}var Ct="tmux-web-sidebar-group-collapsed";function kt(){try{return new Set(JSON.parse(localStorage.getItem(Ct)||"[]"))}catch{return new Set}}function kn(e){try{localStorage.setItem(Ct,JSON.stringify([...e]))}catch{}}function Sn(e,t,s){if(!e)return"\u2026";let n=s&&(e===s||e.startsWith(s+"/"))?"~"+e.slice(s.length):e,l=n.split("/").filter(Boolean);if(l.length===0)return e;for(let r=1;r<=l.length;r++){let c=l.slice(-r).join("/");if(!t.some(i=>!i||i===e?!1:(s&&(i===s||i.startsWith(s+"/"))?"~"+i.slice(s.length):i).split("/").filter(Boolean).slice(-r).join("/")===c))return c}return n}function St(e,t,s,n=!0){let l=new Map;for(let i of e){let u=i.path||"";l.has(u)||l.set(u,[]),l.get(u).push(i)}let r=[...l.keys()],c=kt();for(let[i,u]of l){let f=Sn(i,r,t),S=c.has(i),L=document.createElement("div");L.className="session-group-header"+(S?" collapsed":""),L.title=i||"No working directory",L.innerHTML=`<span class="sg-folder">&#128193;</span><span class="sg-label">${h(f)}</span>`,L.addEventListener("click",()=>{let M=kt(),b=L.nextElementSibling;M.has(i)?(M.delete(i),L.classList.remove("collapsed"),b?.classList.remove("collapsed")):(M.add(i),L.classList.add("collapsed"),b?.classList.add("collapsed")),kn(M)});let F=document.createElement("div");F.className="session-group-body"+(S?" collapsed":"");for(let M of u)F.appendChild(s?Tn(M,s,n):Bn(M));m.appendChild(L),m.appendChild(F)}}function Tn(e,t,s){let n=document.createElement("div");return n.className="session-item"+(e.name===y&&B===t?" active":"")+(s?"":" session-offline"),n.setAttribute("data-session",e.name),n.setAttribute("data-agent",t),n.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
9
9
  <span>${h(e.name)}</span>${s?"":'<span class="meta">offline</span>'}`,s&&n.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||ye(e.name,t)}),n}function Bn(e){let t=document.createElement("div");t.className="session-item"+(e.name===y&&!B?" active":""),t.setAttribute("data-session",e.name),t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${e.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
10
10
  <span>${h(e.name)}</span>
11
11
  <button class="session-edit-btn" data-session="${h(e.name)}" title="Edit session"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
12
- `,t.addEventListener("click",n=>{n.target.closest(".session-edit-btn")||ye(e.name)});let s=t.querySelector(".session-edit-btn");return s&&s.addEventListener("click",n=>{n.stopPropagation(),Nt(e.name,s)}),t}function Mn(e){m.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"+(he?" clickable":""),s.textContent=he?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",he&&(s.title="Back to hub",s.addEventListener("click",()=>{window.location.href="/"})),m.appendChild(s),kt(t,e.home);let n=e.agents||[];for(let r of n){if(!r.sessions||r.sessions.length===0)continue;let c=r.agentId===B,a=document.createElement("div");a.className="sidebar-section-label machine-label"+(r.online?"":" agent-offline-label")+(!c&&r.online?" clickable":""),a.textContent=r.agentName+(r.online?"":" (offline)"),!c&&r.online&&(a.title="\u6253\u5F00 "+r.agentName,a.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(r.agentId)+"/"})),m.appendChild(a),kt(r.sessions,e.home,r.agentId,r.online)}let l=e.tombstones||[];if(l.length>0){let r=document.createElement("div");r.className="sidebar-section-label tombstone-label",r.textContent="\u5931\u6548\u4F1A\u8BDD",m.appendChild(r);for(let c of l){let a=document.createElement("div");a.className="session-item session-tombstone",a.setAttribute("data-session",c.name),c.agentId&&a.setAttribute("data-agent",c.agentId);let u=c.agentId?c.agentOnline?" (agent)":" (agent \u79BB\u7EBF)":"";a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"></svg>
12
+ `,t.addEventListener("click",n=>{n.target.closest(".session-edit-btn")||ye(e.name)});let s=t.querySelector(".session-edit-btn");return s&&s.addEventListener("click",n=>{n.stopPropagation(),Rt(e.name,s)}),t}function Mn(e){m.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"+(he?" clickable":""),s.textContent=he?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",he&&(s.title="Back to hub",s.addEventListener("click",()=>{window.location.href="/"})),m.appendChild(s),St(t,e.home);let n=e.agents||[];for(let r of n){if(!r.sessions||r.sessions.length===0)continue;let c=r.agentId===B,i=document.createElement("div");i.className="sidebar-section-label machine-label"+(r.online?"":" agent-offline-label")+(!c&&r.online?" clickable":""),i.textContent=r.agentName+(r.online?"":" (offline)"),!c&&r.online&&(i.title="\u6253\u5F00 "+r.agentName,i.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(r.agentId)+"/"})),m.appendChild(i),St(r.sessions,e.home,r.agentId,r.online)}let l=e.tombstones||[];if(l.length>0){let r=document.createElement("div");r.className="sidebar-section-label tombstone-label",r.textContent="\u5931\u6548\u4F1A\u8BDD",m.appendChild(r);for(let c of l){let i=document.createElement("div");i.className="session-item session-tombstone",i.setAttribute("data-session",c.name),c.agentId&&i.setAttribute("data-agent",c.agentId);let u=c.agentId?c.agentOnline?" (agent)":" (agent \u79BB\u7EBF)":"";i.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"></svg>
13
13
  <span>${h(c.name)}</span><span class="meta">\u5931\u6548${u}</span>
14
- <button class="session-edit-btn" data-session="${h(c.name)}" title="\u6062\u590D\u4F1A\u8BDD"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>`;let f=a.querySelector(".session-edit-btn");f&&f.addEventListener("click",S=>{S.stopPropagation(),Nt(c.name,f,{tombstone:!0,agentId:c.agentId})}),m.appendChild(a)}}}function h(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function ye(e,t){if(y===e&&g&&B===(t||null))return;ve.style.display="none",X.style.display="none",I.style.display="",I.classList.add("terminal-pending"),Qe(),g&&(Ye(),g.destroy(),g=null),y=e,B=t||null,window.__tmuxWebScopeAgent=B,window.__tmuxWebCurrentSession=y,Gn(e,t);let s=at(e,t);zt.set(s,Date.now()+Xn);let n=se.get(s);if(n&&n.state==="working"){n.state="idle",n.doneUntil=void 0,n.unread=!1;let l=m.querySelector(t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])');l&&We(l,s,n)}for(let l of m.querySelectorAll(".session-item"))l.classList.toggle("active",l.getAttribute("data-session")===e&&(l.getAttribute("data-agent")||null)===B);try{g=await wn(I,e,{terminal:me.terminal,scrollback:me.scrollback,theme:me.theme,wsBase:B?"/ws/a/"+encodeURIComponent(B):me.wsBase||"/ws"}),Pt()}catch(l){console.error("[shell] terminal init failed:",l);let r=l&&typeof l=="object"&&"message"in l?l.message:String(l);I.textContent="Failed to open terminal for "+e+": "+r,I.classList.remove("terminal-pending")}}var Ne=[],w=null,St=!1;function In(e){if(!w||!w.files.length)return null;let t=w.repoRoot;if(!t||!e.startsWith(t))return null;let s=e.slice(t.length+1);for(let n of w.files)if(n.path===s)return n.status;return null}async function Dn(e){St=!0;try{let t=await fetch(C("/api/git/status?path="+encodeURIComponent(e)));t.ok?w=await t.json():w=null}catch{w=null}finally{St=!1}}function Fn(){if(!w||!w.branch)return"";let{branch:e,linesAdded:t,linesRemoved:s}=w,n=`<div class="sidebar-git"><div class="git-branch-badge">
14
+ <button class="session-edit-btn" data-session="${h(c.name)}" title="\u6062\u590D\u4F1A\u8BDD"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>`;let f=i.querySelector(".session-edit-btn");f&&f.addEventListener("click",S=>{S.stopPropagation(),Rt(c.name,f,{tombstone:!0,agentId:c.agentId})}),m.appendChild(i)}}}function h(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function ye(e,t){if(y===e&&g&&B===(t||null))return;ve.style.display="none",X.style.display="none",I.style.display="",I.classList.add("terminal-pending"),Qe(),g&&(Ye(),g.destroy(),g=null),y=e,B=t||null,window.__tmuxWebScopeAgent=B,window.__tmuxWebCurrentSession=y,Gn(e,t);let s=it(e,t);jt.set(s,Date.now()+Xn);let n=se.get(s);if(n&&n.state==="working"){n.state="idle",n.doneUntil=void 0,n.unread=!1;let l=m.querySelector(t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])');l&&We(l,s,n)}for(let l of m.querySelectorAll(".session-item"))l.classList.toggle("active",l.getAttribute("data-session")===e&&(l.getAttribute("data-agent")||null)===B);try{g=await wn(I,e,{terminal:me.terminal,scrollback:me.scrollback,theme:me.theme,wsBase:B?"/ws/a/"+encodeURIComponent(B):me.wsBase||"/ws"}),Nt()}catch(l){console.error("[shell] terminal init failed:",l);let r=l&&typeof l=="object"&&"message"in l?l.message:String(l);I.textContent="Failed to open terminal for "+e+": "+r,I.classList.remove("terminal-pending")}}var Ne=[],w=null,Tt=!1;function In(e){if(!w||!w.files.length)return null;let t=w.repoRoot;if(!t||!e.startsWith(t))return null;let s=e.slice(t.length+1);for(let n of w.files)if(n.path===s)return n.status;return null}async function Dn(e){Tt=!0;try{let t=await fetch(C("/api/git/status?path="+encodeURIComponent(e)));t.ok?w=await t.json():w=null}catch{w=null}finally{Tt=!1}}function Fn(){if(!w||!w.branch)return"";let{branch:e,linesAdded:t,linesRemoved:s}=w,n=`<div class="sidebar-git"><div class="git-branch-badge">
15
15
  <svg viewBox="0 0 16 16" fill="currentColor"><path d="M11.63 1.22a.75.75 0 00-1.06 0L5.99 5.8 4.12 3.93a.75.75 0 10-1.06 1.06L4.93 6.87A3.5 3.5 0 003 10.09v3.16a.75.75 0 001 0v-3.16a2.5 2.5 0 012.5-2.5h.27l-1.06 1.06a.75.75 0 001.06 1.06l2.37-2.38a.75.75 0 000-1.06L7.59 5.27l4.04-4.05a.75.75 0 000-1.06L11.63 1.22zM3.25 13.94a.75.75 0 01-.75-.75v-.69a.75.75 0 011.5 0v.69c0 .414-.336.75-.75.75z"/></svg>
16
- ${h(e)}</div>`;return(t>0||s>0)&&(n+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${s}</span></div>`),n+"</div>"}m.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&te&&w&&(e.stopPropagation(),Dt())});var we=document.getElementById("header-git"),Tt=document.getElementById("git-diff-view"),Bt=document.getElementById("gd-path"),Re=document.getElementById("gd-status"),$e=document.getElementById("gd-content"),An=document.getElementById("gd-back"),te=null;function be(e){if(!e||!e.branch){we.style.display="none",te=null;return}te=e.repoRoot;let{branch:t,linesAdded:s,linesRemoved:n}=e,l=`<span class="branch">${h(t)}</span>`;(s>0||n>0)&&(l+=`<span class="sep">\xB7</span>+<span class="diff-add">${s}</span> <span class="diff-del">-${n}</span>`),we.innerHTML=l,we.style.display="block"}we.addEventListener("click",e=>{te&&w&&(e.stopPropagation(),Dt())});var k=document.getElementById("git-popover"),ze=document.getElementById("git-popover-backdrop");function tt(){let e=we.getBoundingClientRect();k.style.top=e.bottom+4+"px",k.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function Mt(){let e=te,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(Ue(),e){Ge="files",document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let s=document.getElementById("mode-files");s&&s.classList.add("active"),Y(e)}})}var It="";function Dt(){if(!w)return;let e=w.files||[];if(!e.length)k.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
16
+ ${h(e)}</div>`;return(t>0||s>0)&&(n+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${s}</span></div>`),n+"</div>"}m.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&te&&w&&(e.stopPropagation(),Ft())});var we=document.getElementById("header-git"),Bt=document.getElementById("git-diff-view"),Mt=document.getElementById("gd-path"),Re=document.getElementById("gd-status"),$e=document.getElementById("gd-content"),An=document.getElementById("gd-back"),te=null;function be(e){if(!e||!e.branch){we.style.display="none",te=null;return}te=e.repoRoot;let{branch:t,linesAdded:s,linesRemoved:n}=e,l=`<span class="branch">${h(t)}</span>`;(s>0||n>0)&&(l+=`<span class="sep">\xB7</span>+<span class="diff-add">${s}</span> <span class="diff-del">-${n}</span>`),we.innerHTML=l,we.style.display="block"}we.addEventListener("click",e=>{te&&w&&(e.stopPropagation(),Ft())});var k=document.getElementById("git-popover"),ze=document.getElementById("git-popover-backdrop");function tt(){let e=we.getBoundingClientRect();k.style.top=e.bottom+4+"px",k.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function It(){let e=te,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(Ue(),e){Ge="files",document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let s=document.getElementById("mode-files");s&&s.classList.add("active"),Y(e)}})}var Dt="";function Ft(){if(!w)return;let e=w.files||[];if(!e.length)k.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
17
17
  <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let t=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let s=0;s<e.length;s++){let n=e[s],l=n.additions||0,r=n.deletions||0,c="";switch(n.status){case"M":c='<span class="git-file-status M">M</span>';break;case"A":c='<span class="git-file-status A">A</span>';break;case"D":c='<span class="git-file-status D">D</span>';break;case"?":c='<span class="git-file-status ?">?</span>';break;case"R":c='<span class="git-file-status R">R</span>';break}t+=`<div class="git-popover-item" data-idx="${s}">
18
18
  ${c}<span class="file-path">${h(n.path)}</span>
19
19
  <span class="file-add">${l>0?"+"+l:""}</span>
20
20
  <span class="file-del">${r>0?"-"+r:""}</span>
21
- </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',It=t,k.innerHTML=t,k.querySelectorAll(".git-popover-item").forEach(s=>{let n=parseInt(s.dataset.idx||"0",10),l=e[n];!l||l.status==="D"||(s.classList.add("clickable"),s.style.cursor="pointer",s.addEventListener("click",()=>At(l.path)))})}tt(),k.classList.add("open"),ze.classList.add("open"),Mt()}function Hn(){return window.innerWidth>=1024}function Ft(e,t){let s=(e+(t&&e?`
21
+ </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',Dt=t,k.innerHTML=t,k.querySelectorAll(".git-popover-item").forEach(s=>{let n=parseInt(s.dataset.idx||"0",10),l=e[n];!l||l.status==="D"||(s.classList.add("clickable"),s.style.cursor="pointer",s.addEventListener("click",()=>Ht(l.path)))})}tt(),k.classList.add("open"),ze.classList.add("open"),It()}function Hn(){return window.innerWidth>=1024}function At(e,t){let s=(e+(t&&e?`
22
22
  `:"")+t).split(`
23
- `),n="",l=0,r=0;for(let c of s){let a=c;if(a.startsWith("@@")){let u=a.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(l=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),n+=`<div class="diff-line hunk"><span class="ln-body">${h(a)}</span></div>`;continue}if(a.startsWith("diff --git")||a.startsWith("index ")||a.startsWith("--- ")||a.startsWith("+++ ")||a.startsWith("new file")||a.startsWith("deleted file")){a.startsWith("diff --git")&&(n+=`<div class="diff-line header"><span class="ln-body">${h(a)}</span></div>`);continue}if(a.startsWith("+"))r++,n+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(a)}</span></div>`;else if(a.startsWith("-"))l++,n+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${h(a)}</span></div>`;else if(a.startsWith("\\"))n+=`<div class="diff-line"><span class="ln-body">${h(a)}</span></div>`;else{if(!a&&!l&&!r)continue;l++,r++,n+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(a)}</span></div>`}}return{html:n,lineCount:s.length}}var je=null;async function At(e){let t=te;if(t)if(Ue(),Hn()){I.style.display="none",g&&(Ye(),g.destroy(),g=null,je=y,y=null),be(null),ve.style.display="none",X.style.display="none",Tt.style.display="flex",Bt.textContent=e,Re.textContent="Loading\u2026",$e.innerHTML="";try{let s=await fetch(C("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!s.ok)throw new Error(await s.text());let n=await s.json();Nn(e,n.diff,n.stagedDiff)}catch(s){let n=s instanceof Error?s.message:String(s);Re.textContent="Error",$e.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${h(n)}</div>`}}else{k.innerHTML=`<div class="git-diff-view"><div class="diff-header">
23
+ `),n="",l=0,r=0;for(let c of s){let i=c;if(i.startsWith("@@")){let u=i.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(l=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),n+=`<div class="diff-line hunk"><span class="ln-body">${h(i)}</span></div>`;continue}if(i.startsWith("diff --git")||i.startsWith("index ")||i.startsWith("--- ")||i.startsWith("+++ ")||i.startsWith("new file")||i.startsWith("deleted file")){i.startsWith("diff --git")&&(n+=`<div class="diff-line header"><span class="ln-body">${h(i)}</span></div>`);continue}if(i.startsWith("+"))r++,n+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(i)}</span></div>`;else if(i.startsWith("-"))l++,n+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${h(i)}</span></div>`;else if(i.startsWith("\\"))n+=`<div class="diff-line"><span class="ln-body">${h(i)}</span></div>`;else{if(!i&&!l&&!r)continue;l++,r++,n+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(i)}</span></div>`}}return{html:n,lineCount:s.length}}var je=null;async function Ht(e){let t=te;if(t)if(Ue(),Hn()){I.style.display="none",g&&(Ye(),g.destroy(),g=null,je=y,y=null),be(null),ve.style.display="none",X.style.display="none",Bt.style.display="flex",Mt.textContent=e,Re.textContent="Loading\u2026",$e.innerHTML="";try{let s=await fetch(C("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!s.ok)throw new Error(await s.text());let n=await s.json();Nn(e,n.diff,n.stagedDiff)}catch(s){let n=s instanceof Error?s.message:String(s);Re.textContent="Error",$e.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${h(n)}</div>`}}else{k.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
24
  <button class="diff-back">\u2190 Back</button>
25
25
  <span class="diff-filename">${h(e)}</span>
26
26
  <span class="diff-loading">Loading\u2026</span>
@@ -30,11 +30,11 @@ var hn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
30
30
  </div><div class="diff-empty">Error loading diff: ${h(n)}</div></div>`}}}function Pn(e,t,s){if(!t&&!s){k.innerHTML=`<div class="git-diff-view"><div class="diff-header">
31
31
  <button class="diff-back">\u2190 Back</button>
32
32
  <span class="diff-filename">${h(e)}</span>
33
- </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:n,lineCount:l}=Ft(t,s);k.innerHTML=`<div class="git-diff-view"><div class="diff-header">
33
+ </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:n,lineCount:l}=At(t,s);k.innerHTML=`<div class="git-diff-view"><div class="diff-header">
34
34
  <button class="diff-back">\u2190 Back</button>
35
35
  <span class="diff-filename">${h(e)}</span>
36
36
  <span class="diff-filesize">${l} lines</span>
37
- </div><div class="diff-content">${n}</div></div>`,k.querySelector(".diff-back")?.addEventListener("click",()=>{k.innerHTML=It,tt(),w&&k.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),a=w.files[c];!a||a.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>At(a.path)))}),Mt()})}function Nn(e,t,s){if(!t&&!s){Re.textContent="No diff",$e.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:n,lineCount:l}=Ft(t,s);Bt.textContent=e,Re.textContent=l+" lines",$e.innerHTML=n}function Rn(){Tt.style.display="none",je?(I.style.display="",I.classList.add("terminal-pending"),ye(je),je=null):y?(I.style.display="",I.classList.add("terminal-pending"),Pt()):X.style.display="flex"}An.addEventListener("click",Rn);function Ue(){k.classList.remove("open"),ze.classList.remove("open")}ze.addEventListener("click",Ue),document.addEventListener("keydown",e=>{e.key==="Escape"&&(Ue(),Oe())});async function Ht(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 n=await s.json();w=n,be(n);return}}}catch{}w=null,be(null)}var _e=null;function Pt(){Ye(),y&&(Ht(y),_e=setInterval(()=>{y&&Ht(y)},3e4))}function Ye(){_e&&(clearInterval(_e),_e=null)}function $n(){if(Ne=me.fsRoots||[],Ne.length===0){if(y){zn(y);return}m.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}m.innerHTML="";for(let e of Ne){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(e)}`,t.addEventListener("click",()=>Y(e)),m.appendChild(t)}}async function zn(e){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(C("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?Y(t.path):m.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{m.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function Y(e){U=e,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=Dn(e);try{let s=await(await fetch(C("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,m.innerHTML="",be(w),m.innerHTML+=Fn();let n=jn(e);if(n&&!Ne.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>Y(n)),m.appendChild(r)}if(s.dirs)for(let r of s.dirs){let c=r.split("/").pop()||r.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item",a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(c)}`,a.addEventListener("click",()=>Y(r)),m.appendChild(a)}else m.innerHTML="";let l=s.files||[];for(let r of l){let c=r.split("/").pop()||r.split("\\").pop(),a=In(r),u="",f="";if(a)switch(a){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 S=document.createElement("div");S.className="file-tree-item file"+(u?" "+u:""),S.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${f}${h(c)}`,S.addEventListener("click",()=>Un(r)),m.appendChild(S)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function jn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Un(e){He=e,I.style.display="none",g&&(Ye(),g.destroy(),g=null,y=null),be(null),Qe(),X.style.display="none",ve.style.display="flex",bn.textContent=e,H.textContent="Loading...",J.style.display="none";try{let t=await fetch(C("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let n=await t.json();H.textContent=n.error||"Failed to load",ge.value="";return}let s=await t.json();ge.value=s.content,Pe=s.content,gt=s.content,H.textContent=s.size+" bytes",J.style.display=""}catch{H.textContent="Failed to load",ge.value=""}}ge.addEventListener("input",()=>{Pe=ge.value}),J.addEventListener("click",async()=>{J.textContent="Saving...",J.disabled=!0;try{let e=await fetch(C("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:He,content:Pe})});if(!e.ok){let t=await e.json();H.textContent=t.error||"Save failed";return}gt=Pe,H.textContent="Saved"}catch{H.textContent="Save failed"}finally{J.disabled=!1,J.textContent="Save"}}),En.addEventListener("click",async()=>{if(confirm("Delete "+He+"?"))try{let e=await fetch(C("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:He})});if(!e.ok){let t=await e.json();H.textContent=t.error||"Delete failed";return}ve.style.display="none",X.style.display="flex",U&&Y(U)}catch{H.textContent="Delete failed"}}),Ln.addEventListener("click",()=>{ve.style.display="none",X.style.display="flex",U&&Y(U)}),xn.addEventListener("click",async()=>{let e=yt.value.trim();if(!e)return;let t=U+"/"+e;try{let s=await fetch(C("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!s.ok){let n=await s.json();H.textContent=n.error||"Create failed";return}yt.value="",U&&Y(U)}catch{H.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=wt.classList.toggle("open");Ze.classList.toggle("open",e)}),Ze.addEventListener("click",()=>{wt.classList.remove("open"),Ze.classList.remove("open")});var D="",z=!1,qe,Ee=document.createElement("div");Ee.className="session-popover-backdrop",document.body.appendChild(Ee);var P=document.createElement("div");P.id="session-popover",P.className="session-popover",P.style.display="none",P.innerHTML=`<div class="sp-field">
37
+ </div><div class="diff-content">${n}</div></div>`,k.querySelector(".diff-back")?.addEventListener("click",()=>{k.innerHTML=Dt,tt(),w&&k.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),i=w.files[c];!i||i.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>Ht(i.path)))}),It()})}function Nn(e,t,s){if(!t&&!s){Re.textContent="No diff",$e.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:n,lineCount:l}=At(t,s);Mt.textContent=e,Re.textContent=l+" lines",$e.innerHTML=n}function Rn(){Bt.style.display="none",je?(I.style.display="",I.classList.add("terminal-pending"),ye(je),je=null):y?(I.style.display="",I.classList.add("terminal-pending"),Nt()):X.style.display="flex"}An.addEventListener("click",Rn);function Ue(){k.classList.remove("open"),ze.classList.remove("open")}ze.addEventListener("click",Ue),document.addEventListener("keydown",e=>{e.key==="Escape"&&(Ue(),Oe())});async function Pt(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 n=await s.json();w=n,be(n);return}}}catch{}w=null,be(null)}var _e=null;function Nt(){Ye(),y&&(Pt(y),_e=setInterval(()=>{y&&Pt(y)},3e4))}function Ye(){_e&&(clearInterval(_e),_e=null)}function $n(){if(Ne=me.fsRoots||[],Ne.length===0){if(y){zn(y);return}m.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}m.innerHTML="";for(let e of Ne){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(e)}`,t.addEventListener("click",()=>Y(e)),m.appendChild(t)}}async function zn(e){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(C("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?Y(t.path):m.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{m.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function Y(e){U=e,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=Dn(e);try{let s=await(await fetch(C("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,m.innerHTML="",be(w),m.innerHTML+=Fn();let n=jn(e);if(n&&!Ne.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>Y(n)),m.appendChild(r)}if(s.dirs)for(let r of s.dirs){let c=r.split("/").pop()||r.split("\\").pop(),i=document.createElement("div");i.className="file-tree-item",i.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(c)}`,i.addEventListener("click",()=>Y(r)),m.appendChild(i)}else m.innerHTML="";let l=s.files||[];for(let r of l){let c=r.split("/").pop()||r.split("\\").pop(),i=In(r),u="",f="";if(i)switch(i){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 S=document.createElement("div");S.className="file-tree-item file"+(u?" "+u:""),S.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${f}${h(c)}`,S.addEventListener("click",()=>Un(r)),m.appendChild(S)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function jn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Un(e){He=e,I.style.display="none",g&&(Ye(),g.destroy(),g=null,y=null),be(null),Qe(),X.style.display="none",ve.style.display="flex",bn.textContent=e,H.textContent="Loading...",J.style.display="none";try{let t=await fetch(C("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let n=await t.json();H.textContent=n.error||"Failed to load",ge.value="";return}let s=await t.json();ge.value=s.content,Pe=s.content,yt=s.content,H.textContent=s.size+" bytes",J.style.display=""}catch{H.textContent="Failed to load",ge.value=""}}ge.addEventListener("input",()=>{Pe=ge.value}),J.addEventListener("click",async()=>{J.textContent="Saving...",J.disabled=!0;try{let e=await fetch(C("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:He,content:Pe})});if(!e.ok){let t=await e.json();H.textContent=t.error||"Save failed";return}yt=Pe,H.textContent="Saved"}catch{H.textContent="Save failed"}finally{J.disabled=!1,J.textContent="Save"}}),En.addEventListener("click",async()=>{if(confirm("Delete "+He+"?"))try{let e=await fetch(C("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:He})});if(!e.ok){let t=await e.json();H.textContent=t.error||"Delete failed";return}ve.style.display="none",X.style.display="flex",U&&Y(U)}catch{H.textContent="Delete failed"}}),Ln.addEventListener("click",()=>{ve.style.display="none",X.style.display="flex",U&&Y(U)}),xn.addEventListener("click",async()=>{let e=wt.value.trim();if(!e)return;let t=U+"/"+e;try{let s=await fetch(C("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!s.ok){let n=await s.json();H.textContent=n.error||"Create failed";return}wt.value="",U&&Y(U)}catch{H.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=bt.classList.toggle("open");Ze.classList.toggle("open",e)}),Ze.addEventListener("click",()=>{bt.classList.remove("open"),Ze.classList.remove("open")});var D="",z=!1,qe,Ee=document.createElement("div");Ee.className="session-popover-backdrop",document.body.appendChild(Ee);var P=document.createElement("div");P.id="session-popover",P.className="session-popover",P.style.display="none",P.innerHTML=`<div class="sp-field">
38
38
  <label>Rename session</label>
39
39
  <input type="text" id="sp-name" />
40
40
  </div>
@@ -44,5 +44,5 @@ var hn=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
44
44
  </div>
45
45
  <hr class="sp-divider" />
46
46
  <button class="btn primary" id="sp-rebuild" style="display:none">\u91CD\u5EFA\u540C\u540D\u540C\u8DEF\u5F84 session</button>
47
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(P);function _n(e){let t=e.getBoundingClientRect();P.style.top=t.bottom+4+"px",P.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Nt(e,t,s){D=e,z=!!s?.tombstone,qe=s?.agentId;let n=document.getElementById("sp-name");n.value=e;let l=P.querySelector(".sp-field label");l.textContent=z?"\u91CD\u547D\u540D (\u4EC5\u8BB0\u5F55\uFF0C\u4E0D\u91CD\u5EFA)":"Rename session";let r=document.getElementById("sp-rebuild");r.style.display=z?"":"none";let c=document.getElementById("sp-delete");c.textContent=z?"\u5220\u9664\u5931\u6548\u8BB0\u5F55 (\u5893\u7891\u6D88\u5931)":"Delete session";let a=document.getElementById("sp-save");a.textContent=z?"\u91CD\u547D\u540D":"Save",Ee.classList.add("open"),P.style.display="block",_n(t),setTimeout(()=>n.focus(),50)}function G(){Ee.classList.remove("open"),P.style.display="none"}Ee.addEventListener("click",G),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===D){G();return}try{let t=z?{oldName:D,newName:e,agentId:qe}:{oldName:D,newName:e};if(!(await fetch(C(z?"/api/sessions/rename-tombstone":"/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)return;G(),_(),!z&&y===D&&ye(e)}catch{}}),document.getElementById("sp-rebuild").addEventListener("click",async()=>{if(confirm('\u91CD\u5EFA\u4F1A\u8BDD "'+D+'" (\u540C\u540D\u540C\u8DEF\u5F84\uFF0C\u5185\u5BB9\u4E3A\u7A7A)\uFF1F'))try{let e=await fetch(C("/api/sessions/rebuild"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:qe})});if(!e.ok){let t=await e.json();alert(t.error||"\u91CD\u5EFA\u5931\u8D25");return}G(),_()}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(z){if(!confirm('\u5220\u9664\u5931\u6548\u8BB0\u5F55 "'+D+'"\uFF1F'))return;try{if(!(await fetch(C("/api/sessions/dismiss"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:qe})})).ok)return;G(),_()}catch{}return}if(confirm('Delete session "'+D+'"?'))try{if(!(await fetch(C("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D})})).ok)return;G(),y===D&&(g&&(g.destroy(),g=null),y=null,I.style.display="none",X.style.display="flex"),_()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",G);var Yn={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||!g)return;let s=Yn[t.dataset.key];s&&(g.sendInput(s),g.focus())});var N=document.getElementById("mk-input"),Rt=document.getElementById("mk-send");function $t(){if(!N||!g)return;let e=N.value;e&&(g.sendInput(e+"\r"),N.value="",N.rows=1,g.focus())}if(N&&(N.addEventListener("input",()=>{N.rows=1;let e=N.value.split(`
48
- `).length;N.rows=Math.min(e,4)}),N.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),$t())})),Rt&&Rt.addEventListener("click",$t),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 nt=document.getElementById("process-panel"),Le=document.getElementById("proc-list"),qn=document.getElementById("proc-close"),st=document.getElementById("proc-backdrop"),On=document.getElementById("header-ram");function Wn(){Le.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(C("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){Le.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let s of e){let n=s.rss,l=n<1048576?(n/1024).toFixed(0)+"K":(n/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+s.pid+'"><span class="proc-mem">'+s.mem+'%</span><span class="proc-rss">'+l+'</span><span class="proc-cmd" title="'+h(s.command)+'">'+h(s.command)+'</span><button class="proc-kill" title="Kill PID '+s.pid+'">&times;</button></div>'}Le.innerHTML=t;for(let s of Le.querySelectorAll(".proc-row")){let n=s.querySelector(".proc-kill");n&&n.addEventListener("click",l=>{let r=l.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(C("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?r.style.opacity="0.3":alert("Failed: "+(c.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{Le.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Kn(){nt.classList.add("open"),st.classList.add("open"),Wn()}function Oe(){nt.classList.remove("open"),st.classList.remove("open")}On.addEventListener("click",e=>{e.stopPropagation(),nt.classList.contains("open")?Oe():Kn()}),qn.addEventListener("click",Oe),st.addEventListener("click",Oe);var Vn=2e3,xe=null,it=null,ne=new Map,se=new Map,Xn=5e3,zt=new Map;function at(e,t){return(t?"a:"+t+":":"l:")+e}function Jn(e,t,s){let n=at(e,t);if(s==="working"){let f=zt.get(n);if(f&&Date.now()<f)return}let l=se.get(n),r=e===y&&(t??null)===B,c=s==="idle"&&l?.state==="working";se.set(n,{session:e,agentId:t,state:s,doneUntil:c?Date.now()+Vn:void 0,unread:c?!r:!1});let a=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',u=m.querySelector(a);u&&We(u,n,se.get(n))}function We(e,t,s){let n=ne.get(t);n&&(clearTimeout(n),ne.delete(t)),e.classList.remove("working","just-done");let l=s.session===y&&(s.agentId??null)===B;s.state==="working"?e.classList.add("working"):l&&s.doneUntil&&Date.now()<s.doneUntil?(e.classList.add("just-done"),ne.set(t,setTimeout(()=>{e.classList.remove("just-done"),ne.delete(t)},Math.max(0,s.doneUntil-Date.now())))):!l&&s.unread&&e.classList.add("just-done")}function ot(){for(let[e,t]of se){let s=t.agentId?'.session-item[data-agent="'+CSS.escape(t.agentId)+'"][data-session="'+CSS.escape(t.session)+'"]':'.session-item[data-session="'+CSS.escape(t.session)+'"]:not([data-agent])',n=m.querySelector(s);n&&We(n,e,t)}}function Gn(e,t){let s=at(e,t),n=se.get(s);if(!n)return;let l=ne.get(s);l&&(clearTimeout(l),ne.delete(s)),n.doneUntil=void 0,n.unread=!1;let r=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',c=m.querySelector(r);c&&We(c,s,n)}function jt(){if(xe||it)return;let e=localStorage.getItem("tmux-web-token"),t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{let s=new WebSocket(t);xe=s,s.onmessage=n=>{try{let l=JSON.parse(String(n.data));l&&l.type==="session.activity"&&typeof l.session=="string"&&Jn(l.session,l.agentId||void 0,l.state==="working"?"working":"idle")}catch{}},s.onclose=()=>{xe===s&&(xe=null,it=setTimeout(()=>{it=null,jt()},5e3))},s.onerror=()=>{try{s.close()}catch{}}}catch{xe=null}}window.__openSession=ye,window.__refreshSidebar=_,_(),Qe(),ot(),jt(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&ot()}),setInterval(()=>{document.visibilityState==="visible"&&_()},1e4);
47
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(P);function _n(e){let t=e.getBoundingClientRect();P.style.top=t.bottom+4+"px",P.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Rt(e,t,s){D=e,z=!!s?.tombstone,qe=s?.agentId;let n=document.getElementById("sp-name");n.value=e;let l=P.querySelector(".sp-field label");l.textContent=z?"\u91CD\u547D\u540D (\u4EC5\u8BB0\u5F55\uFF0C\u4E0D\u91CD\u5EFA)":"Rename session";let r=document.getElementById("sp-rebuild");r.style.display=z?"":"none";let c=document.getElementById("sp-delete");c.textContent=z?"\u5220\u9664\u5931\u6548\u8BB0\u5F55 (\u5893\u7891\u6D88\u5931)":"Delete session";let i=document.getElementById("sp-save");i.textContent=z?"\u91CD\u547D\u540D":"Save",Ee.classList.add("open"),P.style.display="block",_n(t),setTimeout(()=>n.focus(),50)}function G(){Ee.classList.remove("open"),P.style.display="none"}Ee.addEventListener("click",G),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===D){G();return}try{let t=z?{oldName:D,newName:e,agentId:qe}:{oldName:D,newName:e};if(!(await fetch(C(z?"/api/sessions/rename-tombstone":"/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)return;G(),_(),!z&&y===D&&ye(e)}catch{}}),document.getElementById("sp-rebuild").addEventListener("click",async()=>{if(confirm('\u91CD\u5EFA\u4F1A\u8BDD "'+D+'" (\u540C\u540D\u540C\u8DEF\u5F84\uFF0C\u5185\u5BB9\u4E3A\u7A7A)\uFF1F'))try{let e=await fetch(C("/api/sessions/rebuild"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:qe})});if(!e.ok){let t=await e.json();alert(t.error||"\u91CD\u5EFA\u5931\u8D25");return}G(),_()}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(z){if(!confirm('\u5220\u9664\u5931\u6548\u8BB0\u5F55 "'+D+'"\uFF1F'))return;try{if(!(await fetch(C("/api/sessions/dismiss"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D,agentId:qe})})).ok)return;G(),_()}catch{}return}if(confirm('Delete session "'+D+'"?'))try{if(!(await fetch(C("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:D})})).ok)return;G(),y===D&&(g&&(g.destroy(),g=null),y=null,I.style.display="none",X.style.display="flex"),_()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",G);var Yn={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||!g)return;let s=Yn[t.dataset.key];s&&(g.sendInput(s),g.focus())});var N=document.getElementById("mk-input"),$t=document.getElementById("mk-send");function zt(){if(!N||!g)return;let e=N.value;e&&(g.sendInput(e+"\r"),N.value="",N.rows=1,g.focus())}if(N&&(N.addEventListener("input",()=>{N.rows=1;let e=N.value.split(`
48
+ `).length;N.rows=Math.min(e,4)}),N.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),zt())})),$t&&$t.addEventListener("click",zt),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 nt=document.getElementById("process-panel"),Le=document.getElementById("proc-list"),qn=document.getElementById("proc-close"),st=document.getElementById("proc-backdrop"),On=document.getElementById("header-ram");function Wn(){Le.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(C("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){Le.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let s of e){let n=s.rss,l=n<1048576?(n/1024).toFixed(0)+"K":(n/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+s.pid+'"><span class="proc-mem">'+s.mem+'%</span><span class="proc-rss">'+l+'</span><span class="proc-cmd" title="'+h(s.command)+'">'+h(s.command)+'</span><button class="proc-kill" title="Kill PID '+s.pid+'">&times;</button></div>'}Le.innerHTML=t;for(let s of Le.querySelectorAll(".proc-row")){let n=s.querySelector(".proc-kill");n&&n.addEventListener("click",l=>{let r=l.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(C("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?r.style.opacity="0.3":alert("Failed: "+(c.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{Le.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Kn(){nt.classList.add("open"),st.classList.add("open"),Wn()}function Oe(){nt.classList.remove("open"),st.classList.remove("open")}On.addEventListener("click",e=>{e.stopPropagation(),nt.classList.contains("open")?Oe():Kn()}),qn.addEventListener("click",Oe),st.addEventListener("click",Oe);var Vn=2e3,xe=null,at=null,ne=new Map,se=new Map,Xn=5e3,jt=new Map;function it(e,t){return(t?"a:"+t+":":"l:")+e}function Jn(e,t,s){let n=it(e,t);if(s==="working"){let f=jt.get(n);if(f&&Date.now()<f)return}let l=se.get(n),r=e===y&&(t??null)===B,c=s==="idle"&&l?.state==="working";se.set(n,{session:e,agentId:t,state:s,doneUntil:c?Date.now()+Vn:void 0,unread:c?!r:!1});let i=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',u=m.querySelector(i);u&&We(u,n,se.get(n))}function We(e,t,s){let n=ne.get(t);n&&(clearTimeout(n),ne.delete(t)),e.classList.remove("working","just-done");let l=s.session===y&&(s.agentId??null)===B;s.state==="working"?e.classList.add("working"):l&&s.doneUntil&&Date.now()<s.doneUntil?(e.classList.add("just-done"),ne.set(t,setTimeout(()=>{e.classList.remove("just-done"),ne.delete(t)},Math.max(0,s.doneUntil-Date.now())))):!l&&s.unread&&e.classList.add("just-done")}function ot(){for(let[e,t]of se){let s=t.agentId?'.session-item[data-agent="'+CSS.escape(t.agentId)+'"][data-session="'+CSS.escape(t.session)+'"]':'.session-item[data-session="'+CSS.escape(t.session)+'"]:not([data-agent])',n=m.querySelector(s);n&&We(n,e,t)}}function Gn(e,t){let s=it(e,t),n=se.get(s);if(!n)return;let l=ne.get(s);l&&(clearTimeout(l),ne.delete(s)),n.doneUntil=void 0,n.unread=!1;let r=t?'.session-item[data-agent="'+CSS.escape(t)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',c=m.querySelector(r);c&&We(c,s,n)}function Ut(){if(xe||at)return;let e=localStorage.getItem("tmux-web-token"),t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{let s=new WebSocket(t);xe=s,s.onmessage=n=>{try{let l=JSON.parse(String(n.data));l&&l.type==="session.activity"&&typeof l.session=="string"&&Jn(l.session,l.agentId||void 0,l.state==="working"?"working":"idle")}catch{}},s.onclose=()=>{xe===s&&(xe=null,at=setTimeout(()=>{at=null,Ut()},5e3))},s.onerror=()=>{try{s.close()}catch{}}}catch{xe=null}}window.__openSession=ye,window.__refreshSidebar=_,_(),Qe(),ot(),Ut(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&ot()}),setInterval(()=>{document.visibilityState==="visible"&&_()},1e4);
@@ -1,8 +1,8 @@
1
1
  const Ht=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function rt(a){return a.replace(/\x1b\[\?1049[hl]/g,"")}function H(a){return a.replace(Ht,"")}let N;function Nt(){return N||(document.querySelector("link[data-tmux-web-xterm-css]")?N=Promise.resolve():(N=new Promise(o=>{const r=document.createElement("link");r.rel="stylesheet",r.href="/assets/xterm.css",r.setAttribute("data-tmux-web-xterm-css",""),r.addEventListener("load",()=>o(),{once:!0}),r.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(r)}),N))}class st{terminal;fitAddon;container;constructor(o,r,n){this.container=o,this.terminal=r,this.fitAddon=n,this.terminal.loadAddon(this.fitAddon),this.terminal.open(o)}static async create(o,r,n){const[{Terminal:l},{FitAddon:d}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),Nt()]),m=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:r,convertEol:!1,smoothScrollDuration:120,theme:n});return new st(o,m,new d)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(o){return new Promise(r=>this.terminal.write(o,r))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(o,r=!1){this.terminal.scrollToLine(Math.max(0,o),r)}scrollLines(o){const r=this.terminal.buffer?.active;this.terminal.scrollLines(o)}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const o=_t(this.container);if(o.width<=0||o.height<=0)return!1;const n=this.terminal._core?._renderService?.dimensions?.css?.cell;if(!n?.width||!n?.height)return this.fitAddon.fit(),!0;const l=n.width,d=n.height,m=this.terminal.element?.querySelector(".xterm-viewport"),s=m?Math.max(0,m.offsetWidth-m.clientWidth):0,f=Math.max(2,Math.floor((o.width-s-l/2)/l)),p=Math.max(2,Math.floor(o.height/d));return(f!==this.terminal.cols||p!==this.terminal.rows)&&this.terminal.resize(f,p),!0}focus(){this.terminal.focus()}pasteText(o){this.terminal.paste(o)}input(o){this.terminal.input(o,!1)}onData(o){this.terminal.onData(o),this.terminal.onBinary(o)}onResize(o){this.terminal.onResize(o)}onScroll(o){this.terminal.onScroll(o)}attachCustomKeyEventHandler(o){this.terminal.attachCustomKeyEventHandler(o)}isFocused(){const o=document.activeElement;return o?o===this.container||o===this.terminal.textarea||this.container.contains(o)||!!o.closest?.(".xterm"):!1}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(o,r){const n=this.terminal.buffer.active,[l,d]=o<=r?[o,r]:[r,o],m=n.viewportY,s=[];for(let f=l;f<=d;f++){const p=n.getLine(m+f);s.push(p?p.translateToString(!0):"")}return s.join(`
2
2
  `)}selectRows(o,r){const n=this.terminal.buffer.active,[l,d]=o<=r?[o,r]:[r,o];this.terminal.selectLines(n.viewportY+l,n.viewportY+d)}selectCells(o,r,n,l){const d=this.terminal.buffer.active,m=Math.min(r,l),s=Math.max(r,l);if(m===s){const f=Math.min(o,n),p=Math.max(o,n);this.terminal.select(f,d.viewportY+m,p-f+1)}else this.terminal.selectLines(d.viewportY+m,d.viewportY+s)}getCellsText(o,r,n,l){const d=this.terminal.buffer.active,m=Math.min(r,l),s=Math.max(r,l),f=r<l?o:r>l?n:Math.min(o,n),p=r<l?n:r>l?o:Math.max(o,n),et=d.viewportY,P=[];for(let L=m;L<=s;L++){const _=d.getLine(et+L);if(!_){P.push("");continue}const w=L===m?f:0,M=L===s?p:this.terminal.cols-1;let F="";for(let q=w;q<=M;q++){const C=_.getCell(q);C&&(F+=C.getChars())}P.push(F.replace(/\s+$/,""))}return P.join(`
3
- `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function _t(a){const o=a.getBoundingClientRect(),r=window.visualViewport;return r?{width:Math.min(o.width,r.width),height:Math.max(0,Math.min(o.height,r.height-Math.max(0,o.top)))}:o}function qt(a,o,r){return a.innerHTML="",a.classList.add("terminal-pending"),(async()=>{const n=await st.create(a,r.scrollback,r.theme);let l,d=0,m,s=null,f=0,p=0;const et=.2,P=10,L=.95,_=.04;let w=null,M=null;function F(t){w||(w=document.createElement("div"),w.style.cssText=["position:absolute","left:0","width:40%","padding:10px 0","text-align:center","font:600 14px/1.4 system-ui, -apple-system, sans-serif","color:rgba(255,255,255,0.95)","background:rgba(20,30,50,0.55)","border:1px solid rgba(255,255,255,0.25)","border-radius:10px","pointer-events:none","opacity:0","transition:opacity 0.15s ease","z-index:6"].join(";"),a.appendChild(w)),w.textContent=t?"\u2191 \u4E0A\u7FFB\u4E00\u9875 (PgUp)":"\u2193 \u4E0B\u7FFB\u4E00\u9875 (PgDn)",w.style.top=t?"10%":"auto",w.style.bottom=t?"auto":"10%",w.style.opacity="1",M&&clearTimeout(M),M=setTimeout(()=>{w&&(w.style.opacity="0")},900)}function q(){M&&clearTimeout(M),w&&(w.style.opacity="0")}function C(){f&&(cancelAnimationFrame(f),f=0),p=0}function gt(t){if(C(),Math.abs(t)<et)return;p=Math.max(-P,Math.min(P,t));const e=()=>{if(E){f=0;return}const i=a.querySelector(".xterm-rows > div"),u=i?i.getBoundingClientRect().height:16;let c=u>0?Math.round(p*16/u):p>0?1:-1;c===0&&(c=p>0?1:-1);const v=n.viewportY()-c,tt=n.baseY(),R=Math.max(0,Math.min(v,tt));if(n.scrollToLine(R,!0),p*=L,R!==v){f=0;return}if(Math.abs(p)<_){f=0;return}f=requestAnimationFrame(e)};f=requestAnimationFrame(e)}let j=0,x="connecting",V=0,A=!1,W=[],k="",E=!1,z=1e3;const wt=location.protocol==="https:"?"wss:":"ws:",yt=r.wsBase||"/ws",vt=wt+"//"+location.host+yt+"/"+encodeURIComponent(o),xt="/api/session/"+encodeURIComponent(o)+"/upload",Tt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function bt(){return W.join("")+k}async function Et(t,e=0){const i=t?n.viewportY():0,u=H(bt());if(n.reset(),!u){t||n.scrollToBottom();return}await n.write(u),t?n.scrollToLine(e+i):n.scrollToBottom()}function S(t){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(t))}function Lt(t){let e;try{e=JSON.parse(t)}catch{x==="live"&&(k+=t,n.write(H(t)));return}if(e.type==="auth.required"){const i=localStorage.getItem("tmux-web-token");i?S({type:"auth.token",token:i}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="attach_failed"&&typeof e.message=="string"){J=!0,x="dead",n.reset(),n.write(`\r
3
+ `)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}}function _t(a){const o=a.getBoundingClientRect(),r=window.visualViewport;return r?{width:Math.min(o.width,r.width),height:Math.max(0,Math.min(o.height,r.height-Math.max(0,o.top)))}:o}function jt(a,o,r){return a.innerHTML="",a.classList.add("terminal-pending"),(async()=>{const n=await st.create(a,r.scrollback,r.theme);let l,d=0,m,s=null,f=0,p=0;const et=.2,P=10,L=.95,_=.04;let w=null,M=null;function F(t){w||(w=document.createElement("div"),w.style.cssText=["position:absolute","left:0","width:40%","padding:10px 0","text-align:center","font:600 14px/1.4 system-ui, -apple-system, sans-serif","color:rgba(255,255,255,0.95)","background:rgba(20,30,50,0.55)","border:1px solid rgba(255,255,255,0.25)","border-radius:10px","pointer-events:none","opacity:0","transition:opacity 0.15s ease","z-index:6"].join(";"),a.appendChild(w)),w.textContent=t?"\u2191 \u4E0A\u7FFB\u4E00\u9875 (PgUp)":"\u2193 \u4E0B\u7FFB\u4E00\u9875 (PgDn)",w.style.top=t?"10%":"auto",w.style.bottom=t?"auto":"10%",w.style.opacity="1",M&&clearTimeout(M),M=setTimeout(()=>{w&&(w.style.opacity="0")},900)}function q(){M&&clearTimeout(M),w&&(w.style.opacity="0")}function C(){f&&(cancelAnimationFrame(f),f=0),p=0}function wt(t){if(C(),Math.abs(t)<et)return;p=Math.max(-P,Math.min(P,t));const e=()=>{if(E){f=0;return}const i=a.querySelector(".xterm-rows > div"),u=i?i.getBoundingClientRect().height:16;let c=u>0?Math.round(p*16/u):p>0?1:-1;c===0&&(c=p>0?1:-1);const v=n.viewportY()-c,tt=n.baseY(),R=Math.max(0,Math.min(v,tt));if(n.scrollToLine(R,!0),p*=L,R!==v){f=0;return}if(Math.abs(p)<_){f=0;return}f=requestAnimationFrame(e)};f=requestAnimationFrame(e)}let j=0,x="connecting",V=0,A=!1,W=[],k="",E=!1,z=1e3;const yt=location.protocol==="https:"?"wss:":"ws:",at=r.wsBase||"/ws",vt=yt+"//"+location.host+at+"/"+encodeURIComponent(o),xt=(at.replace(/^\/ws/,"")||"")+"/api/session/"+encodeURIComponent(o)+"/upload",Tt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function bt(){return W.join("")+k}async function Et(t,e=0){const i=t?n.viewportY():0,u=H(bt());if(n.reset(),!u){t||n.scrollToBottom();return}await n.write(u),t?n.scrollToLine(e+i):n.scrollToBottom()}function S(t){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(t))}function Lt(t){let e;try{e=JSON.parse(t)}catch{x==="live"&&(k+=t,n.write(H(t)));return}if(e.type==="auth.required"){const i=localStorage.getItem("tmux-web-token");i?S({type:"auth.token",token:i}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="attach_failed"&&typeof e.message=="string"){J=!0,x="dead",n.reset(),n.write(`\r
4
4
  \x1B[31m`+H(e.message)+`\x1B[0m\r
5
- `).then(()=>{G();try{l?.close()}catch{}});return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),z=1e3,D(),S({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(e.type==="auth.failed"){if(e.permanentLock||e.retryAfterMs&&e.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(e.message);return}return}if(e.type==="snapshot"&&typeof e.data=="string"){const i=rt(H(e.data));W=[i],k="",V=typeof e.lines=="number"?e.lines:r.terminal.initialLines,x="live",n.reset(),D(),S({type:"resize",cols:n.cols,rows:n.rows}),n.write(i).then(()=>n.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){const i=new URL(location.href);i.searchParams.get("window")!==String(e.activeIndex)&&(i.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",i)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){e.lines>0&&e.data?(W.unshift(rt(e.data)),V+=e.lines,Et(!0,e.lines).finally(()=>{A=!1})):A=!1;return}if(e.type==="data"&&typeof e.data=="string"){const i=rt(H(e.data));if(x==="connecting"){x="live",k=i,n.write(i);return}x==="live"&&(k+=i,n.write(i))}}let J=!1;function at(){if(J)return;let t=vt;const e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),t+=(t.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,l=new WebSocket(t),l.onopen=()=>{x="connecting",V=0,A=!1,W=[],k="",n.reset(),z=1e3},l.onmessage=i=>{typeof i.data=="string"&&Lt(i.data)},l.onclose=()=>{E||(x="connecting",!J&&setTimeout(()=>{z=Math.min(z*2,1e4),!E&&!J&&at()},z))},l.onerror=()=>l?.close()}function I(t){S({type:"input",data:t})}function D(){return n.fit()}function G(){a.classList.remove("terminal-pending")}function y(){d&&cancelAnimationFrame(d),m&&clearTimeout(m),D(),d=requestAnimationFrame(()=>{d=0,D()&&G()}),m=setTimeout(()=>{D()&&G()},120)}function Mt(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function lt(t){return typeof t=="string"&&t.startsWith("image/")}function ct(t){return t?.files?.length?t.files[0]??null:null}function Ct(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){const i=t.items[e];if(i?.kind==="file"&&lt(i.type))return i.getAsFile()}return null}async function St(t){const e=new FormData,i=t instanceof File&&t.name?t.name:"upload";e.append("file",t,i);const u=localStorage.getItem("tmux-web-token"),c={};u&&(c.Authorization="Bearer "+u);const b=await fetch(xt,{method:"POST",body:e,headers:c});if(!b.ok){let tt="upload failed";try{const R=await b.json();R.error&&(tt=R.error)}catch{}throw new Error(tt)}const v=await b.json();if(!v.path)throw new Error("no path in response");return v.path}async function nt(t){try{const e=await St(t);I(e)}catch{}}n.onData(t=>I(t)),n.onResize(({cols:t,rows:e})=>S({type:"resize",cols:t,rows:e})),n.onScroll(()=>{x!=="live"||A||!n.isNearScrollbackTop()||(A=!0,S({type:"load_history",before:V}))});let B=!1,K=!1,h=null,g=null;const U=document.createElement("div");U.className="terminal-copy-banner",U.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',U.hidden=!0,a.appendChild(U);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,a.appendChild(T);function ut(t,e){const i=a.querySelector(".xterm-screen, canvas"),c=(i&&i.getBoundingClientRect().width>0?i:a).getBoundingClientRect(),b=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((e-c.top)/b))),col:Math.max(0,Math.min(n.cols-1,Math.floor((t-c.left)/v)))}}async function $(t){if(!t)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(t);return}}catch{}const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.opacity="0",e.style.pointerEvents="none",document.body.appendChild(e),e.focus(),e.select();try{document.execCommand("copy")}catch{}e.remove()}let X;function Q(){T.hidden=!1,X&&clearTimeout(X),X=setTimeout(()=>{T.hidden=!0},1e3)}function Y(t){B=t,U.hidden=!t,a.classList.toggle("terminal-copy-mode-active",t),t&&n.focus()}function ft(){Y(!B)}function Dt(t){E||t.button!==0||K||(B||t.shiftKey)&&(t.preventDefault(),t.stopPropagation(),K=!0,h=ut(t.clientX,t.clientY),g=h,n.selectCells(h.col,h.row,g.col,g.row),document.addEventListener("mousemove",ot,!0),document.addEventListener("mouseup",it,!0))}function ot(t){E||!K||!h||(t.preventDefault(),t.stopPropagation(),g=ut(t.clientX,t.clientY),n.selectCells(h.col,h.row,g.col,g.row))}function it(t){if(E||!K||!h||!g)return;t.preventDefault(),t.stopPropagation(),document.removeEventListener("mousemove",ot,!0),document.removeEventListener("mouseup",it,!0),K=!1;const e=n.getCellsText(h.col,h.row,g.col,g.row);h=null,g=null,n.clearSelection(),Y(!1),e&&(T.textContent="\u5DF2\u590D\u5236",$(e).then(Q)),n.focus()}a.addEventListener("mousedown",Dt,!0),document.addEventListener("keydown",Pt,!0);function Pt(t){if(!(E||!n.isFocused()||t.defaultPrevented||t.isComposing)){if(B){if(t.key==="Escape"){t.preventDefault(),t.stopPropagation(),h=null,g=null,n.clearSelection(),Y(!1);return}if(t.key==="Enter"||(t.ctrlKey||t.metaKey)&&t.code==="KeyC"){t.preventDefault(),t.stopPropagation(),$(n.getScreenText()).then(Q),Y(!1);return}}if(t.altKey&&t.code==="KeyC"&&!t.ctrlKey&&!t.metaKey){if(t.preventDefault(),t.stopPropagation(),h&&g){const e=n.getCellsText(h.col,h.row,g.col,g.row);h=null,g=null,n.clearSelection(),Y(!1),e&&(T.textContent="Copied",$(e).then(Q))}else{const e=n.getSelection();e?(n.clearSelection(),T.textContent="Copied",$(e).then(Q)):ft()}return}if((t.ctrlKey||t.metaKey)&&t.code==="KeyV"){t.preventDefault(),t.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const e=await navigator.clipboard.read();for(const i of e)for(const u of i.types)if(lt(u)){const c=await i.getType(u);await nt(c);return}}}catch{}try{const e=await navigator.clipboard?.readText();e&&n.pasteText(dt(e))}catch{}})();return}}}let Z=0;function dt(t){return t.split(`\r
5
+ `).then(()=>{G();try{l?.close()}catch{}});return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),z=1e3,D(),S({type:"resize",cols:n.cols,rows:n.rows}),y();return}if(e.type==="auth.failed"){if(e.permanentLock||e.retryAfterMs&&e.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(e.message);return}return}if(e.type==="snapshot"&&typeof e.data=="string"){const i=rt(H(e.data));W=[i],k="",V=typeof e.lines=="number"?e.lines:r.terminal.initialLines,x="live",n.reset(),D(),S({type:"resize",cols:n.cols,rows:n.rows}),n.write(i).then(()=>n.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){const i=new URL(location.href);i.searchParams.get("window")!==String(e.activeIndex)&&(i.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",i)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){e.lines>0&&e.data?(W.unshift(rt(e.data)),V+=e.lines,Et(!0,e.lines).finally(()=>{A=!1})):A=!1;return}if(e.type==="data"&&typeof e.data=="string"){const i=rt(H(e.data));if(x==="connecting"){x="live",k=i,n.write(i);return}x==="live"&&(k+=i,n.write(i))}}let J=!1;function lt(){if(J)return;let t=vt;const e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),t+=(t.includes("?")?"&":"?")+"cols="+n.cols+"&rows="+n.rows,l=new WebSocket(t),l.onopen=()=>{x="connecting",V=0,A=!1,W=[],k="",n.reset(),z=1e3},l.onmessage=i=>{typeof i.data=="string"&&Lt(i.data)},l.onclose=()=>{E||(x="connecting",!J&&setTimeout(()=>{z=Math.min(z*2,1e4),!E&&!J&&lt()},z))},l.onerror=()=>l?.close()}function I(t){S({type:"input",data:t})}function D(){return n.fit()}function G(){a.classList.remove("terminal-pending")}function y(){d&&cancelAnimationFrame(d),m&&clearTimeout(m),D(),d=requestAnimationFrame(()=>{d=0,D()&&G()}),m=setTimeout(()=>{D()&&G()},120)}function Mt(){y(),setTimeout(y,50),setTimeout(y,150),setTimeout(y,300)}function ct(t){return typeof t=="string"&&t.startsWith("image/")}function ut(t){return t?.files?.length?t.files[0]??null:null}function Ct(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){const i=t.items[e];if(i?.kind==="file"&&ct(i.type))return i.getAsFile()}return null}async function St(t){const e=new FormData,i=t instanceof File&&t.name?t.name:"upload";e.append("file",t,i);const u=localStorage.getItem("tmux-web-token"),c={};u&&(c.Authorization="Bearer "+u);const b=await fetch(xt,{method:"POST",body:e,headers:c});if(!b.ok){let tt="upload failed";try{const R=await b.json();R.error&&(tt=R.error)}catch{}throw new Error(tt)}const v=await b.json();if(!v.path)throw new Error("no path in response");return v.path}async function nt(t){try{const e=await St(t);I(e)}catch{}}n.onData(t=>I(t)),n.onResize(({cols:t,rows:e})=>S({type:"resize",cols:t,rows:e})),n.onScroll(()=>{x!=="live"||A||!n.isNearScrollbackTop()||(A=!0,S({type:"load_history",before:V}))});let B=!1,K=!1,h=null,g=null;const U=document.createElement("div");U.className="terminal-copy-banner",U.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',U.hidden=!0,a.appendChild(U);const T=document.createElement("div");T.className="terminal-copy-banner terminal-copy-toast",T.textContent="Copied",T.hidden=!0,a.appendChild(T);function ft(t,e){const i=a.querySelector(".xterm-screen, canvas"),c=(i&&i.getBoundingClientRect().width>0?i:a).getBoundingClientRect(),b=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((e-c.top)/b))),col:Math.max(0,Math.min(n.cols-1,Math.floor((t-c.left)/v)))}}async function $(t){if(!t)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(t);return}}catch{}const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.opacity="0",e.style.pointerEvents="none",document.body.appendChild(e),e.focus(),e.select();try{document.execCommand("copy")}catch{}e.remove()}let X;function Q(){T.hidden=!1,X&&clearTimeout(X),X=setTimeout(()=>{T.hidden=!0},1e3)}function Y(t){B=t,U.hidden=!t,a.classList.toggle("terminal-copy-mode-active",t),t&&n.focus()}function dt(){Y(!B)}function Dt(t){E||t.button!==0||K||(B||t.shiftKey)&&(t.preventDefault(),t.stopPropagation(),K=!0,h=ft(t.clientX,t.clientY),g=h,n.selectCells(h.col,h.row,g.col,g.row),document.addEventListener("mousemove",ot,!0),document.addEventListener("mouseup",it,!0))}function ot(t){E||!K||!h||(t.preventDefault(),t.stopPropagation(),g=ft(t.clientX,t.clientY),n.selectCells(h.col,h.row,g.col,g.row))}function it(t){if(E||!K||!h||!g)return;t.preventDefault(),t.stopPropagation(),document.removeEventListener("mousemove",ot,!0),document.removeEventListener("mouseup",it,!0),K=!1;const e=n.getCellsText(h.col,h.row,g.col,g.row);h=null,g=null,n.clearSelection(),Y(!1),e&&(T.textContent="\u5DF2\u590D\u5236",$(e).then(Q)),n.focus()}a.addEventListener("mousedown",Dt,!0),document.addEventListener("keydown",Pt,!0);function Pt(t){if(!(E||!n.isFocused()||t.defaultPrevented||t.isComposing)){if(B){if(t.key==="Escape"){t.preventDefault(),t.stopPropagation(),h=null,g=null,n.clearSelection(),Y(!1);return}if(t.key==="Enter"||(t.ctrlKey||t.metaKey)&&t.code==="KeyC"){t.preventDefault(),t.stopPropagation(),$(n.getScreenText()).then(Q),Y(!1);return}}if(t.altKey&&t.code==="KeyC"&&!t.ctrlKey&&!t.metaKey){if(t.preventDefault(),t.stopPropagation(),h&&g){const e=n.getCellsText(h.col,h.row,g.col,g.row);h=null,g=null,n.clearSelection(),Y(!1),e&&(T.textContent="Copied",$(e).then(Q))}else{const e=n.getSelection();e?(n.clearSelection(),T.textContent="Copied",$(e).then(Q)):dt()}return}if((t.ctrlKey||t.metaKey)&&t.code==="KeyV"){t.preventDefault(),t.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){const e=await navigator.clipboard.read();for(const i of e)for(const u of i.types)if(ct(u)){const c=await i.getType(u);await nt(c);return}}}catch{}try{const e=await navigator.clipboard?.readText();e&&n.pasteText(mt(e))}catch{}})();return}}}let Z=0;function mt(t){return t.split(`\r
6
6
  `).join(`
7
7
  `).split(`
8
- `).join("\r")}async function kt(t){const e=Ct(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();const c=Date.now();if(c-Z<50)return;Z=c,await nt(e);return}const i=t.clipboardData?.getData("text/plain");if(!i)return;t.preventDefault(),t.stopPropagation();const u=Date.now();u-Z<50||(Z=u,n.pasteText(dt(i)))}let O=0;function It(t){t.preventDefault(),O++,ct(t.dataTransfer)&&a.classList.add("terminal-drag-over")}function Yt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function Ft(t){t.preventDefault(),O--,O<=0&&(O=0,a.classList.remove("terminal-drag-over"))}async function At(t){t.preventDefault(),O=0,a.classList.remove("terminal-drag-over");const e=ct(t.dataTransfer);e&&await nt(e)}function mt(t){!Tt||t.key!=="Escape"||B||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||n.isFocused()&&(t.preventDefault(),t.stopPropagation(),I("\x1B"))}a.addEventListener("paste",kt),a.addEventListener("dragenter",It),a.addEventListener("dragover",Yt),a.addEventListener("dragleave",Ft),a.addEventListener("drop",At),document.addEventListener("keydown",mt,!0);function zt(t){if(t.touches.length!==1){s=null;return}const e=t.touches[0];if(!e)return;C();const i=a.getBoundingClientRect(),u=e.clientX-i.left<i.width*.4?"tui":"history",c=e.clientY-i.top<i.height*.5;s={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:u,tuiUp:c,sent:!1},t.stopPropagation()}function Bt(t){if(!s||t.touches.length!==1)return;const e=t.touches[0];if(!e)return;const i=e.clientY-s.startY,u=e.clientX-s.startX;if(!s.scrolling){if(Math.abs(i)<8||Math.abs(i)<Math.abs(u))return;s.scrolling=!0,j=Date.now()+500}t.preventDefault(),t.stopPropagation();const c=performance.now(),b=e.clientY-s.lastY;if(s.zone==="tui"){s.sent||(s.sent=!0,I(s.tuiUp?"\x1B[5~":"\x1B[6~"),F(s.tuiUp)),s.lastX=e.clientX,s.lastY=e.clientY,s.lastT=c;return}const v=c-s.lastT;v>0&&(s.velocity=b/v),Ot(-b),s.lastX=e.clientX,s.lastY=e.clientY,s.lastT=c}function Kt(t){if(!s)return;const e=s.scrolling,i=s.zone,u=s.tuiUp,c=s.velocity;if(s=null,t.stopPropagation(),i==="tui"){e||(I(u?"\x1B[5~":"\x1B[6~"),F(u),j=Date.now()+500,t.preventDefault());return}if(!e){n.focus();return}j=Date.now()+500,t.preventDefault(),c&&gt(c)}function Ut(t){s=null,C(),t.stopPropagation()}function Xt(t){t.pointerType==="touch"&&Date.now()<j&&(t.preventDefault(),t.stopPropagation())}function Ot(t){const e=a.querySelector(".xterm-rows > div"),i=e?e.getBoundingClientRect().height:16;let u=i>0?Math.round(t/i):t>0?1:-1;u===0&&(u=t>0?1:-1),n.scrollLines(u)}a.addEventListener("touchstart",zt,{passive:!0,capture:!0}),a.addEventListener("touchmove",Bt,{passive:!1,capture:!0}),a.addEventListener("touchend",Kt,{passive:!1,capture:!0}),a.addEventListener("touchcancel",Ut,{passive:!0,capture:!0}),a.addEventListener("pointerup",Xt,!0);function pt(){y()}window.addEventListener("resize",pt),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const ht=new ResizeObserver(()=>y());ht.observe(a),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),a.addEventListener("focusin",Mt,!0);async function Rt(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(t=>setTimeout(t,1e3))])}catch{}}return requestAnimationFrame(()=>{D()&&G(),(async()=>(await Rt(),D()&&S({type:"resize",cols:n.cols,rows:n.rows}),at(),y()))()}),{destroy(){if(E=!0,C(),Y(!1),X&&clearTimeout(X),document.removeEventListener("mousemove",ot,!0),document.removeEventListener("mouseup",it,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}a.innerHTML="",a.classList.remove("terminal-pending"),window.removeEventListener("resize",pt),ht.disconnect(),document.removeEventListener("keydown",mt,!0)},focus(){n.focus()},sendInput(t){I(t)},toggleCopyMode(){ft()}}})()}export{qt as initTerminal,rt as stripAltScreenSequences};
8
+ `).join("\r")}async function kt(t){const e=Ct(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();const c=Date.now();if(c-Z<50)return;Z=c,await nt(e);return}const i=t.clipboardData?.getData("text/plain");if(!i)return;t.preventDefault(),t.stopPropagation();const u=Date.now();u-Z<50||(Z=u,n.pasteText(mt(i)))}let O=0;function It(t){t.preventDefault(),O++,ut(t.dataTransfer)&&a.classList.add("terminal-drag-over")}function Yt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function Ft(t){t.preventDefault(),O--,O<=0&&(O=0,a.classList.remove("terminal-drag-over"))}async function At(t){t.preventDefault(),O=0,a.classList.remove("terminal-drag-over");const e=ut(t.dataTransfer);e&&await nt(e)}function pt(t){!Tt||t.key!=="Escape"||B||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||n.isFocused()&&(t.preventDefault(),t.stopPropagation(),I("\x1B"))}a.addEventListener("paste",kt),a.addEventListener("dragenter",It),a.addEventListener("dragover",Yt),a.addEventListener("dragleave",Ft),a.addEventListener("drop",At),document.addEventListener("keydown",pt,!0);function zt(t){if(t.touches.length!==1){s=null;return}const e=t.touches[0];if(!e)return;C();const i=a.getBoundingClientRect(),u=e.clientX-i.left<i.width*.4?"tui":"history",c=e.clientY-i.top<i.height*.5;s={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,lastT:performance.now(),velocity:0,scrolling:!1,zone:u,tuiUp:c,sent:!1},t.stopPropagation()}function Bt(t){if(!s||t.touches.length!==1)return;const e=t.touches[0];if(!e)return;const i=e.clientY-s.startY,u=e.clientX-s.startX;if(!s.scrolling){if(Math.abs(i)<8||Math.abs(i)<Math.abs(u))return;s.scrolling=!0,j=Date.now()+500}t.preventDefault(),t.stopPropagation();const c=performance.now(),b=e.clientY-s.lastY;if(s.zone==="tui"){s.sent||(s.sent=!0,I(s.tuiUp?"\x1B[5~":"\x1B[6~"),F(s.tuiUp)),s.lastX=e.clientX,s.lastY=e.clientY,s.lastT=c;return}const v=c-s.lastT;v>0&&(s.velocity=b/v),Ot(-b),s.lastX=e.clientX,s.lastY=e.clientY,s.lastT=c}function Kt(t){if(!s)return;const e=s.scrolling,i=s.zone,u=s.tuiUp,c=s.velocity;if(s=null,t.stopPropagation(),i==="tui"){e||(I(u?"\x1B[5~":"\x1B[6~"),F(u),j=Date.now()+500,t.preventDefault());return}if(!e){n.focus();return}j=Date.now()+500,t.preventDefault(),c&&wt(c)}function Ut(t){s=null,C(),t.stopPropagation()}function Xt(t){t.pointerType==="touch"&&Date.now()<j&&(t.preventDefault(),t.stopPropagation())}function Ot(t){const e=a.querySelector(".xterm-rows > div"),i=e?e.getBoundingClientRect().height:16;let u=i>0?Math.round(t/i):t>0?1:-1;u===0&&(u=t>0?1:-1),n.scrollLines(u)}a.addEventListener("touchstart",zt,{passive:!0,capture:!0}),a.addEventListener("touchmove",Bt,{passive:!1,capture:!0}),a.addEventListener("touchend",Kt,{passive:!1,capture:!0}),a.addEventListener("touchcancel",Ut,{passive:!0,capture:!0}),a.addEventListener("pointerup",Xt,!0);function ht(){y()}window.addEventListener("resize",ht),window.visualViewport?.addEventListener("resize",y),window.visualViewport?.addEventListener("scroll",y);const gt=new ResizeObserver(()=>y());gt.observe(a),document.fonts?.ready.then(()=>{y(),setTimeout(y,50),setTimeout(y,200)}),a.addEventListener("focusin",Mt,!0);async function Rt(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(t=>setTimeout(t,1e3))])}catch{}}return requestAnimationFrame(()=>{D()&&G(),(async()=>(await Rt(),D()&&S({type:"resize",cols:n.cols,rows:n.rows}),lt(),y()))()}),{destroy(){if(E=!0,C(),Y(!1),X&&clearTimeout(X),document.removeEventListener("mousemove",ot,!0),document.removeEventListener("mouseup",it,!0),l){try{l.close(1e3,"session switch")}catch{}l=void 0}a.innerHTML="",a.classList.remove("terminal-pending"),window.removeEventListener("resize",ht),gt.disconnect(),document.removeEventListener("keydown",pt,!0)},focus(){n.focus()},sendInput(t){I(t)},toggleCopyMode(){dt()}}})()}export{jt as initTerminal,rt as stripAltScreenSequences};
@@ -1,8 +1,8 @@
1
- import{existsSync as P,readdirSync as le,statSync as A,unlinkSync as me,writeFileSync as fe,mkdirSync as pe,readFileSync as G}from"node:fs";import{readFile as he}from"node:fs/promises";import{execSync as _,execFileSync as L}from"node:child_process";import{tmuxEnv as ge}from"./tmux-env.js";import h from"node:path";import{fileURLToPath as ye}from"node:url";import{Hono as je}from"hono";import{listSessions as D}from"../sessions.js";import{renderLoginPage as we,renderNotesIndex as ve,renderNotesPage as ke,renderSettings as qe,renderThemeSettings as Se,renderHistoryIndex as Te,renderQuickCommandsPage as Ae,renderFilesIndex as be,renderShell as xe}from"../frontend.js";import{renderFederationPage as _e}from"./pages/federation.js";import{createAgentToken as Ce,removeAgentToken as Ie,listAgentTokens as z}from"./agent-tokens.js";import{hashPassword as Ee,verifyPassword as Oe,validatePassword as Ne}from"./auth.js";import{atomicWriteFileSync as Re}from"./atomicWrite.js";import{resolveFsPath as b,resolveFsRoots as B,MAX_FILE_BYTES as V,walkRecursive as Pe}from"./fs-access.js";import{captureSessionWindowsWithPath as Le}from"./tmux-windows.js";import{audit as v}from"./auditLog.js";import{db as S}from"./db.js";import{getSessionAccessMap as De}from"./session-access.js";import{listWindowHistory as Fe,clearWindowHistory as He}from"./window-history.js";import{saveSecurityConfig as Me}from"./security-config.js";import{readSettings as Q,writeSettings as Ue}from"./settings.js";import{setActiveThemeTemplate as K}from"./theme-store.js";import{isThemeTemplateId as X,THEME_TEMPLATE_IDS as We}from"./themes/index.js";import{pinView as $e,unpinView as Ge,listPinnedViews as ze}from"./pinned-views.js";import{getStoredWindows as Be}from"./session-windows.js";import{buildSidebarSessions as Ve}from"./sessions-sidebar.js";import{upsertSessionState as Z,removeSessionState as Y,renameSessionState as J,computeTombstones as Qe,getSessionState as Ke}from"./session-state.js";import{createQuickCommand as Xe,deleteQuickCommand as Ze,listQuickCommands as ee,updateQuickCommand as Ye}from"./quick-commands.js";import{getSystemStatus as Je,getTopProcesses as et,killProcess as tt}from"./system-monitor.js";import{newTmuxSession as te,renameSession as rt,killSession as nt,sessionExists as st,TmuxWindowsError as re}from"./tmux-windows.js";import{registerAgentTunnel as ot}from"./agent-tunnel.js";import{injectScopeScript as it}from"./scope-script.js";const F="tmux-web-token",at=365;function dt(x){return x==="127.0.0.1"||x==="::1"||x==="::ffff:127.0.0.1"}function $t(x){const{mode:ne,securityConfig:g,tokenStore:j,rateLimiter:I,settings:ut,terminalBufferConfig:E,getAgentId:H,state:l,federation:k,hub:w}=x,q=ne==="agent";function C(e){if(g.security.trustProxy){const t=(e.req.header("x-forwarded-for")||"").split(",")[0].trim();if(t)return t}return e.env?.incoming?.socket?.remoteAddress||"unknown"}function O(e){const t=e.req.header("authorization");if(t?.startsWith("Bearer "))return t.slice(7);const r=e.req.header("cookie");if(!r)return null;const n=r.match(new RegExp(`(?:^|;\\s*)${F}=([^;]+)`));return n?decodeURIComponent(n[1]):null}function ct(e){const t=O(e);return t?j.validateToken(t):null}function M(e,t){const r=e.req.header("x-forwarded-proto")==="https"||e.req.url.startsWith("https:");e.header("Set-Cookie",`${F}=${encodeURIComponent(t)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${at*86400}${r?"; Secure":""}`)}function se(e){e.header("Set-Cookie",`${F}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`)}const o=()=>q?async(e,t)=>t():async(e,t)=>{const r=O(e);return!r||!j.validateToken(r)?(v("http_unauthorized",{ip:C(e)}),e.json({error:"unauthorized"},401)):t()},y=()=>q?async(e,t)=>t():async(e,t)=>{const r=O(e);if(!r||!j.validateToken(r)){const n=encodeURIComponent(e.req.url);return e.redirect(`/login?returnTo=${n}`,302)}return t()};function N(e){const t=D(),r=new Set(t.map(c=>c.name)),n=Ve(t,De(),ze()),a=[],i=new Map,u=new Map;if(w)for(const c of w.agents.list()){const d=w.agents.get(c.agentId);i.set(c.agentId,new Set((d?d.sessions:[]).map(m=>m.name))),u.set(c.agentId,c.online),a.push({agentId:c.agentId,agentName:c.name,online:c.online,sessions:d?d.sessions:[]})}const p=Qe(r,i,u);return{...n,currentSession:e??null,agents:a,tombstones:p}}function U(e){const t=typeof e.sessionName=="string"?e.sessionName.trim():"";if(!t)return{error:"sessionName is required"};if(e.windowIndex===void 0)return{sessionName:t};const r=e.windowIndex;return typeof r!="number"||!Number.isInteger(r)||r<0?{error:"windowIndex must be a non-negative integer"}:{sessionName:t,windowIndex:r}}const s=new je;s.use("*",async(e,t)=>{await t(),e.header("X-Content-Type-Options","nosniff"),e.header("X-Frame-Options","DENY"),e.header("Content-Security-Policy",["default-src 'self'","script-src 'self' 'unsafe-inline'","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com","connect-src 'self' ws: wss: http: https:","img-src 'self' data:","font-src 'self' https://fonts.gstatic.com","worker-src 'self' blob:"].join("; ")),e.header("Referrer-Policy","strict-origin-when-cross-origin")}),s.use("*",async(e,t)=>{const r=e.req.method;if(r==="GET"||r==="HEAD"||r==="OPTIONS")return t();const n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.text("cross-site request blocked",403):t()}),q||s.use("*",async(e,t)=>{if(await t(),e.req.method!=="GET"||e.req.path==="/login"||!(e.res.headers.get("content-type")??"").includes("text/html"))return;const n=await e.res.text();e.res=new Response(it(n,{prefix:"",agentId:null}),e.res)});const W=h.dirname(ye(import.meta.url)),oe=[h.join(W,"..","assets"),h.join(W,"assets"),h.join(process.cwd(),"dist","assets")];s.get("/assets/:file",async e=>{const t=e.req.param("file");if(!/^[a-zA-Z0-9._-]+$/.test(t))return e.notFound();for(const r of oe){const n=h.join(r,t);if(!P(n))continue;const a=await he(n),i=h.extname(t),u={".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".map":"application/json; charset=utf-8"};return e.body(a,200,{"Content-Type":u[i]??"application/octet-stream","Cache-Control":i===".js"||i===".css"?"no-cache":"public, max-age=3600"})}return e.notFound()});const ie=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
1
+ import{existsSync as P,readdirSync as me,statSync as T,unlinkSync as fe,writeFileSync as pe,mkdirSync as he,readFileSync as B}from"node:fs";import{readFile as ge}from"node:fs/promises";import{execSync as _,execFileSync as L}from"node:child_process";import{tmuxEnv as ye}from"./tmux-env.js";import h from"node:path";import{fileURLToPath as je}from"node:url";import{Hono as we}from"hono";import{listSessions as D}from"../sessions.js";import{renderLoginPage as ve,renderNotesIndex as ke,renderNotesPage as qe,renderSettings as Se,renderThemeSettings as be,renderHistoryIndex as Te,renderQuickCommandsPage as Ae,renderFilesIndex as xe,renderShell as _e}from"../frontend.js";import{renderFederationPage as Ce}from"./pages/federation.js";import{createAgentToken as Ie,removeAgentToken as Ee,listAgentTokens as G}from"./agent-tokens.js";import{hashPassword as Oe,verifyPassword as Ne,validatePassword as Re}from"./auth.js";import{atomicWriteFileSync as Pe}from"./atomicWrite.js";import{resolveFsPath as A,resolveFsRoots as z,MAX_FILE_BYTES as V,walkRecursive as Le,fsEntryCmp as Q}from"./fs-access.js";import{captureSessionWindowsWithPath as De}from"./tmux-windows.js";import{audit as v}from"./auditLog.js";import{db as S}from"./db.js";import{getSessionAccessMap as Fe}from"./session-access.js";import{listWindowHistory as He,clearWindowHistory as Me}from"./window-history.js";import{saveSecurityConfig as Ue}from"./security-config.js";import{ImageUploadError as We,saveUploadedImage as $e}from"./image-upload.js";import{readSettings as K,writeSettings as Be}from"./settings.js";import{setActiveThemeTemplate as X}from"./theme-store.js";import{isThemeTemplateId as Z,THEME_TEMPLATE_IDS as Ge}from"./themes/index.js";import{pinView as ze,unpinView as Ve,listPinnedViews as Qe}from"./pinned-views.js";import{getStoredWindows as Ke}from"./session-windows.js";import{buildSidebarSessions as Xe}from"./sessions-sidebar.js";import{upsertSessionState as Y,removeSessionState as J,renameSessionState as ee,computeTombstones as Ze,getSessionState as Ye}from"./session-state.js";import{createQuickCommand as Je,deleteQuickCommand as et,listQuickCommands as te,updateQuickCommand as tt}from"./quick-commands.js";import{getSystemStatus as rt,getTopProcesses as nt,killProcess as st}from"./system-monitor.js";import{newTmuxSession as re,renameSession as ot,killSession as it,sessionExists as at,TmuxWindowsError as ne}from"./tmux-windows.js";import{registerAgentTunnel as dt}from"./agent-tunnel.js";import{injectScopeScript as ut}from"./scope-script.js";const F="tmux-web-token",ct=365;function lt(x){return x==="127.0.0.1"||x==="::1"||x==="::ffff:127.0.0.1"}function Vt(x){const{mode:se,securityConfig:g,tokenStore:j,rateLimiter:I,settings:mt,terminalBufferConfig:E,getAgentId:H,state:l,federation:k,hub:w}=x,q=se==="agent";function C(e){if(g.security.trustProxy){const t=(e.req.header("x-forwarded-for")||"").split(",")[0].trim();if(t)return t}return e.env?.incoming?.socket?.remoteAddress||"unknown"}function O(e){const t=e.req.header("authorization");if(t?.startsWith("Bearer "))return t.slice(7);const r=e.req.header("cookie");if(!r)return null;const n=r.match(new RegExp(`(?:^|;\\s*)${F}=([^;]+)`));return n?decodeURIComponent(n[1]):null}function ft(e){const t=O(e);return t?j.validateToken(t):null}function M(e,t){const r=e.req.header("x-forwarded-proto")==="https"||e.req.url.startsWith("https:");e.header("Set-Cookie",`${F}=${encodeURIComponent(t)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ct*86400}${r?"; Secure":""}`)}function oe(e){e.header("Set-Cookie",`${F}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`)}const o=()=>q?async(e,t)=>t():async(e,t)=>{const r=O(e);return!r||!j.validateToken(r)?(v("http_unauthorized",{ip:C(e)}),e.json({error:"unauthorized"},401)):t()},y=()=>q?async(e,t)=>t():async(e,t)=>{const r=O(e);if(!r||!j.validateToken(r)){const n=encodeURIComponent(e.req.url);return e.redirect(`/login?returnTo=${n}`,302)}return t()};function N(e){const t=D(),r=new Set(t.map(c=>c.name)),n=Xe(t,Fe(),Qe()),a=[],i=new Map,u=new Map;if(w)for(const c of w.agents.list()){const d=w.agents.get(c.agentId);i.set(c.agentId,new Set((d?d.sessions:[]).map(m=>m.name))),u.set(c.agentId,c.online),a.push({agentId:c.agentId,agentName:c.name,online:c.online,sessions:d?d.sessions:[]})}const p=Ze(r,i,u);return{...n,currentSession:e??null,agents:a,tombstones:p}}function U(e){const t=typeof e.sessionName=="string"?e.sessionName.trim():"";if(!t)return{error:"sessionName is required"};if(e.windowIndex===void 0)return{sessionName:t};const r=e.windowIndex;return typeof r!="number"||!Number.isInteger(r)||r<0?{error:"windowIndex must be a non-negative integer"}:{sessionName:t,windowIndex:r}}const s=new we;s.use("*",async(e,t)=>{await t(),e.header("X-Content-Type-Options","nosniff"),e.header("X-Frame-Options","DENY"),e.header("Content-Security-Policy",["default-src 'self'","script-src 'self' 'unsafe-inline'","style-src 'self' 'unsafe-inline' https://fonts.googleapis.com","connect-src 'self' ws: wss: http: https:","img-src 'self' data:","font-src 'self' https://fonts.gstatic.com","worker-src 'self' blob:"].join("; ")),e.header("Referrer-Policy","strict-origin-when-cross-origin")}),s.use("*",async(e,t)=>{const r=e.req.method;if(r==="GET"||r==="HEAD"||r==="OPTIONS")return t();const n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.text("cross-site request blocked",403):t()}),q||s.use("*",async(e,t)=>{if(await t(),e.req.method!=="GET"||e.req.path==="/login"||!(e.res.headers.get("content-type")??"").includes("text/html"))return;const n=await e.res.text();e.res=new Response(ut(n,{prefix:"",agentId:null}),e.res)});const W=h.dirname(je(import.meta.url)),ie=[h.join(W,"..","assets"),h.join(W,"assets"),h.join(process.cwd(),"dist","assets")];s.get("/assets/:file",async e=>{const t=e.req.param("file");if(!/^[a-zA-Z0-9._-]+$/.test(t))return e.notFound();for(const r of ie){const n=h.join(r,t);if(!P(n))continue;const a=await ge(n),i=h.extname(t),u={".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".map":"application/json; charset=utf-8"};return e.body(a,200,{"Content-Type":u[i]??"application/octet-stream","Cache-Control":i===".js"||i===".css"?"no-cache":"public, max-age=3600"})}return e.notFound()});const ae=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
2
  <rect x="2.5" y="5.5" width="27" height="21" rx="4" fill="#0d1117" stroke="#7dd3fc" stroke-width="2"/>
3
3
  <path d="M8 13l4.2 3.1L8 19.2" fill="none" stroke="#7dd3fc" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
4
4
  <line x1="15.5" y1="19.6" x2="22.5" y2="19.6" stroke="#7dd3fc" stroke-width="2.2" stroke-linecap="round"/>
5
- </svg>`,$=e=>e.body(ie,200,{"Content-Type":"image/svg+xml; charset=utf-8","Cache-Control":"public, max-age=86400"});s.get("/favicon.svg",$),s.get("/favicon.ico",$);const ae={name:"tmux-weblink",short_name:"tmux-web",description:"Access your tmux sessions from the browser",start_url:"/",display:"standalone",background_color:"#0d1117",theme_color:"#0d1117",icons:[{src:"/assets/icon-192.png",sizes:"192x192",type:"image/png"},{src:"/assets/icon-512.png",sizes:"512x512",type:"image/png"}]};s.get("/manifest.json",e=>e.json(ae,200,{"Cache-Control":"public, max-age=3600"}));const de=`// tmux-weblink Service Worker
5
+ </svg>`,$=e=>e.body(ae,200,{"Content-Type":"image/svg+xml; charset=utf-8","Cache-Control":"public, max-age=86400"});s.get("/favicon.svg",$),s.get("/favicon.ico",$);const de={name:"tmux-weblink",short_name:"tmux-web",description:"Access your tmux sessions from the browser",start_url:"/",display:"standalone",background_color:"#0d1117",theme_color:"#0d1117",icons:[{src:"/assets/icon-192.png",sizes:"192x192",type:"image/png"},{src:"/assets/icon-512.png",sizes:"512x512",type:"image/png"}]};s.get("/manifest.json",e=>e.json(de,200,{"Cache-Control":"public, max-age=3600"}));const ue=`// tmux-weblink Service Worker
6
6
  const CACHE = "tmux-weblink-v1";
7
7
  const ASSETS = ["/", "/favicon.svg"];
8
8
  self.addEventListener("install", (e) => {
@@ -13,7 +13,7 @@ self.addEventListener("activate", (e) => { e.waitUntil(clients.claim()); });
13
13
  self.addEventListener("fetch", (e) => {
14
14
  e.respondWith(fetch(e.request).catch(() => caches.match(e.request)));
15
15
  });
16
- `;s.get("/sw.js",e=>e.body(de,200,{"Content-Type":"application/javascript; charset=utf-8"})),s.get("/login",e=>{const r=e.req.query("setup")==="1"?!0:!g.passwordHash,n=e.req.query("error");return e.html(we({setupMode:r,error:n?decodeURIComponent(n):void 0,theme:l.activeTheme}))}),s.get("/",y(),e=>{const t=B();return e.html(xe({theme:l.activeTheme,fsRoots:t,terminalCfg:E,scrollback:E.initialLines+2*E.historyChunk,wsBase:q&&H?.()?`/ws/a/${H()}`:void 0}))}),s.get("/notes",y(),e=>e.html(ve(S.data.notes,l.activeTheme))),s.get("/notes/:session",y(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.html(ke(t,l.activeTheme))}),s.get("/history",y(),e=>{const t=D(),r=new Set(t.map(n=>n.name));return e.html(Te(Fe(),l.activeTheme,r))}),s.get("/quick-commands",y(),e=>e.html(Ae(ee(),l.activeTheme))),s.get("/files",y(),e=>{const t=B();return e.html(be(l.activeTheme,t))}),s.post("/api/history/clear",o(),async e=>(await He(),e.json({ok:!0}))),s.get("/api/quick-commands",o(),e=>e.json(ee())),s.post("/api/quick-commands",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Xe(t);return"error"in r?e.json({error:r.error},400):e.json(r,201)}),s.patch("/api/quick-commands/:id",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Ye(e.req.param("id"),t);return"error"in r?e.json({error:r.error},r.status):e.json(r)}),s.delete("/api/quick-commands/:id",o(),e=>Ze(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),s.post("/api/auth/password",async e=>{if(q)return e.json({error:"hub only"},403);const t=C(e),r=I.check(t);if(!r.allowed)return v("rate_limited",{ip:t,retryAfterMs:r.retryAfterMs,permanentLock:r.permanentLock}),r.permanentLock?e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403):e.json({error:"Too many attempts",retryAfterMs:r.retryAfterMs},429);let n;try{n=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const a=typeof n.password=="string"?n.password:"";if(!g.passwordHash){if(!g.security.allowRemoteSetup&&!dt(t))return v("setup_rejected_remote",{ip:t}),e.json({error:"First-run setup must be performed from localhost"},403);if(l.settingUpPassword)return e.json({error:"Password setup in progress"},409);const c=Ne(a);if(c)return e.json({error:c},400);l.settingUpPassword=!0;try{g.passwordHash=await Ee(a),Me(g),v("password_set",{ip:t})}finally{l.settingUpPassword=!1}const{plaintext:d}=j.createAccessToken("setup",g.security.tokenTtlDays);return M(e,d),e.json({ok:!0,token:d,setupMode:!0})}if(!await Oe(a,g.passwordHash)){const c=I.recordFailure(t);return v("auth_failed",{ip:t,method:"password",failures:c.failures,permanentLock:c.permanentLock}),c.permanentLock?(j.revokeAll(),v("permanent_lock",{ip:t,failures:c.failures}),e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403)):c.allowed?e.json({error:"Incorrect password"},401):e.json({error:"Too many attempts",retryAfterMs:c.retryAfterMs},429)}I.recordSuccess(t);const u=`browser-${t}`,{plaintext:p}=j.createAccessToken(u,g.security.tokenTtlDays);return M(e,p),v("auth_success",{ip:t,method:"password",tokenName:u}),e.json({ok:!0,token:p})}),s.post("/api/auth/token",o(),async e=>{if(q)return e.json({error:"hub only"},403);const t=C(e);let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const n=typeof r.name=="string"?r.name:`api-${t}`,a=typeof r.ttlDays=="number"?r.ttlDays:g.security.tokenTtlDays,{stored:i,plaintext:u}=j.createAccessToken(n,a);return v("token_created",{ip:t,name:i.name,tokenId:i.id}),e.json({id:i.id,name:i.name,token:u,expiresAt:i.expiresAt})}),s.get("/api/auth/tokens",o(),e=>q?e.json({error:"hub only"},403):e.json(j.list().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,lastUsedAt:t.lastUsedAt,expiresAt:t.expiresAt})))),s.delete("/api/auth/tokens/:id",o(),e=>q?e.json({error:"hub only"},403):j.revoke(e.req.param("id"))?(v("token_revoked",{ip:C(e),tokenId:e.req.param("id")}),e.json({ok:!0})):e.json({error:"not found"},404)),s.post("/api/auth/logout",o(),e=>(se(e),e.json({ok:!0}))),s.get("/settings",y(),async e=>{const t=await Q();return e.html(qe({settings:t,theme:l.activeTheme,saved:e.req.query("saved")==="1",error:e.req.query("error")?decodeURIComponent(e.req.query("error")):void 0}))}),s.post("/settings",o(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=await Q(),n=t.defaultView==="recent"?"recent":"default";return await Ue({...r,defaultView:n}),e.redirect("/settings?saved=1",303)}),s.get("/settings/theme",y(),e=>e.html(Se({theme:l.activeTheme,saved:e.req.query("saved")==="1"}))),s.post("/settings/theme",o(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings/theme?error=1",303)}const r=t.template;return typeof r!="string"||!X(r)?e.redirect("/settings/theme",303):(l.activeTheme=await K(r),e.redirect("/settings/theme?saved=1",303))});const ue={vscode:"VS Code",ghostty:"Ghostty","warm-clay":"Warm Clay","dark-cove":"Dark Cove"};s.get("/api/theme",o(),e=>e.json({active:l.activeTheme.template,templates:We.map(t=>({id:t,name:ue[t]}))})),s.post("/api/theme",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=t.template;return typeof r!="string"||!X(r)?e.json({error:"invalid theme template"},400):(l.activeTheme=await K(r),e.json({ok:!0,active:l.activeTheme.template}))}),s.get("/api/system/status",o(),e=>e.json(Je())),s.get("/api/system/processes",o(),e=>e.json(et())),k&&(s.get("/settings/federation",y(),e=>e.redirect("/settings/machines",301)),s.get("/settings/machines",y(),async e=>e.html(_e({config:k.getConfig(),status:k.status(),tokens:z().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt})),hostname:k.hostname},l.activeTheme))),s.get("/api/machines",o(),e=>e.json({config:k.getConfig(),status:k.status()})),s.post("/api/machines",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.hub=="string"?t.hub.trim():"",n=typeof t.token=="string"?t.token.trim():"",a=typeof t.name=="string"?t.name.trim().slice(0,64):"",i=t.enabled===!0;if(i){if(!r)return e.json({error:"hub URL is required"},400);if(!/^wss?:\/\//i.test(r))return e.json({error:"hub URL must start with ws:// or wss://"},400);if(!n)return e.json({error:"registration token is required"},400)}const u={hub:r||void 0,token:n||void 0,name:a||void 0,enabled:i};return await k.setConfig(u),e.json({ok:!0,status:k.status()})}),s.get("/api/agent-tokens",o(),e=>e.json(z().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt})))),s.post("/api/agent-tokens",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim().slice(0,64):"";if(!r)return e.json({error:"name is required"},400);const{id:n,token:a}=Ce(r);return e.json({ok:!0,id:n,name:r,token:a})}),s.delete("/api/agent-tokens/:id",o(),e=>{const t=e.req.param("id");return Ie(t)?e.json({ok:!0}):e.json({error:"no such token"},404)})),s.post("/api/system/kill",o(),async e=>{const{pid:t}=await e.req.json();if(typeof t!="number"||!Number.isInteger(t)||t<=0)return e.json({error:"invalid pid"},400);const r=tt(t);return r.ok?e.json({ok:!0}):e.json({error:r.error},500)}),s.get("/api/agents",o(),e=>w?e.json(w.agents.list()):e.json([])),s.post("/api/sessions/new",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name is required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(r))return e.json({error:"name contains invalid characters"},400);const n=typeof t.dir=="string"&&t.dir.trim()?t.dir.trim():void 0;if(D().some(i=>i.name===r))return e.json({error:"session already exists"},409);try{return te(r,n),Z(r,{path:n}),e.json({ok:!0})}catch(i){const u=i instanceof re?i.message:"failed to create session";return e.json({error:u},500)}}),s.post("/api/sessions/rename",o(),async e=>{try{const{oldName:t,newName:r}=await e.req.json();return!t||!r?e.json({error:"oldName and newName required"},400):/^[a-zA-Z0-9_\-. ]+$/.test(r)?(rt(t,r),J(t,r),e.json({ok:!0})):e.json({error:"invalid characters in name"},400)}catch{return e.json({error:"rename failed"},500)}}),s.post("/api/sessions/kill",o(),async e=>{try{const{name:t}=await e.req.json();return t?(nt(t),Y(t),e.json({ok:!0})):e.json({error:"name required"},400)}catch{return e.json({error:"kill failed"},500)}}),s.post("/api/sessions/rebuild",o(),async e=>{try{const t=await e.req.json(),r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name required"},400);const n=typeof t.agentId=="string"?t.agentId:void 0,i=Ke().find(u=>u.name===r&&(u.agentId??null)===(n??null))?.path;if(n){if(!w)return e.json({error:"not a hub"},400);const u=w.getChannel(n);return u?u.rebuildSession(r,i)?e.json({ok:!0,async:!0}):e.json({error:"agent offline"},409):e.json({error:"agent offline"},409)}if(st(r))return e.json({error:"session already exists"},409);try{te(r,i)}catch(u){const p=u instanceof re?u.message:"rebuild failed";return e.json({error:p},500)}return Z(r,{path:i}),e.json({ok:!0})}catch{return e.json({error:"rebuild failed"},500)}}),s.post("/api/sessions/dismiss",o(),async e=>{try{const t=await e.req.json(),r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name required"},400);const n=typeof t.agentId=="string"?t.agentId:void 0;return Y(r,n),e.json({ok:!0})}catch{return e.json({error:"dismiss failed"},500)}}),s.post("/api/sessions/rename-tombstone",o(),async e=>{try{const t=await e.req.json(),r=typeof t.oldName=="string"?t.oldName.trim():"",n=typeof t.newName=="string"?t.newName.trim():"";if(!r||!n)return e.json({error:"oldName and newName required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(n))return e.json({error:"invalid characters in name"},400);const a=typeof t.agentId=="string"?t.agentId:void 0;return J(r,n,a),e.json({ok:!0})}catch{return e.json({error:"rename failed"},500)}}),s.get("/api/session-program",o(),e=>{const t=e.req.query("session");if(!t)return e.json({program:null});try{const n=L("tmux",["display-message","-p","-t",t,"#{pane_current_command}"],{encoding:"utf-8",timeout:3e3,env:ge()}).trim()||null;return e.json({program:n})}catch{return e.json({program:null})}}),s.get("/api/fs/session-path",o(),e=>{const t=e.req.query("session");if(!t)return e.json({path:process.env.HOME??"/"});try{const r=Le(t),a=r.find(i=>i.active)?.path??r[0]?.path??process.env.HOME??"/";return e.json({path:a})}catch{return e.json({path:process.env.HOME??"/"})}}),s.get("/api/fs/list",o(),e=>{const t=process.env.HOME??"/";let r=e.req.query("path")??t;r.startsWith("~")&&(r=t+r.slice(1)),r.startsWith("/")||(r=h.join(t,r));const n=e.req.query("recursive")==="true";let a=r,i="",u=r.endsWith("/");if(!u)try{u=A(r).isDirectory()}catch{}u||(a=h.dirname(r),i=h.basename(r).toLowerCase());try{const p=le(a),c=[],d=[];for(const m of p)if(!m.startsWith(".")&&!(i&&!m.toLowerCase().startsWith(i))){try{const f=h.join(a,m);A(f).isDirectory()?(c.push(f),n&&Pe(f,c,d,0)):d.push(f)}catch{}if(c.length+d.length>=5e3)break}return e.json({dirs:c,files:d})}catch{return e.json({dirs:[],files:[]})}});function ce(e){const t={repoRoot:null,branch:null,files:[],linesAdded:0,linesRemoved:0};try{const r=_("git rev-parse --show-toplevel 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:3e3}).trim();if(!r)return t;let n;try{n=_("git branch --show-current 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3}).trim()}catch{n=_("git rev-parse --short HEAD 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3}).trim()}n||(n="HEAD");let a="";try{a=_("git status --porcelain 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3})}catch{}const i=[];for(const d of a.split(`
17
- `)){if(!d.trim())continue;const m=d[0],f=d[1],T=d.substring(3).trim(),R=f!==" "?f:m;i.push({path:T,status:R,staged:m,unstaged:f,additions:0,deletions:0})}const u=new Map;try{const d=_("git diff --numstat HEAD 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3});for(const m of d.split(`
18
- `)){if(!m.trim())continue;const f=m.split(" ");if(f.length>=3){const T=parseInt(f[0],10)||0,R=parseInt(f[1],10)||0;u.set(f[2],{added:T,deleted:R})}}}catch{}for(const d of i){const m=u.get(d.path);m&&(d.additions=m.added,d.deletions=m.deleted)}let p=0,c=0;for(const d of i)p+=d.additions,c+=d.deletions;for(const d of i)if(d.status==="?"&&d.additions===0)try{const m=h.join(r,d.path),T=G(m,"utf-8").split(`
19
- `).length;d.additions=T,p+=T}catch{}return{repoRoot:r,branch:n,files:i,linesAdded:p,linesRemoved:c}}catch{return t}}return s.get("/api/git/status",o(),e=>{const t=e.req.query("path");if(!t)return e.json({error:"path is required"},400);try{const r=b(t),n=ce(r);return e.json(n)}catch(r){return r.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):r.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({repoRoot:null,branch:null,files:[],linesAdded:0,linesRemoved:0})}}),s.get("/api/git/diff",o(),e=>{const t=e.req.query("path"),r=e.req.query("file");if(!t||!r)return e.json({error:"path and file are required"},400);try{const n=b(t);if(!P(n)||!A(n).isDirectory())return e.json({error:"invalid repository path: "+n},400);try{const a=L("/usr/bin/git",["diff","HEAD","--",r],{cwd:n,encoding:"utf-8",timeout:5e3}),i=L("/usr/bin/git",["diff","--cached","--",r],{cwd:n,encoding:"utf-8",timeout:5e3});return e.json({diff:a,stagedDiff:i})}catch(a){throw console.error("[git/diff] execFileSync failed:",a?.code,a.message),a}}catch(n){return n.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):n.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):(console.error("[git/diff] exception:",n),e.json({error:String(n)},500))}}),s.get("/api/file",o(),e=>{try{const t=e.req.query("path");if(!t)return e.json({error:"path is required"},400);const r=b(t);if(!A(r).isFile())return e.json({error:"not a file"},400);const n=A(r).size;if(n>V)return e.json({error:"file too large",size:n,maxBytes:V},413);const a=G(r,"utf-8");return e.json({path:r,content:a,size:n})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):t.code==="ENOENT"?e.json({error:"not found"},404):e.json({error:"internal error"},500)}}),s.put("/api/file",o(),async e=>{try{const t=await e.req.json();if(!t.path||typeof t.content!="string")return e.json({error:"path and content are required"},400);const r=b(t.path);return Re(r,t.content),e.json({ok:!0})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"write failed"},500)}}),s.post("/api/file/delete",o(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=b(t.path);return A(r).isFile()?(me(r),e.json({ok:!0})):e.json({error:"not a file"},400)}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"delete failed"},500)}}),s.post("/api/file/touch",o(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=b(t.path);return P(r)?e.json({error:"file already exists"},409):(pe(h.dirname(r),{recursive:!0}),fe(r,"","utf-8"),e.json({ok:!0,path:r}))}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"touch failed"},500)}}),s.get("/api/sidebar/sessions",o(),e=>{const t=e.req.query("currentSession");return e.json(N(typeof t=="string"&&t?t:void 0))}),s.get("/api/sidebar/session-windows/:session",o(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.json(Be(t))}),s.post("/api/pinned-views",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=U(t);return"error"in r?e.json({error:r.error},400):(await $e(r.sessionName,r.windowIndex),e.json(N()))}),s.delete("/api/pinned-views",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=U(t);return"error"in r?e.json({error:r.error},400):(await Ge(r.sessionName,r.windowIndex),e.json(N()))}),s.get("/api/notes",o(),e=>{const t=[...S.data.notes].sort((r,n)=>(n.updatedAt??0)-(r.updatedAt??0));return e.json(t)}),s.get("/api/notes/:scope",o(),e=>{const t=decodeURIComponent(e.req.param("scope")),r=S.data.notes.find(n=>n.scope===t);return r?e.json(r):e.json(null,404)}),s.put("/api/notes/:scope",o(),async e=>{const t=decodeURIComponent(e.req.param("scope"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}if(typeof r.content!="string")return e.json({error:"content must be string"},400);const n={scope:t,content:r.content,updatedAt:Date.now()},a=S.data.notes.findIndex(i=>i.scope===t);return a>=0?S.data.notes[a]=n:S.data.notes.push(n),await S.write(),e.json({ok:!0})}),w&&(s.use("/a/:agentId/*",o()),ot(s,{getChannel:w.getChannel})),s}export{$t as buildApp};
16
+ `;s.get("/sw.js",e=>e.body(ue,200,{"Content-Type":"application/javascript; charset=utf-8"})),s.get("/login",e=>{const r=e.req.query("setup")==="1"?!0:!g.passwordHash,n=e.req.query("error");return e.html(ve({setupMode:r,error:n?decodeURIComponent(n):void 0,theme:l.activeTheme}))}),s.get("/",y(),e=>{const t=z();return e.html(_e({theme:l.activeTheme,fsRoots:t,terminalCfg:E,scrollback:E.initialLines+2*E.historyChunk,wsBase:q&&H?.()?`/ws/a/${H()}`:void 0}))}),s.get("/notes",y(),e=>e.html(ke(S.data.notes,l.activeTheme))),s.get("/notes/:session",y(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.html(qe(t,l.activeTheme))}),s.get("/history",y(),e=>{const t=D(),r=new Set(t.map(n=>n.name));return e.html(Te(He(),l.activeTheme,r))}),s.get("/quick-commands",y(),e=>e.html(Ae(te(),l.activeTheme))),s.get("/files",y(),e=>{const t=z();return e.html(xe(l.activeTheme,t))}),s.post("/api/history/clear",o(),async e=>(await Me(),e.json({ok:!0}))),s.get("/api/quick-commands",o(),e=>e.json(te())),s.post("/api/quick-commands",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Je(t);return"error"in r?e.json({error:r.error},400):e.json(r,201)}),s.patch("/api/quick-commands/:id",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await tt(e.req.param("id"),t);return"error"in r?e.json({error:r.error},r.status):e.json(r)}),s.delete("/api/quick-commands/:id",o(),e=>et(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),s.post("/api/auth/password",async e=>{if(q)return e.json({error:"hub only"},403);const t=C(e),r=I.check(t);if(!r.allowed)return v("rate_limited",{ip:t,retryAfterMs:r.retryAfterMs,permanentLock:r.permanentLock}),r.permanentLock?e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403):e.json({error:"Too many attempts",retryAfterMs:r.retryAfterMs},429);let n;try{n=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const a=typeof n.password=="string"?n.password:"";if(!g.passwordHash){if(!g.security.allowRemoteSetup&&!lt(t))return v("setup_rejected_remote",{ip:t}),e.json({error:"First-run setup must be performed from localhost"},403);if(l.settingUpPassword)return e.json({error:"Password setup in progress"},409);const c=Re(a);if(c)return e.json({error:c},400);l.settingUpPassword=!0;try{g.passwordHash=await Oe(a),Ue(g),v("password_set",{ip:t})}finally{l.settingUpPassword=!1}const{plaintext:d}=j.createAccessToken("setup",g.security.tokenTtlDays);return M(e,d),e.json({ok:!0,token:d,setupMode:!0})}if(!await Ne(a,g.passwordHash)){const c=I.recordFailure(t);return v("auth_failed",{ip:t,method:"password",failures:c.failures,permanentLock:c.permanentLock}),c.permanentLock?(j.revokeAll(),v("permanent_lock",{ip:t,failures:c.failures}),e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403)):c.allowed?e.json({error:"Incorrect password"},401):e.json({error:"Too many attempts",retryAfterMs:c.retryAfterMs},429)}I.recordSuccess(t);const u=`browser-${t}`,{plaintext:p}=j.createAccessToken(u,g.security.tokenTtlDays);return M(e,p),v("auth_success",{ip:t,method:"password",tokenName:u}),e.json({ok:!0,token:p})}),s.post("/api/auth/token",o(),async e=>{if(q)return e.json({error:"hub only"},403);const t=C(e);let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const n=typeof r.name=="string"?r.name:`api-${t}`,a=typeof r.ttlDays=="number"?r.ttlDays:g.security.tokenTtlDays,{stored:i,plaintext:u}=j.createAccessToken(n,a);return v("token_created",{ip:t,name:i.name,tokenId:i.id}),e.json({id:i.id,name:i.name,token:u,expiresAt:i.expiresAt})}),s.get("/api/auth/tokens",o(),e=>q?e.json({error:"hub only"},403):e.json(j.list().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,lastUsedAt:t.lastUsedAt,expiresAt:t.expiresAt})))),s.delete("/api/auth/tokens/:id",o(),e=>q?e.json({error:"hub only"},403):j.revoke(e.req.param("id"))?(v("token_revoked",{ip:C(e),tokenId:e.req.param("id")}),e.json({ok:!0})):e.json({error:"not found"},404)),s.post("/api/auth/logout",o(),e=>(oe(e),e.json({ok:!0}))),s.get("/settings",y(),async e=>{const t=await K();return e.html(Se({settings:t,theme:l.activeTheme,saved:e.req.query("saved")==="1",error:e.req.query("error")?decodeURIComponent(e.req.query("error")):void 0}))}),s.post("/settings",o(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=await K(),n=t.defaultView==="recent"?"recent":"default";return await Be({...r,defaultView:n}),e.redirect("/settings?saved=1",303)}),s.get("/settings/theme",y(),e=>e.html(be({theme:l.activeTheme,saved:e.req.query("saved")==="1"}))),s.post("/settings/theme",o(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings/theme?error=1",303)}const r=t.template;return typeof r!="string"||!Z(r)?e.redirect("/settings/theme",303):(l.activeTheme=await X(r),e.redirect("/settings/theme?saved=1",303))});const ce={vscode:"VS Code",ghostty:"Ghostty","warm-clay":"Warm Clay","dark-cove":"Dark Cove"};s.get("/api/theme",o(),e=>e.json({active:l.activeTheme.template,templates:Ge.map(t=>({id:t,name:ce[t]}))})),s.post("/api/theme",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=t.template;return typeof r!="string"||!Z(r)?e.json({error:"invalid theme template"},400):(l.activeTheme=await X(r),e.json({ok:!0,active:l.activeTheme.template}))}),s.get("/api/system/status",o(),e=>e.json(rt())),s.get("/api/system/processes",o(),e=>e.json(nt())),k&&(s.get("/settings/federation",y(),e=>e.redirect("/settings/machines",301)),s.get("/settings/machines",y(),async e=>e.html(Ce({config:k.getConfig(),status:k.status(),tokens:G().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt})),hostname:k.hostname},l.activeTheme))),s.get("/api/machines",o(),e=>e.json({config:k.getConfig(),status:k.status()})),s.post("/api/machines",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.hub=="string"?t.hub.trim():"",n=typeof t.token=="string"?t.token.trim():"",a=typeof t.name=="string"?t.name.trim().slice(0,64):"",i=t.enabled===!0;if(i){if(!r)return e.json({error:"hub URL is required"},400);if(!/^wss?:\/\//i.test(r))return e.json({error:"hub URL must start with ws:// or wss://"},400);if(!n)return e.json({error:"registration token is required"},400)}const u={hub:r||void 0,token:n||void 0,name:a||void 0,enabled:i};return await k.setConfig(u),e.json({ok:!0,status:k.status()})}),s.get("/api/agent-tokens",o(),e=>e.json(G().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt})))),s.post("/api/agent-tokens",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim().slice(0,64):"";if(!r)return e.json({error:"name is required"},400);const{id:n,token:a}=Ie(r);return e.json({ok:!0,id:n,name:r,token:a})}),s.delete("/api/agent-tokens/:id",o(),e=>{const t=e.req.param("id");return Ee(t)?e.json({ok:!0}):e.json({error:"no such token"},404)})),s.post("/api/system/kill",o(),async e=>{const{pid:t}=await e.req.json();if(typeof t!="number"||!Number.isInteger(t)||t<=0)return e.json({error:"invalid pid"},400);const r=st(t);return r.ok?e.json({ok:!0}):e.json({error:r.error},500)}),s.get("/api/agents",o(),e=>w?e.json(w.agents.list()):e.json([])),s.post("/api/sessions/new",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name is required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(r))return e.json({error:"name contains invalid characters"},400);const n=typeof t.dir=="string"&&t.dir.trim()?t.dir.trim():void 0;if(D().some(i=>i.name===r))return e.json({error:"session already exists"},409);try{return re(r,n),Y(r,{path:n}),e.json({ok:!0})}catch(i){const u=i instanceof ne?i.message:"failed to create session";return e.json({error:u},500)}}),s.post("/api/sessions/rename",o(),async e=>{try{const{oldName:t,newName:r}=await e.req.json();return!t||!r?e.json({error:"oldName and newName required"},400):/^[a-zA-Z0-9_\-. ]+$/.test(r)?(ot(t,r),ee(t,r),e.json({ok:!0})):e.json({error:"invalid characters in name"},400)}catch{return e.json({error:"rename failed"},500)}}),s.post("/api/sessions/kill",o(),async e=>{try{const{name:t}=await e.req.json();return t?(it(t),J(t),e.json({ok:!0})):e.json({error:"name required"},400)}catch{return e.json({error:"kill failed"},500)}}),s.post("/api/sessions/rebuild",o(),async e=>{try{const t=await e.req.json(),r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name required"},400);const n=typeof t.agentId=="string"?t.agentId:void 0,i=Ye().find(u=>u.name===r&&(u.agentId??null)===(n??null))?.path;if(n){if(!w)return e.json({error:"not a hub"},400);const u=w.getChannel(n);return u?u.rebuildSession(r,i)?e.json({ok:!0,async:!0}):e.json({error:"agent offline"},409):e.json({error:"agent offline"},409)}if(at(r))return e.json({error:"session already exists"},409);try{re(r,i)}catch(u){const p=u instanceof ne?u.message:"rebuild failed";return e.json({error:p},500)}return Y(r,{path:i}),e.json({ok:!0})}catch{return e.json({error:"rebuild failed"},500)}}),s.post("/api/sessions/dismiss",o(),async e=>{try{const t=await e.req.json(),r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name required"},400);const n=typeof t.agentId=="string"?t.agentId:void 0;return J(r,n),e.json({ok:!0})}catch{return e.json({error:"dismiss failed"},500)}}),s.post("/api/sessions/rename-tombstone",o(),async e=>{try{const t=await e.req.json(),r=typeof t.oldName=="string"?t.oldName.trim():"",n=typeof t.newName=="string"?t.newName.trim():"";if(!r||!n)return e.json({error:"oldName and newName required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(n))return e.json({error:"invalid characters in name"},400);const a=typeof t.agentId=="string"?t.agentId:void 0;return ee(r,n,a),e.json({ok:!0})}catch{return e.json({error:"rename failed"},500)}}),s.get("/api/session-program",o(),e=>{const t=e.req.query("session");if(!t)return e.json({program:null});try{const n=L("tmux",["display-message","-p","-t",t,"#{pane_current_command}"],{encoding:"utf-8",timeout:3e3,env:ye()}).trim()||null;return e.json({program:n})}catch{return e.json({program:null})}}),s.post("/api/session/:name/upload",o(),async e=>{try{const r=(await e.req.parseBody()).file;if(!(r instanceof File))return e.json({error:'expected a file field named "file"'},400);const n=Buffer.from(await r.arrayBuffer()),{path:a}=await $e(n,r.type,r.name);return e.json({path:a})}catch(t){return t instanceof We?e.json({error:t.message},t.status):e.json({error:"upload failed"},500)}}),s.get("/api/fs/session-path",o(),e=>{const t=e.req.query("session");if(!t)return e.json({path:process.env.HOME??"/"});try{const r=De(t),a=r.find(i=>i.active)?.path??r[0]?.path??process.env.HOME??"/";return e.json({path:a})}catch{return e.json({path:process.env.HOME??"/"})}}),s.get("/api/fs/list",o(),e=>{const t=process.env.HOME??"/";let r=e.req.query("path")??t;r.startsWith("~")&&(r=t+r.slice(1)),r.startsWith("/")||(r=h.join(t,r));const n=e.req.query("recursive")==="true";let a=r,i="",u=r.endsWith("/");if(!u)try{u=T(r).isDirectory()}catch{}u||(a=h.dirname(r),i=h.basename(r).toLowerCase());try{const p=me(a),c=[],d=[];for(const m of p)if(!(i&&!m.toLowerCase().startsWith(i))){try{const f=h.join(a,m);T(f).isDirectory()?(c.push(f),n&&Le(f,c,d,0)):d.push(f)}catch{}if(c.length+d.length>=5e3)break}return c.sort(Q),d.sort(Q),e.json({dirs:c,files:d})}catch{return e.json({dirs:[],files:[]})}});function le(e){const t={repoRoot:null,branch:null,files:[],linesAdded:0,linesRemoved:0};try{const r=_("git rev-parse --show-toplevel 2>/dev/null",{cwd:e,encoding:"utf-8",timeout:3e3}).trim();if(!r)return t;let n;try{n=_("git branch --show-current 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3}).trim()}catch{n=_("git rev-parse --short HEAD 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3}).trim()}n||(n="HEAD");let a="";try{a=_("git status --porcelain 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3})}catch{}const i=[];for(const d of a.split(`
17
+ `)){if(!d.trim())continue;const m=d[0],f=d[1],b=d.substring(3).trim(),R=f!==" "?f:m;i.push({path:b,status:R,staged:m,unstaged:f,additions:0,deletions:0})}const u=new Map;try{const d=_("git diff --numstat HEAD 2>/dev/null",{cwd:r,encoding:"utf-8",timeout:3e3});for(const m of d.split(`
18
+ `)){if(!m.trim())continue;const f=m.split(" ");if(f.length>=3){const b=parseInt(f[0],10)||0,R=parseInt(f[1],10)||0;u.set(f[2],{added:b,deleted:R})}}}catch{}for(const d of i){const m=u.get(d.path);m&&(d.additions=m.added,d.deletions=m.deleted)}let p=0,c=0;for(const d of i)p+=d.additions,c+=d.deletions;for(const d of i)if(d.status==="?"&&d.additions===0)try{const m=h.join(r,d.path),b=B(m,"utf-8").split(`
19
+ `).length;d.additions=b,p+=b}catch{}return{repoRoot:r,branch:n,files:i,linesAdded:p,linesRemoved:c}}catch{return t}}return s.get("/api/git/status",o(),e=>{const t=e.req.query("path");if(!t)return e.json({error:"path is required"},400);try{const r=A(t),n=le(r);return e.json(n)}catch(r){return r.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):r.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({repoRoot:null,branch:null,files:[],linesAdded:0,linesRemoved:0})}}),s.get("/api/git/diff",o(),e=>{const t=e.req.query("path"),r=e.req.query("file");if(!t||!r)return e.json({error:"path and file are required"},400);try{const n=A(t);if(!P(n)||!T(n).isDirectory())return e.json({error:"invalid repository path: "+n},400);try{const a=L("/usr/bin/git",["diff","HEAD","--",r],{cwd:n,encoding:"utf-8",timeout:5e3}),i=L("/usr/bin/git",["diff","--cached","--",r],{cwd:n,encoding:"utf-8",timeout:5e3});return e.json({diff:a,stagedDiff:i})}catch(a){throw console.error("[git/diff] execFileSync failed:",a?.code,a.message),a}}catch(n){return n.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):n.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):(console.error("[git/diff] exception:",n),e.json({error:String(n)},500))}}),s.get("/api/file",o(),e=>{try{const t=e.req.query("path");if(!t)return e.json({error:"path is required"},400);const r=A(t);if(!T(r).isFile())return e.json({error:"not a file"},400);const n=T(r).size;if(n>V)return e.json({error:"file too large",size:n,maxBytes:V},413);const a=B(r,"utf-8");return e.json({path:r,content:a,size:n})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):t.code==="ENOENT"?e.json({error:"not found"},404):e.json({error:"internal error"},500)}}),s.put("/api/file",o(),async e=>{try{const t=await e.req.json();if(!t.path||typeof t.content!="string")return e.json({error:"path and content are required"},400);const r=A(t.path);return Pe(r,t.content),e.json({ok:!0})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"write failed"},500)}}),s.post("/api/file/delete",o(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=A(t.path);return T(r).isFile()?(fe(r),e.json({ok:!0})):e.json({error:"not a file"},400)}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"delete failed"},500)}}),s.post("/api/file/touch",o(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=A(t.path);return P(r)?e.json({error:"file already exists"},409):(he(h.dirname(r),{recursive:!0}),pe(r,"","utf-8"),e.json({ok:!0,path:r}))}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"touch failed"},500)}}),s.get("/api/sidebar/sessions",o(),e=>{const t=e.req.query("currentSession");return e.json(N(typeof t=="string"&&t?t:void 0))}),s.get("/api/sidebar/session-windows/:session",o(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.json(Ke(t))}),s.post("/api/pinned-views",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=U(t);return"error"in r?e.json({error:r.error},400):(await ze(r.sessionName,r.windowIndex),e.json(N()))}),s.delete("/api/pinned-views",o(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=U(t);return"error"in r?e.json({error:r.error},400):(await Ve(r.sessionName,r.windowIndex),e.json(N()))}),s.get("/api/notes",o(),e=>{const t=[...S.data.notes].sort((r,n)=>(n.updatedAt??0)-(r.updatedAt??0));return e.json(t)}),s.get("/api/notes/:scope",o(),e=>{const t=decodeURIComponent(e.req.param("scope")),r=S.data.notes.find(n=>n.scope===t);return r?e.json(r):e.json(null,404)}),s.put("/api/notes/:scope",o(),async e=>{const t=decodeURIComponent(e.req.param("scope"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}if(typeof r.content!="string")return e.json({error:"content must be string"},400);const n={scope:t,content:r.content,updatedAt:Date.now()},a=S.data.notes.findIndex(i=>i.scope===t);return a>=0?S.data.notes[a]=n:S.data.notes.push(n),await S.write(),e.json({ok:!0})}),w&&(s.use("/a/:agentId/*",o()),dt(s,{getChannel:w.getChannel})),s}export{Vt as buildApp};
@@ -1 +1 @@
1
- import e from"node:path";import{readdirSync as f,statSync as l}from"node:fs";const v=1048576;function a(){const o=process.env.TMUX_WEB_FS_ROOTS;return o?o.split(":").map(r=>(r=r.trim(),r.startsWith("~")&&(r=e.join(process.env.HOME||"/",r.slice(1))),e.resolve(r))):[]}function m(o){const r=a();let t=o;if(t.startsWith("~")&&(t=e.join(process.env.HOME||"/",t.slice(1))),e.isAbsolute(t)||(t=e.resolve(t)),t=e.normalize(t),r.length===0)return t;if(!r.some(s=>t===s||t.startsWith(s+e.sep)))throw new Error("PATH_NOT_ALLOWED");return t}function h(o,r,t,i,s=5e3){if(i>8)return;let c;try{c=f(o)}catch{return}for(const u of c){if(u.startsWith("."))continue;const n=e.join(o,u);try{l(n).isDirectory()?(r.push(n),h(n,r,t,i+1,s)):t.push(n)}catch{}if(r.length+t.length>=s)return}}export{v as MAX_FILE_BYTES,m as resolveFsPath,a as resolveFsRoots,h as walkRecursive};
1
+ import n from"node:path";import{readdirSync as u,statSync as f}from"node:fs";const _=1048576;function v(e,r){const t=n.basename(e).startsWith("."),s=n.basename(r).startsWith(".");return t!==s?t?1:-1:e.localeCompare(r)}function l(){const e=process.env.TMUX_WEB_FS_ROOTS;return e?e.split(":").map(r=>(r=r.trim(),r.startsWith("~")&&(r=n.join(process.env.HOME||"/",r.slice(1))),n.resolve(r))):[]}function E(e){const r=l();let t=e;if(t.startsWith("~")&&(t=n.join(process.env.HOME||"/",t.slice(1))),n.isAbsolute(t)||(t=n.resolve(t)),t=n.normalize(t),r.length===0)return t;if(!r.some(o=>t===o||t.startsWith(o+n.sep)))throw new Error("PATH_NOT_ALLOWED");return t}function h(e,r,t,s,o=5e3){if(s>8)return;let c;try{c=u(e)}catch{return}for(const a of c){if(a.startsWith("."))continue;const i=n.join(e,a);try{f(i).isDirectory()?(r.push(i),h(i,r,t,s+1,o)):t.push(i)}catch{}if(r.length+t.length>=o)return}}export{_ as MAX_FILE_BYTES,v as fsEntryCmp,E as resolveFsPath,l as resolveFsRoots,h as walkRecursive};
@@ -250,7 +250,7 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as r}from"./ico
250
250
  <script>${y()}</script>`}function A(e){const{activePage:o,refreshHref:a}=e;function t(l,f,b,x,g=""){return`<a href="${f}" class="${l!==null&&l===o?"sidebar-btn current":"sidebar-btn"}"${g}>
251
251
  <svg viewBox="0 0 24 24" fill="currentColor">${b}</svg>
252
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">
253
+ </a>`}const s=r("sessions"),p=r("notes"),h=r("history"),u=r("quick-commands"),m=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")}
@@ -259,9 +259,9 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as r}from"./ico
259
259
  <hr class="sidebar-divider">
260
260
  ${t("home","/",s,"Sessions")}
261
261
  ${t("notes","/notes",p,"All Notes")}
262
- ${t("files","/files",u,"Files")}
262
+ ${t("files","/files",m,"Files")}
263
263
  ${t("history","/history",h,"History")}
264
- ${t("quickCommands","/quick-commands",m,"Quick Commands")}
264
+ ${t("quickCommands","/quick-commands",u,"Quick Commands")}
265
265
  <hr class="sidebar-divider">
266
266
  <a href="${a}" class="sidebar-btn">
267
267
  ${i("refresh")}
@@ -498,7 +498,12 @@ import{escapeHtml as c}from"./html.js";import{icon as i,iconPath as r}from"./ico
498
498
  try {
499
499
  const res = await fetch(selectedBase() + '/api/fs/list?path=' + encodeURIComponent(path));
500
500
  const data = await res.json();
501
- const dirs = (data.dirs || []).filter(d => d !== path).sort((a, b) => a.localeCompare(b));
501
+ const dirs = (data.dirs || []).filter(d => d !== path).sort((a, b) => {
502
+ const ah = a.split('/').filter(Boolean).pop().startsWith('.');
503
+ const bh = b.split('/').filter(Boolean).pop().startsWith('.');
504
+ if (ah !== bh) return ah ? 1 : -1;
505
+ return a.localeCompare(b);
506
+ });
502
507
  treeCache[path] = dirs;
503
508
  return dirs;
504
509
  } catch { treeCache[path] = []; return []; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "2.2.23",
3
+ "version": "2.2.25",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {