tmux-weblink 2.2.0 → 2.2.1
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.
|
@@ -2,23 +2,23 @@ var an=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
|
|
|
2
2
|
`)}selectRows(t,n){let s=this.terminal.buffer.active,[o,r]=t<=n?[t,n]:[n,t];this.terminal.selectLines(s.viewportY+o,s.viewportY+r)}selectCells(t,n,s,o){let r=this.terminal.buffer.active,c=Math.min(n,o),l=Math.max(n,o);if(c===l){let u=Math.min(t,s),p=Math.max(t,s);this.terminal.select(u,r.viewportY+c,p-u+1)}else this.terminal.selectLines(r.viewportY+c,r.viewportY+l)}getCellsText(t,n,s,o){let r=this.terminal.buffer.active,c=Math.min(n,o),l=Math.max(n,o),u=n<o?t:n>o?s:Math.min(t,s),p=n<o?s:n>o?t:Math.max(t,s),S=r.viewportY,x=[];for(let H=c;H<=l;H++){let ge=r.getLine(S+H);if(!ge){x.push("");continue}let O=H===c?u:0,je=H===l?p:this.terminal.cols-1,K="";for(let C=O;C<=je;C++){let j=ge.getCell(C);j&&(K+=j.getChars())}x.push(K.replace(/\s+$/,""))}return x.join(`
|
|
3
3
|
`)}getSelection(){return this.terminal.getSelection()}clearSelection(){this.terminal.clearSelection()}hasSelectionSupport(){return!0}};function rn(e){let t=e.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(t.width,n.width),height:Math.max(0,Math.min(t.height,n.height-Math.max(0,t.top)))}:t}function cn(e,t,n){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let s=await ln.create(e,n.scrollback,n.theme),o,r=0,c,l=null,u=0,p=0,S=.2,x=10,H=.95,ge=.04;function O(){u&&(cancelAnimationFrame(u),u=0),p=0}function je(i){if(O(),Math.abs(i)<S)return;p=Math.max(-x,Math.min(x,i));let a=()=>{if(N){u=0;return}let d=e.querySelector(".xterm-rows > div"),f=d?d.getBoundingClientRect().height:16,b=f>0?Math.round(p*16/f):p>0?1:-1;b===0&&(b=p>0?1:-1);let B=s.viewportY()-b,F=s.baseY(),ae=Math.max(0,Math.min(B,F));if(s.scrollToLine(ae,!0),p*=H,ae!==B){u=0;return}if(Math.abs(p)<ge){u=0;return}u=requestAnimationFrame(a)};u=requestAnimationFrame(a)}let K=0,C="connecting",j=0,G=!1,ye=[],V="",N=!1,Q=1e3,At=location.protocol==="https:"?"wss:":"ws:",Rt=n.wsBase||"/ws",$t=At+"//"+location.host+Rt+"/"+encodeURIComponent(t),Nt="/api/session/"+encodeURIComponent(t)+"/upload",Pt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function _t(){return ye.join("")+V}async function jt(i,a=0){let d=i?s.viewportY():0,f=oe(_t());if(s.reset(),!f){i||s.scrollToBottom();return}await s.write(f),i?s.scrollToLine(a+d):s.scrollToBottom()}function z(i){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(i))}function zt(i){let a;try{a=JSON.parse(i)}catch{C==="live"&&(V+=i,s.write(oe(i)));return}if(a.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?z({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(a.type==="attach_failed"&&typeof a.message=="string"){we=!0,C="dead",s.reset(),s.write(`\r
|
|
4
4
|
\x1B[31m`+oe(a.message)+`\x1B[0m\r
|
|
5
|
-
`).then(()=>{Le();try{o?.close()}catch{}});return}if(a.type==="auth.ok"){a.token&&localStorage.setItem("tmux-web-token",a.token),Q=1e3,W(),z({type:"resize",cols:s.cols,rows:s.rows}),M();return}if(a.type==="auth.failed"){if(a.permanentLock||a.retryAfterMs&&a.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(a.message);return}return}if(a.type==="snapshot"&&typeof a.data=="string"){let d=Ue(oe(a.data));ye=[d],V="",j=typeof a.lines=="number"?a.lines:n.terminal.initialLines,C="live",s.reset(),W(),z({type:"resize",cols:s.cols,rows:s.rows}),s.write(d).then(()=>s.scrollToBottom());return}if(a.type==="window_changed"&&typeof a.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(a.activeIndex)&&(d.searchParams.set("window",String(a.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:a.windows}));return}if(a.type==="history"&&typeof a.data=="string"){a.lines>0&&a.data?(ye.unshift(Ue(a.data)),j+=a.lines,jt(!0,a.lines).finally(()=>{G=!1})):G=!1;return}if(a.type==="data"&&typeof a.data=="string"){let d=Ue(oe(a.data));if(C==="connecting"){C="live",V=d,s.write(d);return}C==="live"&&(V+=d,s.write(d))}}let we=!1;function
|
|
5
|
+
`).then(()=>{Le();try{o?.close()}catch{}});return}if(a.type==="auth.ok"){a.token&&localStorage.setItem("tmux-web-token",a.token),Q=1e3,W(),z({type:"resize",cols:s.cols,rows:s.rows}),M();return}if(a.type==="auth.failed"){if(a.permanentLock||a.retryAfterMs&&a.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(a.message);return}return}if(a.type==="snapshot"&&typeof a.data=="string"){let d=Ue(oe(a.data));ye=[d],V="",j=typeof a.lines=="number"?a.lines:n.terminal.initialLines,C="live",s.reset(),W(),z({type:"resize",cols:s.cols,rows:s.rows}),s.write(d).then(()=>s.scrollToBottom());return}if(a.type==="window_changed"&&typeof a.activeIndex=="number"){let d=new URL(location.href);d.searchParams.get("window")!==String(a.activeIndex)&&(d.searchParams.set("window",String(a.activeIndex)),history.replaceState(history.state,"",d)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:a.windows}));return}if(a.type==="history"&&typeof a.data=="string"){a.lines>0&&a.data?(ye.unshift(Ue(a.data)),j+=a.lines,jt(!0,a.lines).finally(()=>{G=!1})):G=!1;return}if(a.type==="data"&&typeof a.data=="string"){let d=Ue(oe(a.data));if(C==="connecting"){C="live",V=d,s.write(d);return}C==="live"&&(V+=d,s.write(d))}}let we=!1;function Qe(){if(we)return;let i=$t,a=localStorage.getItem("tmux-web-token");a&&(i+=(i.includes("?")?"&":"?")+"token="+encodeURIComponent(a)),i+=(i.includes("?")?"&":"?")+"cols="+s.cols+"&rows="+s.rows,o=new WebSocket(i),o.onopen=()=>{C="connecting",j=0,G=!1,ye=[],V="",s.reset(),Q=1e3},o.onmessage=d=>{typeof d.data=="string"&&zt(d.data)},o.onclose=()=>{N||(C="connecting",!we&&setTimeout(()=>{Q=Math.min(Q*2,1e4),!N&&!we&&Qe()},Q))},o.onerror=()=>o?.close()}function be(i){z({type:"input",data:i})}function W(){return s.fit()}function Le(){e.classList.remove("terminal-pending")}function M(){r&&cancelAnimationFrame(r),c&&clearTimeout(c),W(),r=requestAnimationFrame(()=>{r=0,W()&&Le()}),c=setTimeout(()=>{W()&&Le()},120)}function Wt(){M(),setTimeout(M,50),setTimeout(M,150),setTimeout(M,300)}function et(i){return typeof i=="string"&&i.startsWith("image/")}function tt(i){return i?.files?.length?i.files[0]??null:null}function Ft(i){if(!i?.items)return null;for(let a=0;a<i.items.length;a++){let d=i.items[a];if(d?.kind==="file"&&et(d.type))return d.getAsFile()}return null}async function Ut(i){let a=new FormData,d=i instanceof File&&i.name?i.name:"upload";a.append("file",i,d);let f=localStorage.getItem("tmux-web-token"),b={};f&&(b.Authorization="Bearer "+f);let B=await fetch(Nt,{method:"POST",body:a,headers:b});if(!B.ok){let ae="upload failed";try{let rt=await B.json();rt.error&&(ae=rt.error)}catch{}throw new Error(ae)}let F=await B.json();if(!F.path)throw new Error("no path in response");return F.path}async function ze(i){try{let a=await Ut(i);be(a)}catch{}}s.onData(i=>be(i)),s.onResize(({cols:i,rows:a})=>z({type:"resize",cols:i,rows:a})),s.onScroll(()=>{C!=="live"||G||!s.isNearScrollbackTop()||(G=!0,z({type:"load_history",before:j}))});let ee=!1,te=!1,w=null,L=null,ne=document.createElement("div");ne.className="terminal-copy-banner",ne.innerHTML='<strong>Copy mode</strong><span class="hint">drag to copy \xB7 Esc exits</span>',ne.hidden=!0,e.appendChild(ne);let R=document.createElement("div");R.className="terminal-copy-banner terminal-copy-toast",R.textContent="Copied",R.hidden=!0,e.appendChild(R);function nt(i,a){let d=e.querySelector(".xterm-screen, canvas"),f=(d&&d.getBoundingClientRect().width>0?d:e).getBoundingClientRect(),b=f.height>0&&s.rows>0?f.height/s.rows:18,B=f.width>0&&s.cols>0?f.width/s.cols:9;return{row:Math.max(0,Math.min(s.rows-1,Math.floor((a-f.top)/b))),col:Math.max(0,Math.min(s.cols-1,Math.floor((i-f.left)/B)))}}async function Ee(i){if(!i)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(i);return}}catch{}let a=document.createElement("textarea");a.value=i,a.style.position="fixed",a.style.opacity="0",a.style.pointerEvents="none",document.body.appendChild(a),a.focus(),a.select();try{document.execCommand("copy")}catch{}a.remove()}let se;function ke(){R.hidden=!1,se&&clearTimeout(se),se=setTimeout(()=>{R.hidden=!0},1e3)}function J(i){ee=i,ne.hidden=!i,e.classList.toggle("terminal-copy-mode-active",i),i&&s.focus()}function st(){J(!ee)}function Yt(i){N||i.button!==0||te||(ee||i.shiftKey)&&(i.preventDefault(),i.stopPropagation(),te=!0,w=nt(i.clientX,i.clientY),L=w,s.selectCells(w.col,w.row,L.col,L.row),document.addEventListener("mousemove",We,!0),document.addEventListener("mouseup",Fe,!0))}function We(i){N||!te||!w||(i.preventDefault(),i.stopPropagation(),L=nt(i.clientX,i.clientY),s.selectCells(w.col,w.row,L.col,L.row))}function Fe(i){if(N||!te||!w||!L)return;i.preventDefault(),i.stopPropagation(),document.removeEventListener("mousemove",We,!0),document.removeEventListener("mouseup",Fe,!0),te=!1;let a=s.getCellsText(w.col,w.row,L.col,L.row);w=null,L=null,s.clearSelection(),J(!1),a&&(R.textContent="\u5DF2\u590D\u5236",Ee(a).then(ke)),s.focus()}e.addEventListener("mousedown",Yt,!0),document.addEventListener("keydown",qt,!0);function qt(i){if(!(N||!s.isFocused()||i.defaultPrevented||i.isComposing)){if(ee){if(i.key==="Escape"){i.preventDefault(),i.stopPropagation(),w=null,L=null,s.clearSelection(),J(!1);return}if(i.key==="Enter"||(i.ctrlKey||i.metaKey)&&i.code==="KeyC"){i.preventDefault(),i.stopPropagation(),Ee(s.getScreenText()).then(ke),J(!1);return}}if(i.altKey&&i.code==="KeyC"&&!i.ctrlKey&&!i.metaKey){if(i.preventDefault(),i.stopPropagation(),w&&L){let a=s.getCellsText(w.col,w.row,L.col,L.row);w=null,L=null,s.clearSelection(),J(!1),a&&(R.textContent="Copied",Ee(a).then(ke))}else{let a=s.getSelection();a?(s.clearSelection(),R.textContent="Copied",Ee(a).then(ke)):st()}return}if((i.ctrlKey||i.metaKey)&&i.code==="KeyV"){i.preventDefault(),i.stopPropagation(),(async()=>{try{if(navigator.clipboard?.read){let a=await navigator.clipboard.read();for(let d of a)for(let f of d.types)if(et(f)){let b=await d.getType(f);await ze(b);return}}}catch{}try{let a=await navigator.clipboard?.readText();a&&s.pasteText(it(a))}catch{}})();return}}}let xe=0;function it(i){return i.split(`\r
|
|
6
6
|
`).join(`
|
|
7
7
|
`).split(`
|
|
8
|
-
`).join("\r")}async function Ot(i){let a=Ft(i.clipboardData);if(a){i.preventDefault(),i.stopPropagation();let b=Date.now();if(b-xe<50)return;xe=b,await ze(a);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let f=Date.now();f-xe<50||(xe=f,s.pasteText(
|
|
8
|
+
`).join("\r")}async function Ot(i){let a=Ft(i.clipboardData);if(a){i.preventDefault(),i.stopPropagation();let b=Date.now();if(b-xe<50)return;xe=b,await ze(a);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let f=Date.now();f-xe<50||(xe=f,s.pasteText(it(d)))}let ie=0;function Kt(i){i.preventDefault(),ie++,tt(i.dataTransfer)&&e.classList.add("terminal-drag-over")}function Vt(i){i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy")}function Jt(i){i.preventDefault(),ie--,ie<=0&&(ie=0,e.classList.remove("terminal-drag-over"))}async function Xt(i){i.preventDefault(),ie=0,e.classList.remove("terminal-drag-over");let a=tt(i.dataTransfer);a&&await ze(a)}function at(i){!Pt||i.key!=="Escape"||ee||i.defaultPrevented||i.repeat||i.metaKey||i.ctrlKey||i.altKey||s.isFocused()&&(i.preventDefault(),i.stopPropagation(),be("\x1B"))}e.addEventListener("paste",Ot),e.addEventListener("dragenter",Kt),e.addEventListener("dragover",Vt),e.addEventListener("dragleave",Jt),e.addEventListener("drop",Xt),document.addEventListener("keydown",at,!0);function Zt(i){if(i.touches.length!==1){l=null;return}let a=i.touches[0];a&&(O(),l={startX:a.clientX,startY:a.clientY,lastX:a.clientX,lastY:a.clientY,lastT:performance.now(),velocity:0,scrolling:!1},i.stopPropagation())}function Gt(i){if(!l||i.touches.length!==1)return;let a=i.touches[0];if(!a)return;let d=a.clientY-l.startY,f=a.clientX-l.startX;if(!l.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(f))return;l.scrolling=!0,K=Date.now()+500}i.preventDefault(),i.stopPropagation();let b=performance.now(),B=a.clientY-l.lastY,F=b-l.lastT;F>0&&(l.velocity=B/F),nn(-B),l.lastX=a.clientX,l.lastY=a.clientY,l.lastT=b}function Qt(i){if(!l)return;let a=l.scrolling,d=l.velocity;if(l=null,i.stopPropagation(),!a){s.focus();return}K=Date.now()+500,i.preventDefault(),d&&je(d)}function en(i){l=null,O(),i.stopPropagation()}function tn(i){i.pointerType==="touch"&&Date.now()<K&&(i.preventDefault(),i.stopPropagation())}function nn(i){let a=e.querySelector(".xterm-rows > div"),d=a?a.getBoundingClientRect().height:16,f=d>0?Math.round(i/d):i>0?1:-1;f===0&&(f=i>0?1:-1),s.scrollLines(f)}e.addEventListener("touchstart",Zt,{passive:!0,capture:!0}),e.addEventListener("touchmove",Gt,{passive:!1,capture:!0}),e.addEventListener("touchend",Qt,{passive:!1,capture:!0}),e.addEventListener("touchcancel",en,{passive:!0,capture:!0}),e.addEventListener("pointerup",tn,!0);function ot(){M()}window.addEventListener("resize",ot),window.visualViewport?.addEventListener("resize",M),window.visualViewport?.addEventListener("scroll",M);let lt=new ResizeObserver(()=>M());lt.observe(e),document.fonts?.ready.then(()=>{M(),setTimeout(M,50),setTimeout(M,200)}),e.addEventListener("focusin",Wt,!0);async function sn(){if(document.fonts?.ready)try{await Promise.race([document.fonts.ready,new Promise(i=>setTimeout(i,1e3))])}catch{}}return requestAnimationFrame(()=>{W()&&Le(),(async()=>(await sn(),W()&&z({type:"resize",cols:s.cols,rows:s.rows}),Qe(),M()))()}),{destroy(){if(N=!0,O(),J(!1),se&&clearTimeout(se),document.removeEventListener("mousemove",We,!0),document.removeEventListener("mouseup",Fe,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",ot),lt.disconnect(),document.removeEventListener("keydown",at,!0)},focus(){s.focus()},sendInput(i){be(i)},toggleCopyMode(){st()}}})()}var le=window.__TMUX_WEB_SHELL__,v=null,y=null,ct=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null,k=ct;window.__tmuxWebScopeAgent=ct;var Ye="sessions",P="",Me="",Te="",dt="";function T(e){return k?"/a/"+encodeURIComponent(k)+e:e}var m=document.getElementById("sidebar-content"),U=document.getElementById("main-placeholder"),I=document.getElementById("terminal-container"),re=document.getElementById("file-editor"),dn=document.getElementById("fe-path"),D=document.getElementById("fe-status"),ce=document.getElementById("fe-content"),Y=document.getElementById("fe-save"),un=document.getElementById("fe-delete"),pn=document.getElementById("fe-back"),ut=document.getElementById("fe-new-name"),fn=document.getElementById("fe-new-btn"),qe=document.getElementById("settings-backdrop"),pt=document.getElementById("settings-popover"),ft=document.querySelector(".sidebar");function mn(){return window.matchMedia("(max-width: 640px)").matches}function Oe(){mn()&&ft.classList.add("collapsed")}function mt(){ft.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{mt(),ht("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{mt(),ht("files")});function ht(e){Ye=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?(Ke="",X()):Sn()}var Ke="";async function X(){if(Ye!=="sessions")return;try{let t=await(await fetch("/api/sidebar/sessions")).json(),n=JSON.stringify({recent:t.recent,agents:t.agents});if(n===Ke)return;Ke=n,wn(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")}),Ge()}var vt="tmux-web-sidebar-group-collapsed";function gt(){try{return new Set(JSON.parse(localStorage.getItem(vt)||"[]"))}catch{return new Set}}function hn(e){try{localStorage.setItem(vt,JSON.stringify([...e]))}catch{}}function vn(e,t,n){if(!e)return"\u2026";let s=n&&(e===n||e.startsWith(n+"/"))?"~"+e.slice(n.length):e,o=s.split("/").filter(Boolean);if(o.length===0)return e;for(let r=1;r<=o.length;r++){let c=o.slice(-r).join("/");if(!t.some(l=>!l||l===e?!1:(n&&(l===n||l.startsWith(n+"/"))?"~"+l.slice(n.length):l).split("/").filter(Boolean).slice(-r).join("/")===c))return c}return s}function gn(e,t){let n=new Map;for(let r of e){let c=r.path||"";n.has(c)||n.set(c,[]),n.get(c).push(r)}let s=[...n.keys()],o=gt();for(let[r,c]of n){let l=vn(r,s,t),u=o.has(r),p=document.createElement("div");p.className="session-group-header"+(u?" collapsed":""),p.title=r||"No working directory",p.innerHTML=`<span class="sg-folder">📁</span><span class="sg-label">${h(l)}</span>`,p.addEventListener("click",()=>{let x=gt(),H=p.nextElementSibling;x.has(r)?(x.delete(r),p.classList.remove("collapsed"),H?.classList.remove("collapsed")):(x.add(r),p.classList.add("collapsed"),H?.classList.add("collapsed")),hn(x)});let S=document.createElement("div");S.className="session-group-body"+(u?" collapsed":"");for(let x of c)S.appendChild(yn(x));m.appendChild(p),m.appendChild(S)}}function yn(e){let t=document.createElement("div");t.className="session-item"+(e.name===y&&!k?" 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>
|
|
9
9
|
<span>${h(e.name)}</span>
|
|
10
10
|
<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>
|
|
11
11
|
`,t.addEventListener("click",s=>{s.target.closest(".session-edit-btn")||de(e.name)});let n=t.querySelector(".session-edit-btn");return n&&n.addEventListener("click",s=>{s.stopPropagation(),An(e.name,n)}),t}function wn(e){m.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';let t=e.recent||[],n=document.createElement("div");n.className="sidebar-section-label"+(k?" clickable":""),n.textContent=k?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",k&&(n.title="Back to hub",n.addEventListener("click",()=>{window.location.href="/"})),m.appendChild(n),gn(t,e.home);let s=e.agents||[];for(let o of s){if(!o.sessions||o.sessions.length===0)continue;let r=o.agentId===k,c=document.createElement("div");c.className="sidebar-section-label machine-label"+(o.online?"":" agent-offline-label")+(!r&&o.online?" clickable":""),c.textContent=o.agentName+(o.online?"":" (offline)"),!r&&o.online&&(c.title="\u6253\u5F00 "+o.agentName,c.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(o.agentId)+"/"})),m.appendChild(c);for(let l of o.sessions){let u=document.createElement("div");u.className="session-item"+(l.name===y&&k===o.agentId?" active":"")+(o.online?"":" session-offline"),u.setAttribute("data-session",l.name),u.setAttribute("data-agent",o.agentId),u.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${l.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
|
|
12
|
-
<span>${h(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||de(l.name,o.agentId)}),m.appendChild(u)}}}function h(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function de(e,t){if(!(y===e&&v&&k===(t||null))){re.style.display="none",U.style.display="none",I.style.display="",I.classList.add("terminal-pending"),Oe(),v&&($e(),v.destroy(),v=null),y=e,k=t||null,window.__tmuxWebScopeAgent=k,window.__tmuxWebCurrentSession=y;for(let n of m.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===e&&(n.getAttribute("data-agent")||null)===k);try{v=await cn(I,e,{terminal:le.terminal,scrollback:le.scrollback,theme:le.theme,wsBase:k?"/ws/a/"+encodeURIComponent(k):le.wsBase||"/ws"}),
|
|
12
|
+
<span>${h(l.name)}</span><span class="meta">${o.online?"":"offline"}</span>`,o.online&&u.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||de(l.name,o.agentId)}),m.appendChild(u)}}}function h(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function de(e,t){if(!(y===e&&v&&k===(t||null))){re.style.display="none",U.style.display="none",I.style.display="",I.classList.add("terminal-pending"),Oe(),v&&($e(),v.destroy(),v=null),y=e,k=t||null,window.__tmuxWebScopeAgent=k,window.__tmuxWebCurrentSession=y;for(let n of m.querySelectorAll(".session-item"))n.classList.toggle("active",n.getAttribute("data-session")===e&&(n.getAttribute("data-agent")||null)===k);try{v=await cn(I,e,{terminal:le.terminal,scrollback:le.scrollback,theme:le.theme,wsBase:k?"/ws/a/"+encodeURIComponent(k):le.wsBase||"/ws"}),Tt()}catch(n){console.error("[shell] terminal init failed:",n);let s=n&&typeof n=="object"&&"message"in n?n.message:String(n);I.textContent="Failed to open terminal for "+e+": "+s,I.classList.remove("terminal-pending")}}}var Se=[],g=null,yt=!1;function bn(e){if(!g||!g.files.length)return null;let t=g.repoRoot;if(!t||!e.startsWith(t))return null;let n=e.slice(t.length+1);for(let s of g.files)if(s.path===n)return s.status;return null}async function Ln(e){yt=!0;try{let t=await fetch(T("/api/git/status?path="+encodeURIComponent(e)));t.ok?g=await t.json():g=null}catch{g=null}finally{yt=!1}}function En(){if(!g||!g.branch)return"";let{branch:e,linesAdded:t,linesRemoved:n}=g,s=`<div class="sidebar-git"><div class="git-branch-badge">
|
|
13
13
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M11.63 1.22a.75.75 0 00-1.06 0L5.99 5.8 4.12 3.93a.75.75 0 10-1.06 1.06L4.93 6.87A3.5 3.5 0 003 10.09v3.16a.75.75 0 001 0v-3.16a2.5 2.5 0 012.5-2.5h.27l-1.06 1.06a.75.75 0 001.06 1.06l2.37-2.38a.75.75 0 000-1.06L7.59 5.27l4.04-4.05a.75.75 0 000-1.06L11.63 1.22zM3.25 13.94a.75.75 0 01-.75-.75v-.69a.75.75 0 011.5 0v.69c0 .414-.336.75-.75.75z"/></svg>
|
|
14
|
-
${h(e)}</div>`;return(t>0||n>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${n}</span></div>`),s+"</div>"}m.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&Z&&g&&(e.stopPropagation(),
|
|
14
|
+
${h(e)}</div>`;return(t>0||n>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${t}</span> <span class="del">-${n}</span></div>`),s+"</div>"}m.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&Z&&g&&(e.stopPropagation(),kt())});var ue=document.getElementById("header-git"),wt=document.getElementById("git-diff-view"),bt=document.getElementById("gd-path"),Ie=document.getElementById("gd-status"),Be=document.getElementById("gd-content"),kn=document.getElementById("gd-back"),Z=null;function pe(e){if(!e||!e.branch){ue.style.display="none",Z=null;return}Z=e.repoRoot;let{branch:t,linesAdded:n,linesRemoved:s}=e,o=`<span class="branch">${h(t)}</span>`;(n>0||s>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),ue.innerHTML=o,ue.style.display="block"}ue.addEventListener("click",e=>{Z&&g&&(e.stopPropagation(),kt())});var E=document.getElementById("git-popover"),De=document.getElementById("git-popover-backdrop");function Ve(){let e=ue.getBoundingClientRect();E.style.top=e.bottom+4+"px",E.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function Lt(){let e=Z,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(Ae(),e){Ye="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),_(e)}})}var Et="";function kt(){if(!g)return;let e=g.files||[];if(!e.length)E.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
|
|
15
15
|
<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let t=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let n=0;n<e.length;n++){let s=e[n],o=s.additions||0,r=s.deletions||0,c="";switch(s.status){case"M":c='<span class="git-file-status M">M</span>';break;case"A":c='<span class="git-file-status A">A</span>';break;case"D":c='<span class="git-file-status D">D</span>';break;case"?":c='<span class="git-file-status ?">?</span>';break;case"R":c='<span class="git-file-status R">R</span>';break}t+=`<div class="git-popover-item" data-idx="${n}">
|
|
16
16
|
${c}<span class="file-path">${h(s.path)}</span>
|
|
17
17
|
<span class="file-add">${o>0?"+"+o:""}</span>
|
|
18
18
|
<span class="file-del">${r>0?"-"+r:""}</span>
|
|
19
|
-
</div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',
|
|
19
|
+
</div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',Et=t,E.innerHTML=t,E.querySelectorAll(".git-popover-item").forEach(n=>{let s=parseInt(n.dataset.idx||"0",10),o=e[s];!o||o.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>Ct(o.path)))})}Ve(),E.classList.add("open"),De.classList.add("open"),Lt()}function xn(){return window.innerWidth>=1024}function xt(e,t){let n=(e+(t&&e?`
|
|
20
20
|
`:"")+t).split(`
|
|
21
|
-
`),s="",o=0,r=0;for(let c of n){let l=c;if(l.startsWith("@@")){let u=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(o=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),s+=`<div class="diff-line hunk"><span class="ln-body">${h(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(s+=`<div class="diff-line header"><span class="ln-body">${h(l)}</span></div>`);continue}if(l.startsWith("+"))r++,s+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(l)}</span></div>`;else if(l.startsWith("-"))o++,s+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${h(l)}</span></div>`;else if(l.startsWith("\\"))s+=`<div class="diff-line"><span class="ln-body">${h(l)}</span></div>`;else{if(!l&&!o&&!r)continue;o++,r++,s+=`<div class="diff-line"><span class="ln-no">${o}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(l)}</span></div>`}}return{html:s,lineCount:n.length}}var He=null;async function
|
|
21
|
+
`),s="",o=0,r=0;for(let c of n){let l=c;if(l.startsWith("@@")){let u=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(o=parseInt(u[1],10)-1,r=parseInt(u[2],10)-1),s+=`<div class="diff-line hunk"><span class="ln-body">${h(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(s+=`<div class="diff-line header"><span class="ln-body">${h(l)}</span></div>`);continue}if(l.startsWith("+"))r++,s+=`<div class="diff-line add"><span class="ln-no">${r}</span><span class="ln-body">${h(l)}</span></div>`;else if(l.startsWith("-"))o++,s+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${h(l)}</span></div>`;else if(l.startsWith("\\"))s+=`<div class="diff-line"><span class="ln-body">${h(l)}</span></div>`;else{if(!l&&!o&&!r)continue;o++,r++,s+=`<div class="diff-line"><span class="ln-no">${o}</span><span class="ln-no" style="margin-right:12px">${r}</span><span class="ln-body">${h(l)}</span></div>`}}return{html:s,lineCount:n.length}}var He=null;async function Ct(e){let t=Z;if(t)if(Ae(),xn()){I.style.display="none",v&&($e(),v.destroy(),v=null,He=y,y=null),pe(null),re.style.display="none",U.style.display="none",wt.style.display="flex",bt.textContent=e,Ie.textContent="Loading\u2026",Be.innerHTML="";try{let n=await fetch(T("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e)));if(!n.ok)throw new Error(await n.text());let s=await n.json();Mn(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);Ie.textContent="Error",Be.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${h(s)}</div>`}}else{E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
|
|
22
22
|
<button class="diff-back">\u2190 Back</button>
|
|
23
23
|
<span class="diff-filename">${h(e)}</span>
|
|
24
24
|
<span class="diff-loading">Loading\u2026</span>
|
|
@@ -28,11 +28,11 @@ var an=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
|
|
|
28
28
|
</div><div class="diff-empty">Error loading diff: ${h(s)}</div></div>`}}}function Cn(e,t,n){if(!t&&!n){E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
|
|
29
29
|
<button class="diff-back">\u2190 Back</button>
|
|
30
30
|
<span class="diff-filename">${h(e)}</span>
|
|
31
|
-
</div><div class="diff-empty">No diff content available</div></div>`;return}let{html:s,lineCount:o}=
|
|
31
|
+
</div><div class="diff-empty">No diff content available</div></div>`;return}let{html:s,lineCount:o}=xt(t,n);E.innerHTML=`<div class="git-diff-view"><div class="diff-header">
|
|
32
32
|
<button class="diff-back">\u2190 Back</button>
|
|
33
33
|
<span class="diff-filename">${h(e)}</span>
|
|
34
34
|
<span class="diff-filesize">${o} lines</span>
|
|
35
|
-
</div><div class="diff-content">${s}</div></div>`,E.querySelector(".diff-back")?.addEventListener("click",()=>{E.innerHTML=
|
|
35
|
+
</div><div class="diff-content">${s}</div></div>`,E.querySelector(".diff-back")?.addEventListener("click",()=>{E.innerHTML=Et,Ve(),g&&E.querySelectorAll(".git-popover-item").forEach(r=>{let c=parseInt(r.dataset.idx||"0",10),l=g.files[c];!l||l.status==="D"||(r.style.cursor="pointer",r.addEventListener("click",()=>Ct(l.path)))}),Lt()})}function Mn(e,t,n){if(!t&&!n){Ie.textContent="No diff",Be.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:s,lineCount:o}=xt(t,n);bt.textContent=e,Ie.textContent=o+" lines",Be.innerHTML=s}function Tn(){wt.style.display="none",He?(I.style.display="",I.classList.add("terminal-pending"),de(He),He=null):y?(I.style.display="",I.classList.add("terminal-pending"),Tt()):U.style.display="flex"}kn.addEventListener("click",Tn);function Ae(){E.classList.remove("open"),De.classList.remove("open")}De.addEventListener("click",Ae),document.addEventListener("keydown",e=>{e.key==="Escape"&&(Ae(),Ne())});async function Mt(e){try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(n.ok){let s=await n.json();g=s,pe(s);return}}}catch{}g=null,pe(null)}var Re=null;function Tt(){$e(),y&&(Mt(y),Re=setInterval(()=>{y&&Mt(y)},3e4))}function $e(){Re&&(clearInterval(Re),Re=null)}function Sn(){if(Se=le.fsRoots||[],Se.length===0){if(y){In(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 Se){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",()=>_(e)),m.appendChild(t)}}async function In(e){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch(T("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?_(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 _(e){P=e,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=Ln(e);try{let n=await(await fetch(T("/api/fs/list?path="+encodeURIComponent(e)))).json();await t,m.innerHTML="",pe(g),m.innerHTML+=En();let s=Bn(e);if(s&&!Se.some(r=>e===r)){let r=document.createElement("div");r.className="file-tree-item",r.innerHTML="..",r.addEventListener("click",()=>_(s)),m.appendChild(r)}if(n.dirs)for(let r of n.dirs){let c=r.split("/").pop()||r.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${h(c)}`,l.addEventListener("click",()=>_(r)),m.appendChild(l)}else m.innerHTML="";let o=n.files||[];for(let r of o){let c=r.split("/").pop()||r.split("\\").pop(),l=bn(r),u="",p="";if(l)switch(l){case"M":u="git-mod",p='<span class="git-file-status M">M</span>';break;case"A":u="git-add",p='<span class="git-file-status A">A</span>';break;case"D":u="git-del",p='<span class="git-file-status D">D</span>';break;case"?":u="git-untracked",p='<span class="git-file-status ?">?</span>';break;case"R":u="git-mod",p='<span class="git-file-status R">R</span>';break}let S=document.createElement("div");S.className="file-tree-item file"+(u?" "+u:""),S.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${p}${h(c)}`,S.addEventListener("click",()=>Dn(r)),m.appendChild(S)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function Bn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Dn(e){Me=e,I.style.display="none",v&&($e(),v.destroy(),v=null,y=null),pe(null),Oe(),U.style.display="none",re.style.display="flex",dn.textContent=e,D.textContent="Loading...",Y.style.display="none";try{let t=await fetch(T("/api/file?path="+encodeURIComponent(e)));if(!t.ok){let s=await t.json();D.textContent=s.error||"Failed to load",ce.value="";return}let n=await t.json();ce.value=n.content,Te=n.content,dt=n.content,D.textContent=n.size+" bytes",Y.style.display=""}catch{D.textContent="Failed to load",ce.value=""}}ce.addEventListener("input",()=>{Te=ce.value}),Y.addEventListener("click",async()=>{Y.textContent="Saving...",Y.disabled=!0;try{let e=await fetch(T("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Me,content:Te})});if(!e.ok){let t=await e.json();D.textContent=t.error||"Save failed";return}dt=Te,D.textContent="Saved"}catch{D.textContent="Save failed"}finally{Y.disabled=!1,Y.textContent="Save"}}),un.addEventListener("click",async()=>{if(confirm("Delete "+Me+"?"))try{let e=await fetch(T("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:Me})});if(!e.ok){let t=await e.json();D.textContent=t.error||"Delete failed";return}re.style.display="none",U.style.display="flex",P&&_(P)}catch{D.textContent="Delete failed"}}),pn.addEventListener("click",()=>{re.style.display="none",U.style.display="flex",P&&_(P)}),fn.addEventListener("click",async()=>{let e=ut.value.trim();if(!e)return;let t=P+"/"+e;try{let n=await fetch(T("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){let s=await n.json();D.textContent=s.error||"Create failed";return}ut.value="",P&&_(P)}catch{D.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=pt.classList.toggle("open");qe.classList.toggle("open",e)}),qe.addEventListener("click",()=>{pt.classList.remove("open"),qe.classList.remove("open")});var q="",fe=document.createElement("div");fe.className="session-popover-backdrop",document.body.appendChild(fe);var $=document.createElement("div");$.id="session-popover",$.className="session-popover",$.style.display="none",$.innerHTML=`<div class="sp-field">
|
|
36
36
|
<label>Rename session</label>
|
|
37
37
|
<input type="text" id="sp-name" />
|
|
38
38
|
</div>
|
|
@@ -41,5 +41,5 @@ var an=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function
|
|
|
41
41
|
<button class="btn" id="sp-cancel">Cancel</button>
|
|
42
42
|
</div>
|
|
43
43
|
<hr class="sp-divider" />
|
|
44
|
-
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild($);function Hn(e){let t=e.getBoundingClientRect();$.style.top=t.bottom+4+"px",$.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function An(e,t){q=e;let n=document.getElementById("sp-name");n.value=e,fe.classList.add("open"),$.style.display="block",Hn(t),setTimeout(()=>n.focus(),50)}function me(){fe.classList.remove("open"),$.style.display="none"}fe.addEventListener("click",me),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===q){me();return}try{if(!(await fetch(T("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:q,newName:e})})).ok)return;me(),X(),y===q&&de(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+q+'"?'))try{if(!(await fetch(T("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:q})})).ok)return;me(),y===q&&(v&&(v.destroy(),v=null),y=null,I.style.display="none",U.style.display="flex"),X()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",me);var Rn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!v)return;let n=Rn[t.dataset.key];n&&(v.sendInput(n),v.focus())});var A=document.getElementById("mk-input"),
|
|
45
|
-
`).length;A.rows=Math.min(e,4)}),A.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),
|
|
44
|
+
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild($);function Hn(e){let t=e.getBoundingClientRect();$.style.top=t.bottom+4+"px",$.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function An(e,t){q=e;let n=document.getElementById("sp-name");n.value=e,fe.classList.add("open"),$.style.display="block",Hn(t),setTimeout(()=>n.focus(),50)}function me(){fe.classList.remove("open"),$.style.display="none"}fe.addEventListener("click",me),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===q){me();return}try{if(!(await fetch(T("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:q,newName:e})})).ok)return;me(),X(),y===q&&de(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+q+'"?'))try{if(!(await fetch(T("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:q})})).ok)return;me(),y===q&&(v&&(v.destroy(),v=null),y=null,I.style.display="none",U.style.display="flex"),X()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",me);var Rn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!v)return;let n=Rn[t.dataset.key];n&&(v.sendInput(n),v.focus())});var A=document.getElementById("mk-input"),St=document.getElementById("mk-send");function It(){if(!A||!v)return;let e=A.value;e&&(v.sendInput(e+"\r"),A.value="",A.rows=1,v.focus())}if(A&&(A.addEventListener("input",()=>{A.rows=1;let e=A.value.split(`
|
|
45
|
+
`).length;A.rows=Math.min(e,4)}),A.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),It())})),St&&St.addEventListener("click",It),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 Je=document.getElementById("process-panel"),he=document.getElementById("proc-list"),$n=document.getElementById("proc-close"),Xe=document.getElementById("proc-backdrop"),Nn=document.getElementById("header-ram");function Pn(){he.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(T("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){he.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(let n of e){let s=n.rss,o=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";t+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+h(n.command)+'">'+h(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">×</button></div>'}he.innerHTML=t;for(let n of he.querySelectorAll(".proc-row")){let s=n.querySelector(".proc-kill");s&&s.addEventListener("click",o=>{let r=o.target.closest(".proc-row");!r||!confirm("Kill PID "+r.dataset.pid+"?")||fetch(T("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(r.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?r.style.opacity="0.3":alert("Failed: "+(c.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{he.innerHTML='<div class="proc-empty">Failed to load</div>'})}function _n(){Je.classList.add("open"),Xe.classList.add("open"),Pn()}function Ne(){Je.classList.remove("open"),Xe.classList.remove("open")}Nn.addEventListener("click",e=>{e.stopPropagation(),Je.classList.contains("open")?Ne():_n()}),$n.addEventListener("click",Ne),Xe.addEventListener("click",Ne);var jn=2e3,ve=null,Ze=null,Pe=new Map,_e=new Map;function zn(e,t){return(t?"a:"+t+":":"l:")+e}function Wn(e,t,n){let s=zn(e,t),o=_e.get(s);_e.set(s,{session:e,agentId:t,state:n,doneUntil:n==="idle"&&o?.state==="working"?Date.now()+jn:void 0});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&&Bt(c,s,_e.get(s))}function Bt(e,t,n){let s=Pe.get(t);s&&(clearTimeout(s),Pe.delete(t)),e.classList.remove("working","just-done"),n.state==="working"?e.classList.add("working"):n.doneUntil&&Date.now()<n.doneUntil&&(e.classList.add("just-done"),Pe.set(t,setTimeout(()=>{e.classList.remove("just-done"),Pe.delete(t)},Math.max(0,n.doneUntil-Date.now()))))}function Ge(){for(let[e,t]of _e){let n=t.agentId?'.session-item[data-agent="'+CSS.escape(t.agentId)+'"][data-session="'+CSS.escape(t.session)+'"]':'.session-item[data-session="'+CSS.escape(t.session)+'"]:not([data-agent])',s=m.querySelector(n);s&&Bt(s,e,t)}}function Dt(){if(ve||Ze)return;let e=localStorage.getItem("tmux-web-token"),t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{let n=new WebSocket(t);ve=n,n.onmessage=s=>{try{let o=JSON.parse(String(s.data));o&&o.type==="session.activity"&&typeof o.session=="string"&&Wn(o.session,o.agentId||void 0,o.state==="working"?"working":"idle")}catch{}},n.onclose=()=>{ve===n&&(ve=null,Ze=setTimeout(()=>{Ze=null,Dt()},5e3))},n.onerror=()=>{try{n.close()}catch{}}}catch{ve=null}}window.__openSession=de,window.__refreshSidebar=X,X(),Oe(),Ge(),Dt(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&Ge()}),setInterval(()=>{document.visibilityState==="visible"&&X()},1e4);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{initTerminal as $e}from"./terminal-core.js";const H=window.__TMUX_WEB_SHELL__;let f=null,m=null;const
|
|
1
|
+
import{initTerminal as $e}from"./terminal-core.js";const H=window.__TMUX_WEB_SHELL__;let f=null,m=null;const de=typeof window.__TMUX_WEB_SCOPE__?.agentId=="string"?window.__TMUX_WEB_SCOPE__.agentId:null;let g=de;window.__tmuxWebScopeAgent=de;let ee="sessions",S="",F="",O="",pe="";function y(e){return g?"/a/"+encodeURIComponent(g)+e:e}const r=document.getElementById("sidebar-content"),I=document.getElementById("main-placeholder"),h=document.getElementById("terminal-container"),R=document.getElementById("file-editor"),Ne=document.getElementById("fe-path"),w=document.getElementById("fe-status"),$=document.getElementById("fe-content"),x=document.getElementById("fe-save"),_e=document.getElementById("fe-delete"),Ae=document.getElementById("fe-back"),fe=document.getElementById("fe-new-name"),De=document.getElementById("fe-new-btn"),te=document.getElementById("settings-backdrop"),ue=document.getElementById("settings-popover"),me=document.querySelector(".sidebar");function Pe(){return window.matchMedia("(max-width: 640px)").matches}function ne(){Pe()&&me.classList.add("collapsed")}function ve(){me.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{ve(),ge("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{ve(),ge("files")});function ge(e){ee=e,document.querySelectorAll(".mode-btn").forEach(t=>t.classList.remove("active"));const n=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");n&&n.classList.add("active"),e==="sessions"?(se="",M()):Ze()}let se="";async function M(){if(ee!=="sessions")return;try{const t=await(await fetch("/api/sidebar/sessions")).json(),s=JSON.stringify({recent:t.recent,agents:t.agents});if(s===se)return;se=s,Ue(t)}catch{r.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>{const n=window.__openNewSessionModal;typeof n=="function"?n():document.getElementById("new-session-modal")?.classList.add("open")}),ce()}const ye="tmux-web-sidebar-group-collapsed";function he(){try{return new Set(JSON.parse(localStorage.getItem(ye)||"[]"))}catch{return new Set}}function je(e){try{localStorage.setItem(ye,JSON.stringify([...e]))}catch{}}function We(e,n,t){if(!e)return"\u2026";const s=t&&(e===t||e.startsWith(t+"/"))?"~"+e.slice(t.length):e,i=s.split("/").filter(Boolean);if(i.length===0)return e;for(let a=1;a<=i.length;a++){const o=i.slice(-a).join("/");if(!n.some(l=>!l||l===e?!1:(t&&(l===t||l.startsWith(t+"/"))?"~"+l.slice(t.length):l).split("/").filter(Boolean).slice(-a).join("/")===o))return o}return s}function Fe(e,n){const t=new Map;for(const a of e){const o=a.path||"";t.has(o)||t.set(o,[]),t.get(o).push(a)}const s=[...t.keys()],i=he();for(const[a,o]of t){const c=We(a,s,n),l=i.has(a),p=document.createElement("div");p.className="session-group-header"+(l?" collapsed":""),p.title=a||"No working directory",p.innerHTML=`<span class="sg-folder">📁</span><span class="sg-label">${d(c)}</span>`,p.addEventListener("click",()=>{const b=he(),re=p.nextElementSibling;b.has(a)?(b.delete(a),p.classList.remove("collapsed"),re?.classList.remove("collapsed")):(b.add(a),p.classList.add("collapsed"),re?.classList.add("collapsed")),je(b)});const L=document.createElement("div");L.className="session-group-body"+(l?" collapsed":"");for(const b of o)L.appendChild(Oe(b));r.appendChild(p),r.appendChild(L)}}function Oe(e){const n=document.createElement("div");n.className="session-item"+(e.name===m&&!g?" active":""),n.setAttribute("data-session",e.name),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>
|
|
2
2
|
<span>${d(e.name)}</span>
|
|
3
3
|
<button class="session-edit-btn" data-session="${d(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>
|
|
4
4
|
`,n.addEventListener("click",s=>{s.target.closest(".session-edit-btn")||N(e.name)});const t=n.querySelector(".session-edit-btn");return t&&t.addEventListener("click",s=>{s.stopPropagation(),st(e.name,t)}),n}function Ue(e){r.innerHTML='<button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';const n=e.recent||[],t=document.createElement("div");t.className="sidebar-section-label"+(g?" clickable":""),t.textContent=g?"\u2190 \u8FD4\u56DE\u672C\u673A":"\u672C\u673A",g&&(t.title="Back to hub",t.addEventListener("click",()=>{window.location.href="/"})),r.appendChild(t),Fe(n,e.home);const s=e.agents||[];for(const i of s){if(!i.sessions||i.sessions.length===0)continue;const a=i.agentId===g,o=document.createElement("div");o.className="sidebar-section-label machine-label"+(i.online?"":" agent-offline-label")+(!a&&i.online?" clickable":""),o.textContent=i.agentName+(i.online?"":" (offline)"),!a&&i.online&&(o.title="\u6253\u5F00 "+i.agentName,o.addEventListener("click",()=>{window.location.href="/a/"+encodeURIComponent(i.agentId)+"/"})),r.appendChild(o);for(const c of i.sessions){const l=document.createElement("div");l.className="session-item"+(c.name===m&&g===i.agentId?" active":"")+(i.online?"":" session-offline"),l.setAttribute("data-session",c.name),l.setAttribute("data-agent",i.agentId),l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${c.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
|
|
5
|
-
<span>${d(c.name)}</span><span class="meta">${i.online?"":"offline"}</span>`,i.online&&l.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||N(c.name,i.agentId)}),r.appendChild(l)}}}function d(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function N(e,n){if(!(m===e&&f&&g===(n||null))){R.style.display="none",I.style.display="none",h.style.display="",h.classList.add("terminal-pending"),ne(),f&&(X(),f.destroy(),f=null),m=e,g=n||null,window.__tmuxWebScopeAgent=g,window.__tmuxWebCurrentSession=m;for(const t of r.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e&&(t.getAttribute("data-agent")||null)===g);try{f=await $e(h,e,{terminal:H.terminal,scrollback:H.scrollback,theme:H.theme,wsBase:g?"/ws/a/"+encodeURIComponent(g):H.wsBase||"/ws"}),
|
|
5
|
+
<span>${d(c.name)}</span><span class="meta">${i.online?"":"offline"}</span>`,i.online&&l.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||N(c.name,i.agentId)}),r.appendChild(l)}}}function d(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function N(e,n){if(!(m===e&&f&&g===(n||null))){R.style.display="none",I.style.display="none",h.style.display="",h.classList.add("terminal-pending"),ne(),f&&(X(),f.destroy(),f=null),m=e,g=n||null,window.__tmuxWebScopeAgent=g,window.__tmuxWebCurrentSession=m;for(const t of r.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e&&(t.getAttribute("data-agent")||null)===g);try{f=await $e(h,e,{terminal:H.terminal,scrollback:H.scrollback,theme:H.theme,wsBase:g?"/ws/a/"+encodeURIComponent(g):H.wsBase||"/ws"}),Be()}catch(t){console.error("[shell] terminal init failed:",t);const s=t&&typeof t=="object"&&"message"in t?t.message:String(t);h.textContent="Failed to open terminal for "+e+": "+s,h.classList.remove("terminal-pending")}}}let U=[],u=null,be=!1;function Ge(e){if(!u||!u.files.length)return null;const n=u.repoRoot;if(!n||!e.startsWith(n))return null;const t=e.slice(n.length+1);for(const s of u.files)if(s.path===t)return s.status;return null}async function ze(e){be=!0;try{const n=await fetch(y("/api/git/status?path="+encodeURIComponent(e)));n.ok?u=await n.json():u=null}catch{u=null}finally{be=!1}}function qe(){if(!u||!u.branch)return"";const{branch:e,linesAdded:n,linesRemoved:t}=u;let s=`<div class="sidebar-git"><div class="git-branch-badge">
|
|
6
6
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M11.63 1.22a.75.75 0 00-1.06 0L5.99 5.8 4.12 3.93a.75.75 0 10-1.06 1.06L4.93 6.87A3.5 3.5 0 003 10.09v3.16a.75.75 0 001 0v-3.16a2.5 2.5 0 012.5-2.5h.27l-1.06 1.06a.75.75 0 001.06 1.06l2.37-2.38a.75.75 0 000-1.06L7.59 5.27l4.04-4.05a.75.75 0 000-1.06L11.63 1.22zM3.25 13.94a.75.75 0 01-.75-.75v-.69a.75.75 0 011.5 0v.69c0 .414-.336.75-.75.75z"/></svg>
|
|
7
|
-
${d(e)}</div>`;return(n>0||t>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${n}</span> <span class="del">-${t}</span></div>`),s+"</div>"}r.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&T&&u&&(e.stopPropagation(),
|
|
7
|
+
${d(e)}</div>`;return(n>0||t>0)&&(s+=`<div class="git-diff-stats">+<span class="add">${n}</span> <span class="del">-${t}</span></div>`),s+"</div>"}r.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&T&&u&&(e.stopPropagation(),Se())});const _=document.getElementById("header-git"),we=document.getElementById("git-diff-view"),Le=document.getElementById("gd-path"),G=document.getElementById("gd-status"),z=document.getElementById("gd-content"),Ve=document.getElementById("gd-back");let T=null;function A(e){if(!e||!e.branch){_.style.display="none",T=null;return}T=e.repoRoot;const{branch:n,linesAdded:t,linesRemoved:s}=e;let i=`<span class="branch">${d(n)}</span>`;(t>0||s>0)&&(i+=`<span class="sep">\xB7</span>+<span class="diff-add">${t}</span> <span class="diff-del">-${s}</span>`),_.innerHTML=i,_.style.display="block"}_.addEventListener("click",e=>{T&&u&&(e.stopPropagation(),Se())});const v=document.getElementById("git-popover"),q=document.getElementById("git-popover-backdrop");function ie(){const e=_.getBoundingClientRect();v.style.top=e.bottom+4+"px",v.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function Ee(){const e=T,n=document.getElementById("git-popover-browse");n&&n.addEventListener("click",()=>{if(J(),e){ee="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));const t=document.getElementById("mode-files");t&&t.classList.add("active"),C(e)}})}let ke="";function Se(){if(!u)return;const e=u.files||[];if(!e.length)v.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
|
|
8
8
|
<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let n=`<div class="git-popover-header">${e.length} file${e.length>1?"s":""} changed</div>`;for(let t=0;t<e.length;t++){const s=e[t],i=s.additions||0,a=s.deletions||0;let o="";switch(s.status){case"M":o='<span class="git-file-status M">M</span>';break;case"A":o='<span class="git-file-status A">A</span>';break;case"D":o='<span class="git-file-status D">D</span>';break;case"?":o='<span class="git-file-status ?">?</span>';break;case"R":o='<span class="git-file-status R">R</span>';break}n+=`<div class="git-popover-item" data-idx="${t}">
|
|
9
9
|
${o}<span class="file-path">${d(s.path)}</span>
|
|
10
10
|
<span class="file-add">${i>0?"+"+i:""}</span>
|
|
11
11
|
<span class="file-del">${a>0?"-"+a:""}</span>
|
|
12
|
-
</div>`}n+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',
|
|
12
|
+
</div>`}n+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',ke=n,v.innerHTML=n,v.querySelectorAll(".git-popover-item").forEach(t=>{const s=parseInt(t.dataset.idx||"0",10),i=e[s];!i||i.status==="D"||(t.classList.add("clickable"),t.style.cursor="pointer",t.addEventListener("click",()=>Ie(i.path)))})}ie(),v.classList.add("open"),q.classList.add("open"),Ee()}function Je(){return window.innerWidth>=1024}function Ce(e,n){const s=(e+(n&&e?`
|
|
13
13
|
`:"")+n).split(`
|
|
14
|
-
`);let i="",a=0,o=0;for(const c of s){const l=c;if(l.startsWith("@@")){const p=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);p&&(a=parseInt(p[1],10)-1,o=parseInt(p[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${d(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${d(l)}</span></div>`);continue}if(l.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${d(l)}</span></div>`;else if(l.startsWith("-"))a++,i+=`<div class="diff-line del"><span class="ln-no">${a}</span><span class="ln-body">${d(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${d(l)}</span></div>`;else{if(!l&&!a&&!o)continue;a++,o++,i+=`<div class="diff-line"><span class="ln-no">${a}</span><span class="ln-no" style="margin-right:12px">${o}</span><span class="ln-body">${d(l)}</span></div>`}}return{html:i,lineCount:s.length}}let V=null;async function
|
|
14
|
+
`);let i="",a=0,o=0;for(const c of s){const l=c;if(l.startsWith("@@")){const p=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);p&&(a=parseInt(p[1],10)-1,o=parseInt(p[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${d(l)}</span></div>`;continue}if(l.startsWith("diff --git")||l.startsWith("index ")||l.startsWith("--- ")||l.startsWith("+++ ")||l.startsWith("new file")||l.startsWith("deleted file")){l.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${d(l)}</span></div>`);continue}if(l.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${d(l)}</span></div>`;else if(l.startsWith("-"))a++,i+=`<div class="diff-line del"><span class="ln-no">${a}</span><span class="ln-body">${d(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${d(l)}</span></div>`;else{if(!l&&!a&&!o)continue;a++,o++,i+=`<div class="diff-line"><span class="ln-no">${a}</span><span class="ln-no" style="margin-right:12px">${o}</span><span class="ln-body">${d(l)}</span></div>`}}return{html:i,lineCount:s.length}}let V=null;async function Ie(e){const n=T;if(n)if(J(),Je()){h.style.display="none",f&&(X(),f.destroy(),f=null,V=m,m=null),A(null),R.style.display="none",I.style.display="none",we.style.display="flex",Le.textContent=e,G.textContent="Loading\u2026",z.innerHTML="";try{const t=await fetch(y("/api/git/diff?path="+encodeURIComponent(n)+"&file="+encodeURIComponent(e)));if(!t.ok)throw new Error(await t.text());const s=await t.json();Xe(e,s.diff,s.stagedDiff)}catch(t){const s=t instanceof Error?t.message:String(t);G.textContent="Error",z.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${d(s)}</div>`}}else{v.innerHTML=`<div class="git-diff-view"><div class="diff-header">
|
|
15
15
|
<button class="diff-back">\u2190 Back</button>
|
|
16
16
|
<span class="diff-filename">${d(e)}</span>
|
|
17
17
|
<span class="diff-loading">Loading\u2026</span>
|
|
@@ -25,7 +25,7 @@ import{initTerminal as $e}from"./terminal-core.js";const H=window.__TMUX_WEB_SHE
|
|
|
25
25
|
<button class="diff-back">\u2190 Back</button>
|
|
26
26
|
<span class="diff-filename">${d(e)}</span>
|
|
27
27
|
<span class="diff-filesize">${i} lines</span>
|
|
28
|
-
</div><div class="diff-content">${s}</div></div>`,v.querySelector(".diff-back")?.addEventListener("click",()=>{v.innerHTML=
|
|
28
|
+
</div><div class="diff-content">${s}</div></div>`,v.querySelector(".diff-back")?.addEventListener("click",()=>{v.innerHTML=ke,ie(),u&&v.querySelectorAll(".git-popover-item").forEach(a=>{const o=parseInt(a.dataset.idx||"0",10),c=u.files[o];!c||c.status==="D"||(a.style.cursor="pointer",a.addEventListener("click",()=>Ie(c.path)))}),Ee()})}function Xe(e,n,t){if(!n&&!t){G.textContent="No diff",z.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}const{html:s,lineCount:i}=Ce(n,t);Le.textContent=e,G.textContent=i+" lines",z.innerHTML=s}function Ye(){we.style.display="none",V?(h.style.display="",h.classList.add("terminal-pending"),N(V),V=null):m?(h.style.display="",h.classList.add("terminal-pending"),Be()):I.style.display="flex"}Ve.addEventListener("click",Ye);function J(){v.classList.remove("open"),q.classList.remove("open")}q.addEventListener("click",J),document.addEventListener("keydown",e=>{e.key==="Escape"&&(J(),Y())});async function xe(e){try{const t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(t.path){const s=await fetch("/api/git/status?path="+encodeURIComponent(t.path));if(s.ok){const i=await s.json();u=i,A(i);return}}}catch{}u=null,A(null)}let K=null;function Be(){X(),m&&(xe(m),K=setInterval(()=>{m&&xe(m)},3e4))}function X(){K&&(clearInterval(K),K=null)}function Ze(){if(U=H.fsRoots||[],U.length===0){if(m){Qe(m);return}r.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}r.innerHTML="";for(const e of U){const n=document.createElement("div");n.className="file-tree-item",n.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${d(e)}`,n.addEventListener("click",()=>C(e)),r.appendChild(n)}}async function Qe(e){r.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const t=await(await fetch(y("/api/fs/session-path?session="+encodeURIComponent(e)))).json();t.path?C(t.path):r.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{r.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function C(e){S=e,r.innerHTML='<div class="file-tree-empty">Loading...</div>';const n=ze(e);try{const s=await(await fetch(y("/api/fs/list?path="+encodeURIComponent(e)))).json();await n,r.innerHTML="",A(u),r.innerHTML+=qe();const i=et(e);if(i&&!U.some(c=>e===c)){const c=document.createElement("div");c.className="file-tree-item",c.innerHTML="..",c.addEventListener("click",()=>C(i)),r.appendChild(c)}if(s.dirs)for(const o of s.dirs){const c=o.split("/").pop()||o.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${d(c)}`,l.addEventListener("click",()=>C(o)),r.appendChild(l)}else r.innerHTML="";const a=s.files||[];for(const o of a){const c=o.split("/").pop()||o.split("\\").pop(),l=Ge(o);let p="",L="";if(l)switch(l){case"M":p="git-mod",L='<span class="git-file-status M">M</span>';break;case"A":p="git-add",L='<span class="git-file-status A">A</span>';break;case"D":p="git-del",L='<span class="git-file-status D">D</span>';break;case"?":p="git-untracked",L='<span class="git-file-status ?">?</span>';break;case"R":p="git-mod",L='<span class="git-file-status R">R</span>';break}const b=document.createElement("div");b.className="file-tree-item file"+(p?" "+p:""),b.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${L}${d(c)}`,b.addEventListener("click",()=>tt(o)),r.appendChild(b)}}catch{r.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function et(e){const n=e.lastIndexOf("/");return n<=0?null:e.substring(0,n)}async function tt(e){F=e,h.style.display="none",f&&(X(),f.destroy(),f=null,m=null),A(null),ne(),I.style.display="none",R.style.display="flex",Ne.textContent=e,w.textContent="Loading...",x.style.display="none";try{const n=await fetch(y("/api/file?path="+encodeURIComponent(e)));if(!n.ok){const s=await n.json();w.textContent=s.error||"Failed to load",$.value="";return}const t=await n.json();$.value=t.content,O=t.content,pe=t.content,w.textContent=t.size+" bytes",x.style.display=""}catch{w.textContent="Failed to load",$.value=""}}$.addEventListener("input",()=>{O=$.value}),x.addEventListener("click",async()=>{x.textContent="Saving...",x.disabled=!0;try{const e=await fetch(y("/api/file"),{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:F,content:O})});if(!e.ok){const n=await e.json();w.textContent=n.error||"Save failed";return}pe=O,w.textContent="Saved"}catch{w.textContent="Save failed"}finally{x.disabled=!1,x.textContent="Save"}}),_e.addEventListener("click",async()=>{if(confirm("Delete "+F+"?"))try{const e=await fetch(y("/api/file/delete"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:F})});if(!e.ok){const n=await e.json();w.textContent=n.error||"Delete failed";return}R.style.display="none",I.style.display="flex",S&&C(S)}catch{w.textContent="Delete failed"}}),Ae.addEventListener("click",()=>{R.style.display="none",I.style.display="flex",S&&C(S)}),De.addEventListener("click",async()=>{const e=fe.value.trim();if(!e)return;const n=S+"/"+e;try{const t=await fetch(y("/api/file/touch"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:n})});if(!t.ok){const s=await t.json();w.textContent=s.error||"Create failed";return}fe.value="",S&&C(S)}catch{w.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=ue.classList.toggle("open");te.classList.toggle("open",e)}),te.addEventListener("click",()=>{ue.classList.remove("open"),te.classList.remove("open")});let B="";const D=document.createElement("div");D.className="session-popover-backdrop",document.body.appendChild(D);const k=document.createElement("div");k.id="session-popover",k.className="session-popover",k.style.display="none",k.innerHTML=`<div class="sp-field">
|
|
29
29
|
<label>Rename session</label>
|
|
30
30
|
<input type="text" id="sp-name" />
|
|
31
31
|
</div>
|
|
@@ -34,5 +34,5 @@ import{initTerminal as $e}from"./terminal-core.js";const H=window.__TMUX_WEB_SHE
|
|
|
34
34
|
<button class="btn" id="sp-cancel">Cancel</button>
|
|
35
35
|
</div>
|
|
36
36
|
<hr class="sp-divider" />
|
|
37
|
-
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(k);function nt(e){const n=e.getBoundingClientRect();k.style.top=n.bottom+4+"px",k.style.left=Math.max(8,Math.min(n.left,window.innerWidth-240))+"px"}function st(e,n){B=e;const t=document.getElementById("sp-name");t.value=e,D.classList.add("open"),k.style.display="block",nt(n),setTimeout(()=>t.focus(),50)}function P(){D.classList.remove("open"),k.style.display="none"}D.addEventListener("click",P),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===B){P();return}try{if(!(await fetch(y("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:B,newName:e})})).ok)return;P(),M(),m===B&&N(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+B+'"?'))try{if(!(await fetch(y("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B})})).ok)return;P(),m===B&&(f&&(f.destroy(),f=null),m=null,h.style.display="none",I.style.display="flex"),M()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",P);const it={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const n=e.target.closest("button[data-key]");if(!n||!f)return;const t=it[n.dataset.key];t&&(f.sendInput(t),f.focus())});const E=document.getElementById("mk-input"),
|
|
38
|
-
`).length;E.rows=Math.min(e,4)}),E.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),
|
|
37
|
+
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(k);function nt(e){const n=e.getBoundingClientRect();k.style.top=n.bottom+4+"px",k.style.left=Math.max(8,Math.min(n.left,window.innerWidth-240))+"px"}function st(e,n){B=e;const t=document.getElementById("sp-name");t.value=e,D.classList.add("open"),k.style.display="block",nt(n),setTimeout(()=>t.focus(),50)}function P(){D.classList.remove("open"),k.style.display="none"}D.addEventListener("click",P),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===B){P();return}try{if(!(await fetch(y("/api/sessions/rename"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:B,newName:e})})).ok)return;P(),M(),m===B&&N(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+B+'"?'))try{if(!(await fetch(y("/api/sessions/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B})})).ok)return;P(),m===B&&(f&&(f.destroy(),f=null),m=null,h.style.display="none",I.style.display="flex"),M()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",P);const it={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r","ctrl-r":"","ctrl-c":"",btw:"/btw ",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const n=e.target.closest("button[data-key]");if(!n||!f)return;const t=it[n.dataset.key];t&&(f.sendInput(t),f.focus())});const E=document.getElementById("mk-input"),Me=document.getElementById("mk-send");function Te(){if(!E||!f)return;const e=E.value;e&&(f.sendInput(e+"\r"),E.value="",E.rows=1,f.focus())}if(E&&(E.addEventListener("input",()=>{E.rows=1;const e=E.value.split(`
|
|
38
|
+
`).length;E.rows=Math.min(e,4)}),E.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),Te())})),Me&&Me.addEventListener("click",Te),window.visualViewport){const e=document.querySelector(".app-layout"),n=()=>{e&&(e.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",n),window.addEventListener("orientationchange",()=>setTimeout(n,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}const oe=document.getElementById("process-panel"),j=document.getElementById("proc-list"),ot=document.getElementById("proc-close"),ae=document.getElementById("proc-backdrop"),at=document.getElementById("header-ram");function lt(){j.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch(y("/api/system/processes")).then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){j.innerHTML='<div class="proc-empty">No processes</div>';return}let n="";for(const t of e){const s=t.rss,i=s<1048576?(s/1024).toFixed(0)+"K":(s/1048576).toFixed(1)+"M";n+='<div class="proc-row" data-pid="'+t.pid+'"><span class="proc-mem">'+t.mem+'%</span><span class="proc-rss">'+i+'</span><span class="proc-cmd" title="'+d(t.command)+'">'+d(t.command)+'</span><button class="proc-kill" title="Kill PID '+t.pid+'">×</button></div>'}j.innerHTML=n;for(const t of j.querySelectorAll(".proc-row")){const s=t.querySelector(".proc-kill");s&&s.addEventListener("click",i=>{const a=i.target.closest(".proc-row");!a||!confirm("Kill PID "+a.dataset.pid+"?")||fetch(y("/api/system/kill"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(a.dataset.pid,10)})}).then(o=>o.json()).then(o=>{o.ok?a.style.opacity="0.3":alert("Failed: "+(o.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{j.innerHTML='<div class="proc-empty">Failed to load</div>'})}function ct(){oe.classList.add("open"),ae.classList.add("open"),lt()}function Y(){oe.classList.remove("open"),ae.classList.remove("open")}at.addEventListener("click",e=>{e.stopPropagation(),oe.classList.contains("open")?Y():ct()}),ot.addEventListener("click",Y),ae.addEventListener("click",Y);const rt=2e3;let W=null,le=null;const Z=new Map,Q=new Map;function dt(e,n){return(n?"a:"+n+":":"l:")+e}function pt(e,n,t){const s=dt(e,n),i=Q.get(s);Q.set(s,{session:e,agentId:n,state:t,doneUntil:t==="idle"&&i?.state==="working"?Date.now()+rt:void 0});const a=n?'.session-item[data-agent="'+CSS.escape(n)+'"][data-session="'+CSS.escape(e)+'"]':'.session-item[data-session="'+CSS.escape(e)+'"]:not([data-agent])',o=r.querySelector(a);o&&He(o,s,Q.get(s))}function He(e,n,t){const s=Z.get(n);s&&(clearTimeout(s),Z.delete(n)),e.classList.remove("working","just-done"),t.state==="working"?e.classList.add("working"):t.doneUntil&&Date.now()<t.doneUntil&&(e.classList.add("just-done"),Z.set(n,setTimeout(()=>{e.classList.remove("just-done"),Z.delete(n)},Math.max(0,t.doneUntil-Date.now()))))}function ce(){for(const[e,n]of Q){const t=n.agentId?'.session-item[data-agent="'+CSS.escape(n.agentId)+'"][data-session="'+CSS.escape(n.session)+'"]':'.session-item[data-session="'+CSS.escape(n.session)+'"]:not([data-agent])',s=r.querySelector(t);s&&He(s,e,n)}}function Re(){if(W||le)return;const e=localStorage.getItem("tmux-web-token");let t=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/activity";e&&(t+="?token="+encodeURIComponent(e));try{const s=new WebSocket(t);W=s,s.onmessage=i=>{try{const a=JSON.parse(String(i.data));a&&a.type==="session.activity"&&typeof a.session=="string"&&pt(a.session,a.agentId||void 0,a.state==="working"?"working":"idle")}catch{}},s.onclose=()=>{W===s&&(W=null,le=setTimeout(()=>{le=null,Re()},5e3))},s.onerror=()=>{try{s.close()}catch{}}}catch{W=null}}window.__openSession=N,window.__refreshSidebar=M,M(),ne(),ce(),Re(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&ce()}),setInterval(()=>{document.visibilityState==="visible"&&M()},1e4);
|