tmux-weblink 1.5.1 → 1.5.3

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,48 +1,35 @@
1
- var Ft="https://esm.sh/ghostty-web@0.4.0",Vt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function ue(e){return e.replace(Vt,"")}var pe;function Yt(){return pe||(document.querySelector("link[data-tmux-web-xterm-css]")?pe=Promise.resolve():(pe=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)}),pe))}var Ze=class Lt{terminal;fitAddon;container;constructor(t,n,s){this.container=t,this.terminal=n,this.fitAddon=s,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,n,s){let[{Terminal:l},{FitAddon:o}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Yt()]),c=new l({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:s});return new Lt(t,c,new o)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(t){return new Promise(n=>this.terminal.write(t,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(t){this.terminal.scrollToLine(Math.max(0,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=Ge(this.container);return t.width<=0||t.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(t){this.terminal.paste(t)}input(t){this.terminal.input(t,!1)}onData(t){this.terminal.onData(t),this.terminal.onBinary(t)}onResize(t){this.terminal.onResize(t)}onScroll(t){this.terminal.onScroll(t)}attachCustomKeyEventHandler(t){this.terminal.attachCustomKeyEventHandler(t)}isFocused(){let t=document.activeElement;return!!t&&(t===this.container||this.container.contains(t))}},Ot=class Et{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(t,n){this.container=t,this.terminal=n,this.terminal.open(t)}static async create(t,n,s){let l=await import(Ft);await l.init();let o=new l.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:s});return new Et(t,o)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(t){return new Promise(n=>this.terminal.write(t,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(t){this.terminal.scrollToLine?.(Math.max(0,t))}isNearScrollbackTop(){let t=this.terminal.buffer?.active;return t?t.viewportY>=t.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){let t=Ge(this.container);if(t.width<=0||t.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let n=Math.floor(t.width/this.charW),s=Math.floor(t.height/this.charH);if(n<10||s<5)return!1;if(n!==this.colsValue||s!==this.rowsValue){this.colsValue=n,this.rowsValue=s,this.terminal.resize(n,s);for(let l of this.resizeCallbacks)l({cols:n,rows:s})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(t){let n=this.terminal.getMode?.(2004)??!1;this.input(n?"\x1B[200~"+t+"\x1B[201~":t)}input(t){this.terminal.input?.(t,!1)}onData(t){this.terminal.onData(n=>t(n))}onResize(t){this.resizeCallbacks.push(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))}updateCellMetrics(t=!1){let n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let s=n.offsetWidth/this.colsValue,l=n.offsetHeight/this.rowsValue;(t||!this.charW||!this.charH)&&(this.charW=s,this.charH=l)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function Ge(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 Kt(e,t,n){return e.innerHTML="",e.classList.add("terminal-pending"),(async()=>{let s;if(n.renderer==="ghostty")try{s=await Ot.create(e,n.scrollback,n.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),s=await Ze.create(e,n.scrollback,n.theme)}else s=await Ze.create(e,n.scrollback,n.theme);let l,o=0,c,r=null,f=0,g="connecting",B=0,oe=!1,re=[],W="",Be=!1,O=1e3,kt=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(t),xt="/api/session/"+encodeURIComponent(t)+"/upload",Tt=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function It(){return re.join("")+W}async function Mt(i,a=0){let d=i?s.viewportY():0,E=i?s.baseY():0,T=ue(It());if(s.reset(),!T){i||s.scrollToBottom();return}await s.write(T),i?s.scrollToLine(E+d+a):s.scrollToBottom()}function K(i){l?.readyState===WebSocket.OPEN&&l.send(JSON.stringify(i))}function Bt(i){let a;try{a=JSON.parse(i)}catch{g==="live"&&(W+=i,s.write(ue(i)));return}if(a.type==="auth.required"){let d=localStorage.getItem("tmux-web-token");d?K({type:"auth.token",token:d}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(a.type==="auth.ok"){a.token&&localStorage.setItem("tmux-web-token",a.token),O=1e3,_(),K({type:"resize",cols:s.cols,rows:s.rows}),L();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(a.data);re=[d],W="",B=typeof a.lines=="number"?a.lines:n.terminal.initialLines,g="live",s.reset(),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"){oe=!1,a.lines>0&&a.data&&(re.unshift(a.data),B+=a.lines,Mt(!0,a.lines));return}if(a.type==="data"&&typeof a.data=="string"){let d=ue(a.data);if(g==="connecting"){g="live",W=d,s.write(d);return}g==="live"&&(W+=d,s.write(d))}}function je(){let i=kt,a=localStorage.getItem("tmux-web-token");a&&(i+=(i.includes("?")?"&":"?")+"token="+encodeURIComponent(a)),l=new WebSocket(i),l.onopen=()=>{g="connecting",B=0,oe=!1,re=[],W="",s.reset(),O=1e3},l.onmessage=d=>{typeof d.data=="string"&&Bt(d.data)},l.onclose=()=>{Be||(g="connecting",setTimeout(()=>{O=Math.min(O*2,1e4),Be||je()},O))},l.onerror=()=>l?.close()}function ce(i){K({type:"input",data:i})}function _(){return s.fit()}function Ce(){e.classList.remove("terminal-pending")}function L(){o&&cancelAnimationFrame(o),c&&clearTimeout(c),_(),o=requestAnimationFrame(()=>{o=0,_()&&Ce()}),c=setTimeout(()=>{_()&&Ce()},120)}function Ct(){L(),setTimeout(L,50),setTimeout(L,150),setTimeout(L,300)}function Ue(i){return typeof i=="string"&&i.startsWith("image/")}function Fe(i){return i?.files?.length?i.files[0]??null:null}function St(i){if(!i?.items)return null;for(let a=0;a<i.items.length;a++){let d=i.items[a];if(d?.kind==="file"&&Ue(d.type))return d.getAsFile()}return null}async function Ht(i){let a=new FormData,d=i instanceof File&&i.name?i.name:"upload";a.append("file",i,d);let E=localStorage.getItem("tmux-web-token"),T={};E&&(T.Authorization="Bearer "+E);let He=await fetch(xt,{method:"POST",body:a,headers:T});if(!He.ok){let Xe="upload failed";try{let Je=await He.json();Je.error&&(Xe=Je.error)}catch{}throw new Error(Xe)}let _e=await He.json();if(!_e.path)throw new Error("no path in response");return _e.path}async function Se(i){try{let a=await Ht(i);ce(a)}catch{}}s.onData(i=>ce(i)),s.onResize(({cols:i,rows:a})=>K({type:"resize",cols:i,rows:a})),s.onScroll(()=>{g!=="live"||oe||!s.isNearScrollbackTop()||(oe=!0,K({type:"load_history",before:B}))}),s.attachCustomKeyEventHandler(i=>i.type!=="keydown"?!0:(i.ctrlKey||i.metaKey)&&i.code==="KeyV"?(i.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let a=await navigator.clipboard.read();for(let d of a)for(let E of d.types)if(Ue(E)){let T=await d.getType(E);await Se(T);return}}}catch{}try{let a=await navigator.clipboard?.readText();a&&s.pasteText(Ve(a))}catch{}})(),!1):!0);let de=0;function Ve(i){return i.split(`\r
1
+ var At="https://esm.sh/ghostty-web@0.4.0",Yt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function oe(t){return t.replace(Yt,"")}var le;function Pt(){return le||(document.querySelector("link[data-tmux-web-xterm-css]")?le=Promise.resolve():(le=new Promise(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>t(),{once:!0}),e.addEventListener("error",()=>t(),{once:!0}),document.head.appendChild(e)}),le))}var Ke=class ut{terminal;fitAddon;container;constructor(e,n,i){this.container=e,this.terminal=n,this.fitAddon=i,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,n,i){let[{Terminal:o},{FitAddon:l}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),Pt()]),d=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new ut(e,d,new l)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=qe(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.terminal.rows-1)}getRowsText(e,n){let i=this.terminal.buffer.active,[o,l]=e<=n?[e,n]:[n,e],d=i.viewportY,r=[];for(let u=o;u<=l;u++){let f=i.getLine(d+u);r.push(f?f.translateToString(!0):"")}return r.join(`
2
+ `)}selectRows(e,n){let i=this.terminal.buffer.active,[o,l]=e<=n?[e,n]:[n,e];this.terminal.selectLines(i.viewportY+o,i.viewportY+l)}hasSelectionSupport(){return!0}},zt=class pt{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,n){this.container=e,this.terminal=n,this.terminal.open(e)}static async create(e,n,i){let o=await import(At);await o.init();let l=new o.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new pt(e,l)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){let e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){let e=qe(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let n=Math.floor(e.width/this.charW),i=Math.floor(e.height/this.charH);if(n<10||i<5)return!1;if(n!==this.colsValue||i!==this.rowsValue){this.colsValue=n,this.rowsValue=i,this.terminal.resize(n,i);for(let o of this.resizeCallbacks)o({cols:n,rows:i})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let n=this.terminal.getMode?.(2004)??!1;this.input(n?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(n=>e(n))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}getScreenText(){return this.getRowsText(0,this.rowsValue-1)}getRowsText(e,n){try{let i=this.terminal.buffer?.active;if(!i||typeof i.getLine!="function")return"";let[o,l]=e<=n?[e,n]:[n,e],d=typeof i.viewportY=="number"?i.viewportY:0,r=[];for(let u=o;u<=l;u++){let f=i.getLine(d+u);r.push(typeof f?.translateToString=="function"?f.translateToString(!0):"")}return r.join(`
3
+ `)}catch{return""}}selectRows(e,n){try{if(typeof this.terminal?.selectLines!="function")return;let i=this.terminal.buffer?.active,o=typeof i?.viewportY=="number"?i.viewportY:0,[l,d]=e<=n?[e,n]:[n,e];this.terminal.selectLines(o+l,o+d)}catch{}}hasSelectionSupport(){return typeof this.terminal?.selectLines=="function"}updateCellMetrics(e=!1){let n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let i=n.offsetWidth/this.colsValue,o=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=i,this.charH=o)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function qe(t){let e=t.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(e.width,n.width),height:Math.max(0,Math.min(e.height,n.height-Math.max(0,e.top)))}:e}function jt(t,e,n){return t.innerHTML="",t.classList.add("terminal-pending"),(async()=>{let i;if(n.renderer==="ghostty")try{i=await zt.create(t,n.scrollback,n.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),i=await Ke.create(t,n.scrollback,n.theme)}else i=await Ke.create(t,n.scrollback,n.theme);let o,l=0,d,r=null,u=0,f="connecting",C=0,ne=!1,ie=[],$="",A=!1,z=1e3,ft=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(e),mt="/api/session/"+encodeURIComponent(e)+"/upload",ht=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function vt(){return ie.join("")+$}async function yt(a,s=0){let c=a?i.viewportY():0,b=a?i.baseY():0,x=oe(vt());if(i.reset(),!x){a||i.scrollToBottom();return}await i.write(x),a?i.scrollToLine(b+c+s):i.scrollToBottom()}function j(a){o?.readyState===WebSocket.OPEN&&o.send(JSON.stringify(a))}function gt(a){let s;try{s=JSON.parse(a)}catch{f==="live"&&($+=a,i.write(oe(a)));return}if(s.type==="auth.required"){let c=localStorage.getItem("tmux-web-token");c?j({type:"auth.token",token:c}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(s.type==="auth.ok"){s.token&&localStorage.setItem("tmux-web-token",s.token),z=1e3,N(),j({type:"resize",cols:i.cols,rows:i.rows}),w();return}if(s.type==="auth.failed"){if(s.permanentLock||s.retryAfterMs&&s.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(s.message);return}return}if(s.type==="snapshot"&&typeof s.data=="string"){let c=oe(s.data);ie=[c],$="",C=typeof s.lines=="number"?s.lines:n.terminal.initialLines,f="live",i.reset(),i.write(c).then(()=>i.scrollToBottom());return}if(s.type==="window_changed"&&typeof s.activeIndex=="number"){let c=new URL(location.href);c.searchParams.get("window")!==String(s.activeIndex)&&(c.searchParams.set("window",String(s.activeIndex)),history.replaceState(history.state,"",c)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:s.windows}));return}if(s.type==="history"&&typeof s.data=="string"){ne=!1,s.lines>0&&s.data&&(ie.unshift(s.data),C+=s.lines,yt(!0,s.lines));return}if(s.type==="data"&&typeof s.data=="string"){let c=oe(s.data);if(f==="connecting"){f="live",$=c,i.write(c);return}f==="live"&&($+=c,i.write(c))}}function Se(){let a=ft,s=localStorage.getItem("tmux-web-token");s&&(a+=(a.includes("?")?"&":"?")+"token="+encodeURIComponent(s)),o=new WebSocket(a),o.onopen=()=>{f="connecting",C=0,ne=!1,ie=[],$="",i.reset(),z=1e3},o.onmessage=c=>{typeof c.data=="string"&&gt(c.data)},o.onclose=()=>{A||(f="connecting",setTimeout(()=>{z=Math.min(z*2,1e4),A||Se()},z))},o.onerror=()=>o?.close()}function ae(a){j({type:"input",data:a})}function N(){return i.fit()}function be(){t.classList.remove("terminal-pending")}function w(){l&&cancelAnimationFrame(l),d&&clearTimeout(d),N(),l=requestAnimationFrame(()=>{l=0,N()&&be()}),d=setTimeout(()=>{N()&&be()},120)}function wt(){w(),setTimeout(w,50),setTimeout(w,150),setTimeout(w,300)}function He(a){return typeof a=="string"&&a.startsWith("image/")}function De(a){return a?.files?.length?a.files[0]??null:null}function bt(a){if(!a?.items)return null;for(let s=0;s<a.items.length;s++){let c=a.items[s];if(c?.kind==="file"&&He(c.type))return c.getAsFile()}return null}async function Lt(a){let s=new FormData,c=a instanceof File&&a.name?a.name:"upload";s.append("file",a,c);let b=localStorage.getItem("tmux-web-token"),x={};b&&(x.Authorization="Bearer "+b);let xe=await fetch(mt,{method:"POST",body:s,headers:x});if(!xe.ok){let Fe="upload failed";try{let Ve=await xe.json();Ve.error&&(Fe=Ve.error)}catch{}throw new Error(Fe)}let We=await xe.json();if(!We.path)throw new Error("no path in response");return We.path}async function Le(a){try{let s=await Lt(a);ae(s)}catch{}}i.onData(a=>ae(a)),i.onResize(({cols:a,rows:s})=>j({type:"resize",cols:a,rows:s})),i.onScroll(()=>{f!=="live"||ne||!i.isNearScrollbackTop()||(ne=!0,j({type:"load_history",before:C}))});let W=!1,F=!1,E=null,S=null,V=document.createElement("div");V.className="terminal-copy-banner",V.innerHTML='<strong>Copy mode</strong><span class="hint">drag to select \xB7 Esc exits</span>',V.hidden=!0,t.appendChild(V);let Y=document.createElement("div");Y.className="terminal-copy-banner terminal-copy-toast",Y.textContent="Copied",Y.hidden=!0,t.appendChild(Y);function Re(a){let s=t.getBoundingClientRect(),c=s.height>0&&i.rows>0?s.height/i.rows:18;return Math.max(0,Math.min(i.rows-1,Math.floor((a-s.top)/c)))}async function $e(a){if(!a)return;try{if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(a);return}}catch{}let s=document.createElement("textarea");s.value=a,s.style.position="fixed",s.style.opacity="0",s.style.pointerEvents="none",document.body.appendChild(s),s.focus(),s.select();try{document.execCommand("copy")}catch{}s.remove()}let K;function Ae(){Y.hidden=!1,K&&clearTimeout(K),K=setTimeout(()=>{Y.hidden=!0},1e3)}function q(a){W=a,V.hidden=!a,t.classList.toggle("terminal-copy-mode-active",a),a&&i.focus()}function Ye(){q(!W)}function Et(){return E===null||S===null?null:E<=S?[E,S]:[S,E]}function kt(a){A||a.button!==0||F||(W||a.shiftKey)&&(a.preventDefault(),a.stopPropagation(),F=!0,E=Re(a.clientY),S=E,i.selectRows(E,E),document.addEventListener("mousemove",Ee,!0),document.addEventListener("mouseup",ke,!0))}function Ee(a){A||!F||E===null||(a.preventDefault(),a.stopPropagation(),S=Re(a.clientY),i.selectRows(E,S))}function ke(a){if(A||!F||E===null)return;a.preventDefault(),a.stopPropagation();let s=Et();document.removeEventListener("mousemove",Ee,!0),document.removeEventListener("mouseup",ke,!0),F=!1,q(!1),s&&($e(i.getRowsText(s[0],s[1])).then(Ae),i.focus())}t.addEventListener("mousedown",kt,!0),i.attachCustomKeyEventHandler(a=>{if(a.type!=="keydown")return!0;if(W){if(a.key==="Escape")return a.preventDefault(),q(!1),!1;if(a.key==="Enter"||(a.ctrlKey||a.metaKey)&&a.code==="KeyC")return a.preventDefault(),$e(i.getScreenText()).then(Ae),q(!1),!1}return a.altKey&&a.code==="KeyC"&&!a.ctrlKey&&!a.metaKey?(a.preventDefault(),Ye(),!1):(a.ctrlKey||a.metaKey)&&a.code==="KeyV"?(a.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let s=await navigator.clipboard.read();for(let c of s)for(let b of c.types)if(He(b)){let x=await c.getType(b);await Le(x);return}}}catch{}try{let s=await navigator.clipboard?.readText();s&&i.pasteText(Pe(s))}catch{}})(),!1):!0});let se=0;function Pe(a){return a.split(`\r
2
4
  `).join(`
3
5
  `).split(`
4
- `).join("\r")}async function Dt(i){let a=St(i.clipboardData);if(a){i.preventDefault(),i.stopPropagation();let T=Date.now();if(T-de<50)return;de=T,await Se(a);return}let d=i.clipboardData?.getData("text/plain");if(!d)return;i.preventDefault(),i.stopPropagation();let E=Date.now();E-de<50||(de=E,s.pasteText(Ve(d)))}let X=0;function $t(i){i.preventDefault(),X++,Fe(i.dataTransfer)&&e.classList.add("terminal-drag-over")}function Rt(i){i.preventDefault(),i.dataTransfer&&(i.dataTransfer.dropEffect="copy")}function At(i){i.preventDefault(),X--,X<=0&&(X=0,e.classList.remove("terminal-drag-over"))}async function Pt(i){i.preventDefault(),X=0,e.classList.remove("terminal-drag-over");let a=Fe(i.dataTransfer);a&&await Se(a)}function Ye(i){!Tt||i.key!=="Escape"||i.defaultPrevented||i.repeat||i.metaKey||i.ctrlKey||i.altKey||s.isFocused()&&(i.preventDefault(),i.stopPropagation(),ce("\x1B"))}e.addEventListener("paste",Dt),e.addEventListener("dragenter",$t),e.addEventListener("dragover",Rt),e.addEventListener("dragleave",At),e.addEventListener("drop",Pt),document.addEventListener("keydown",Ye,!0);function zt(i){if(i.touches.length!==1){r=null;return}let a=i.touches[0];a&&(r={startX:a.clientX,startY:a.clientY,lastX:a.clientX,lastY:a.clientY,scrolling:!1},i.stopPropagation())}function Nt(i){if(!r||i.touches.length!==1)return;let a=i.touches[0];if(!a)return;let d=a.clientY-r.startY,E=a.clientX-r.startX;if(!r.scrolling){if(Math.abs(d)<8||Math.abs(d)<Math.abs(E))return;r.scrolling=!0,f=Date.now()+500}i.preventDefault(),i.stopPropagation(),Ut(-(a.clientY-r.lastY),a.clientX,a.clientY),r.lastX=a.clientX,r.lastY=a.clientY}function Wt(i){if(!r)return;let a=r.scrolling;r=null,i.stopPropagation(),a?(f=Date.now()+500,i.preventDefault()):s.focus()}function qt(i){r=null,i.stopPropagation()}function jt(i){i.pointerType==="touch"&&Date.now()<f&&(i.preventDefault(),i.stopPropagation())}function Ut(i,a,d){(e.querySelector(".xterm-screen")??e).dispatchEvent(new WheelEvent("wheel",{deltaY:i,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:a,clientY:d,bubbles:!0,cancelable:!0}))}e.addEventListener("touchstart",zt,{passive:!0,capture:!0}),e.addEventListener("touchmove",Nt,{passive:!1,capture:!0}),e.addEventListener("touchend",Wt,{passive:!1,capture:!0}),e.addEventListener("touchcancel",qt,{passive:!0,capture:!0}),e.addEventListener("pointerup",jt,!0);function Oe(){L()}window.addEventListener("resize",Oe),window.visualViewport?.addEventListener("resize",L),window.visualViewport?.addEventListener("scroll",L);let Ke=new ResizeObserver(()=>L());return Ke.observe(e),document.fonts?.ready.then(()=>{L(),setTimeout(L,50),setTimeout(L,200)}),e.addEventListener("focusin",Ct,!0),requestAnimationFrame(()=>{_()&&Ce(),je(),L()}),{destroy(){if(Be=!0,l){try{l.close(1e3,"session switch")}catch{}l=void 0}e.innerHTML="",e.classList.remove("terminal-pending"),window.removeEventListener("resize",Oe),Ke.disconnect(),document.removeEventListener("keydown",Ye,!0)},focus(){s.focus()},sendInput(i){ce(i)}}})()}var J=window.__TMUX_WEB_SHELL__,u=null,v=null,De="sessions",D="",fe="",me="",Qe="",m=document.getElementById("sidebar-content"),C=document.getElementById("main-placeholder"),w=document.getElementById("terminal-container"),$=document.getElementById("file-editor"),_t=document.getElementById("fe-path"),k=document.getElementById("fe-status"),Z=document.getElementById("fe-content"),P=document.getElementById("fe-save"),Xt=document.getElementById("fe-delete"),Jt=document.getElementById("fe-back"),et=document.getElementById("fe-new-name"),Zt=document.getElementById("fe-new-btn"),$e=document.getElementById("settings-backdrop"),tt=document.getElementById("settings-popover"),nt=document.querySelector(".sidebar");function Gt(){return window.matchMedia("(max-width: 640px)").matches}function Re(){Gt()&&nt.classList.add("collapsed")}function he(){nt.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{he(),Ae("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{he(),Ae("files")}),document.getElementById("mode-browser").addEventListener("click",()=>{he(),Ae("browser")});function Ae(e){De=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":e==="files"?"mode-files":"mode-browser");t&&t.classList.add("active"),e==="sessions"?(se(),G()):e==="files"?(se(),rn()):(Ln(),I())}async function G(){m.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{let t=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(let n of t){let s=document.createElement("div");s.className="session-item"+(n.name===v?" active":""),s.setAttribute("data-session",n.name),s.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${n.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>
6
+ `).join("\r")}async function xt(a){let s=bt(a.clipboardData);if(s){a.preventDefault(),a.stopPropagation();let x=Date.now();if(x-se<50)return;se=x,await Le(s);return}let c=a.clipboardData?.getData("text/plain");if(!c)return;a.preventDefault(),a.stopPropagation();let b=Date.now();b-se<50||(se=b,i.pasteText(Pe(c)))}let O=0;function Tt(a){a.preventDefault(),O++,De(a.dataTransfer)&&t.classList.add("terminal-drag-over")}function Ct(a){a.preventDefault(),a.dataTransfer&&(a.dataTransfer.dropEffect="copy")}function Mt(a){a.preventDefault(),O--,O<=0&&(O=0,t.classList.remove("terminal-drag-over"))}async function Bt(a){a.preventDefault(),O=0,t.classList.remove("terminal-drag-over");let s=De(a.dataTransfer);s&&await Le(s)}function ze(a){!ht||a.key!=="Escape"||W||a.defaultPrevented||a.repeat||a.metaKey||a.ctrlKey||a.altKey||i.isFocused()&&(a.preventDefault(),a.stopPropagation(),ae("\x1B"))}t.addEventListener("paste",xt),t.addEventListener("dragenter",Tt),t.addEventListener("dragover",Ct),t.addEventListener("dragleave",Mt),t.addEventListener("drop",Bt),document.addEventListener("keydown",ze,!0);function It(a){if(a.touches.length!==1){r=null;return}let s=a.touches[0];s&&(r={startX:s.clientX,startY:s.clientY,lastX:s.clientX,lastY:s.clientY,scrolling:!1},a.stopPropagation())}function St(a){if(!r||a.touches.length!==1)return;let s=a.touches[0];if(!s)return;let c=s.clientY-r.startY,b=s.clientX-r.startX;if(!r.scrolling){if(Math.abs(c)<8||Math.abs(c)<Math.abs(b))return;r.scrolling=!0,u=Date.now()+500}a.preventDefault(),a.stopPropagation(),$t(-(s.clientY-r.lastY),s.clientX,s.clientY),r.lastX=s.clientX,r.lastY=s.clientY}function Ht(a){if(!r)return;let s=r.scrolling;r=null,a.stopPropagation(),s?(u=Date.now()+500,a.preventDefault()):i.focus()}function Dt(a){r=null,a.stopPropagation()}function Rt(a){a.pointerType==="touch"&&Date.now()<u&&(a.preventDefault(),a.stopPropagation())}function $t(a,s,c){(t.querySelector(".xterm-screen")??t).dispatchEvent(new WheelEvent("wheel",{deltaY:a,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:s,clientY:c,bubbles:!0,cancelable:!0}))}t.addEventListener("touchstart",It,{passive:!0,capture:!0}),t.addEventListener("touchmove",St,{passive:!1,capture:!0}),t.addEventListener("touchend",Ht,{passive:!1,capture:!0}),t.addEventListener("touchcancel",Dt,{passive:!0,capture:!0}),t.addEventListener("pointerup",Rt,!0);function je(){w()}window.addEventListener("resize",je),window.visualViewport?.addEventListener("resize",w),window.visualViewport?.addEventListener("scroll",w);let Ne=new ResizeObserver(()=>w());return Ne.observe(t),document.fonts?.ready.then(()=>{w(),setTimeout(w,50),setTimeout(w,200)}),t.addEventListener("focusin",wt,!0),requestAnimationFrame(()=>{N()&&be(),Se(),w()}),{destroy(){if(A=!0,q(!1),K&&clearTimeout(K),document.removeEventListener("mousemove",Ee,!0),document.removeEventListener("mouseup",ke,!0),o){try{o.close(1e3,"session switch")}catch{}o=void 0}t.innerHTML="",t.classList.remove("terminal-pending"),window.removeEventListener("resize",je),Ne.disconnect(),document.removeEventListener("keydown",ze,!0)},focus(){i.focus()},sendInput(a){ae(a)},toggleCopyMode(){Ye()}}})()}var U=window.__TMUX_WEB_SHELL__,m=null,y=null,Oe="sessions",B="",re="",ce="",Ue="",h=document.getElementById("sidebar-content"),H=document.getElementById("main-placeholder"),L=document.getElementById("terminal-container"),X=document.getElementById("file-editor"),Nt=document.getElementById("fe-path"),k=document.getElementById("fe-status"),_=document.getElementById("fe-content"),D=document.getElementById("fe-save"),Wt=document.getElementById("fe-delete"),Ft=document.getElementById("fe-back"),Xe=document.getElementById("fe-new-name"),Vt=document.getElementById("fe-new-btn"),Te=document.getElementById("settings-backdrop"),_e=document.getElementById("settings-popover"),Je=document.querySelector(".sidebar");function Kt(){return window.matchMedia("(max-width: 640px)").matches}function Ce(){Kt()&&Je.classList.add("collapsed")}function Ze(){Je.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{Ze(),Ge("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{Ze(),Ge("files")});function Ge(t){Oe=t,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let e=document.getElementById(t==="sessions"?"mode-sessions":"mode-files");e&&e.classList.add("active"),t==="sessions"?J():Qt()}async function J(){h.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{let e=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(let n of e){let i=document.createElement("div");i.className="session-item"+(n.name===y?" active":""),i.setAttribute("data-session",n.name),i.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${n.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
7
  <span>${p(n.name)}</span>
6
8
  <button class="session-edit-btn" data-session="${p(n.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>
7
- `,s.addEventListener("click",o=>{o.target.closest(".session-edit-btn")||Q(n.name)}),m.appendChild(s);let l=s.querySelector(".session-edit-btn");l&&l.addEventListener("click",o=>{o.stopPropagation(),kn(n.name,l)})}}catch{m.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function p(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function Q(e){if(!(v===e&&u)){se(),$.style.display="none",C.style.display="none",w.style.display="",w.classList.add("terminal-pending"),Re(),u&&(U(),u.destroy(),u=null),v=e;for(let t of m.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e);try{u=await Kt(w,e,{terminal:J.terminal,scrollback:J.scrollback,theme:J.theme,renderer:J.renderer}),Ne()}catch(t){console.error("[shell] terminal init failed:",t);let n=t&&typeof t=="object"&&"message"in t?t.message:String(t);w.textContent="Failed to open terminal for "+e+": "+n,w.classList.remove("terminal-pending")}}}var ve=[],y=null,st=!1;function Qt(e){if(!y||!y.files.length)return null;let t=y.repoRoot;if(!t||!e.startsWith(t))return null;let n=e.slice(t.length+1);for(let s of y.files)if(s.path===n)return s.status;return null}async function en(e){st=!0;try{let t=await fetch("/api/git/status?path="+encodeURIComponent(e));t.ok?y=await t.json():y=null}catch{y=null}finally{st=!1}}function tn(){if(!y||!y.branch)return"";let{branch:e,linesAdded:t,linesRemoved:n}=y,s=`<div class="sidebar-git"><div class="git-branch-badge">
9
+ `,i.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||de(n.name)}),h.appendChild(i);let o=i.querySelector(".session-edit-btn");o&&o.addEventListener("click",l=>{l.stopPropagation(),sn(n.name,o)})}}catch{h.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let t=document.getElementById("ns-btn");t&&t.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function p(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function de(t){if(!(y===t&&m)){X.style.display="none",H.style.display="none",L.style.display="",L.classList.add("terminal-pending"),Ce(),m&&(ge(),m.destroy(),m=null),y=t;for(let e of h.querySelectorAll(".session-item"))e.classList.toggle("active",e.getAttribute("data-session")===t);try{m=await jt(L,t,{terminal:U.terminal,scrollback:U.scrollback,theme:U.theme,renderer:U.renderer}),rt()}catch(e){console.error("[shell] terminal init failed:",e);let n=e&&typeof e=="object"&&"message"in e?e.message:String(e);L.textContent="Failed to open terminal for "+t+": "+n,L.classList.remove("terminal-pending")}}}var ue=[],v=null,Qe=!1;function qt(t){if(!v||!v.files.length)return null;let e=v.repoRoot;if(!e||!t.startsWith(e))return null;let n=t.slice(e.length+1);for(let i of v.files)if(i.path===n)return i.status;return null}async function Ot(t){Qe=!0;try{let e=await fetch("/api/git/status?path="+encodeURIComponent(t));e.ok?v=await e.json():v=null}catch{v=null}finally{Qe=!1}}function Ut(){if(!v||!v.branch)return"";let{branch:t,linesAdded:e,linesRemoved:n}=v,i=`<div class="sidebar-git"><div class="git-branch-badge">
8
10
  <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>
9
- ${p(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")&&q&&y&&(e.stopPropagation(),ot())});var ee=document.getElementById("header-git"),Pe=document.getElementById("git-diff-view"),it=document.getElementById("gd-path"),ye=document.getElementById("gd-status"),ge=document.getElementById("gd-content"),nn=document.getElementById("gd-back"),q=null;function j(e){if(!e||!e.branch){ee.style.display="none",q=null;return}q=e.repoRoot;let{branch:t,linesAdded:n,linesRemoved:s}=e,l=`<span class="branch">${p(t)}</span>`;(n>0||s>0)&&(l+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),ee.innerHTML=l,ee.style.display="block"}ee.addEventListener("click",e=>{q&&y&&(e.stopPropagation(),ot())});var b=document.getElementById("git-popover"),we=document.getElementById("git-popover-backdrop");function ze(){let e=ee.getBoundingClientRect();b.style.top=e.bottom+4+"px",b.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function at(){let e=q,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(Le(),e){De="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),R(e)}})}var lt="";function ot(){if(!y)return;let e=y.files||[];if(!e.length)b.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
10
- <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],l=s.additions||0,o=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}">
11
- ${c}<span class="file-path">${p(s.path)}</span>
12
- <span class="file-add">${l>0?"+"+l:""}</span>
13
- <span class="file-del">${o>0?"-"+o:""}</span>
14
- </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',lt=t,b.innerHTML=t,b.querySelectorAll(".git-popover-item").forEach(n=>{let s=parseInt(n.dataset.idx||"0",10),l=e[s];!l||l.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>ct(l.path)))})}ze(),b.classList.add("open"),we.classList.add("open"),at()}function sn(){return window.innerWidth>=1024}function rt(e,t){let n=(e+(t&&e?`
15
- `:"")+t).split(`
16
- `),s="",l=0,o=0;for(let c of n){let r=c;if(r.startsWith("@@")){let f=r.match(/@@\s+-(\d+)[^+]*\+(\d+)/);f&&(l=parseInt(f[1],10)-1,o=parseInt(f[2],10)-1),s+=`<div class="diff-line hunk"><span class="ln-body">${p(r)}</span></div>`;continue}if(r.startsWith("diff --git")||r.startsWith("index ")||r.startsWith("--- ")||r.startsWith("+++ ")||r.startsWith("new file")||r.startsWith("deleted file")){r.startsWith("diff --git")&&(s+=`<div class="diff-line header"><span class="ln-body">${p(r)}</span></div>`);continue}if(r.startsWith("+"))o++,s+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${p(r)}</span></div>`;else if(r.startsWith("-"))l++,s+=`<div class="diff-line del"><span class="ln-no">${l}</span><span class="ln-body">${p(r)}</span></div>`;else if(r.startsWith("\\"))s+=`<div class="diff-line"><span class="ln-body">${p(r)}</span></div>`;else{if(!r&&!l&&!o)continue;l++,o++,s+=`<div class="diff-line"><span class="ln-no">${l}</span><span class="ln-no" style="margin-right:12px">${o}</span><span class="ln-body">${p(r)}</span></div>`}}return{html:s,lineCount:n.length}}var be=null;async function ct(e){let t=q;if(t)if(Le(),se(),sn()){w.style.display="none",u&&(U(),u.destroy(),u=null,be=v,v=null),j(null),$.style.display="none",C.style.display="none",Pe.style.display="flex",it.textContent=e,ye.textContent="Loading\u2026",ge.innerHTML="";try{let n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());let s=await n.json();ln(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);ye.textContent="Error",ge.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${p(s)}</div>`}}else{b.innerHTML=`<div class="git-diff-view"><div class="diff-header">
11
+ ${p(t)}</div>`;return(e>0||n>0)&&(i+=`<div class="git-diff-stats">+<span class="add">${e}</span> <span class="del">-${n}</span></div>`),i+"</div>"}h.addEventListener("click",t=>{t.target.closest(".sidebar-git")&&P&&v&&(t.stopPropagation(),at())});var Z=document.getElementById("header-git"),et=document.getElementById("git-diff-view"),tt=document.getElementById("gd-path"),pe=document.getElementById("gd-status"),fe=document.getElementById("gd-content"),Xt=document.getElementById("gd-back"),P=null;function G(t){if(!t||!t.branch){Z.style.display="none",P=null;return}P=t.repoRoot;let{branch:e,linesAdded:n,linesRemoved:i}=t,o=`<span class="branch">${p(e)}</span>`;(n>0||i>0)&&(o+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${i}</span>`),Z.innerHTML=o,Z.style.display="block"}Z.addEventListener("click",t=>{P&&v&&(t.stopPropagation(),at())});var g=document.getElementById("git-popover"),me=document.getElementById("git-popover-backdrop");function Me(){let t=Z.getBoundingClientRect();g.style.top=t.bottom+4+"px",g.style.left=Math.max(8,Math.min(t.left,window.innerWidth-400))+"px"}function nt(){let t=P,e=document.getElementById("git-popover-browse");e&&e.addEventListener("click",()=>{if(ve(),t){Oe="files",document.querySelectorAll(".mode-btn").forEach(i=>i.classList.remove("active"));let n=document.getElementById("mode-files");n&&n.classList.add("active"),I(t)}})}var it="";function at(){if(!v)return;let t=v.files||[];if(!t.length)g.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
12
+ <div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>`;else{let e=`<div class="git-popover-header">${t.length} file${t.length>1?"s":""} changed</div>`;for(let n=0;n<t.length;n++){let i=t[n],o=i.additions||0,l=i.deletions||0,d="";switch(i.status){case"M":d='<span class="git-file-status M">M</span>';break;case"A":d='<span class="git-file-status A">A</span>';break;case"D":d='<span class="git-file-status D">D</span>';break;case"?":d='<span class="git-file-status ?">?</span>';break;case"R":d='<span class="git-file-status R">R</span>';break}e+=`<div class="git-popover-item" data-idx="${n}">
13
+ ${d}<span class="file-path">${p(i.path)}</span>
14
+ <span class="file-add">${o>0?"+"+o:""}</span>
15
+ <span class="file-del">${l>0?"-"+l:""}</span>
16
+ </div>`}e+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',it=e,g.innerHTML=e,g.querySelectorAll(".git-popover-item").forEach(n=>{let i=parseInt(n.dataset.idx||"0",10),o=t[i];!o||o.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>ot(o.path)))})}Me(),g.classList.add("open"),me.classList.add("open"),nt()}function _t(){return window.innerWidth>=1024}function st(t,e){let n=(t+(e&&t?`
17
+ `:"")+e).split(`
18
+ `),i="",o=0,l=0;for(let d of n){let r=d;if(r.startsWith("@@")){let u=r.match(/@@\s+-(\d+)[^+]*\+(\d+)/);u&&(o=parseInt(u[1],10)-1,l=parseInt(u[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${p(r)}</span></div>`;continue}if(r.startsWith("diff --git")||r.startsWith("index ")||r.startsWith("--- ")||r.startsWith("+++ ")||r.startsWith("new file")||r.startsWith("deleted file")){r.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${p(r)}</span></div>`);continue}if(r.startsWith("+"))l++,i+=`<div class="diff-line add"><span class="ln-no">${l}</span><span class="ln-body">${p(r)}</span></div>`;else if(r.startsWith("-"))o++,i+=`<div class="diff-line del"><span class="ln-no">${o}</span><span class="ln-body">${p(r)}</span></div>`;else if(r.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${p(r)}</span></div>`;else{if(!r&&!o&&!l)continue;o++,l++,i+=`<div class="diff-line"><span class="ln-no">${o}</span><span class="ln-no" style="margin-right:12px">${l}</span><span class="ln-body">${p(r)}</span></div>`}}return{html:i,lineCount:n.length}}var he=null;async function ot(t){let e=P;if(e)if(ve(),_t()){L.style.display="none",m&&(ge(),m.destroy(),m=null,he=y,y=null),G(null),X.style.display="none",H.style.display="none",et.style.display="flex",tt.textContent=t,pe.textContent="Loading\u2026",fe.innerHTML="";try{let n=await fetch("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t));if(!n.ok)throw new Error(await n.text());let i=await n.json();Zt(t,i.diff,i.stagedDiff)}catch(n){let i=n instanceof Error?n.message:String(n);pe.textContent="Error",fe.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${p(i)}</div>`}}else{g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
17
19
  <button class="diff-back">\u2190 Back</button>
18
- <span class="diff-filename">${p(e)}</span>
20
+ <span class="diff-filename">${p(t)}</span>
19
21
  <span class="diff-loading">Loading\u2026</span>
20
- </div></div>`,ze(),b.classList.add("open"),we.classList.add("open");try{let n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());let s=await n.json();an(e,s.diff,s.stagedDiff)}catch(n){let s=n instanceof Error?n.message:String(n);b.innerHTML=`<div class="git-diff-view"><div class="diff-header">
22
+ </div></div>`,Me(),g.classList.add("open"),me.classList.add("open");try{let n=await fetch("/api/git/diff?path="+encodeURIComponent(e)+"&file="+encodeURIComponent(t));if(!n.ok)throw new Error(await n.text());let i=await n.json();Jt(t,i.diff,i.stagedDiff)}catch(n){let i=n instanceof Error?n.message:String(n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
21
23
  <button class="diff-back">\u2190 Back</button>
22
- <span class="diff-filename">${p(e)}</span>
23
- </div><div class="diff-empty">Error loading diff: ${p(s)}</div></div>`}}}function an(e,t,n){if(!t&&!n){b.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
+ <span class="diff-filename">${p(t)}</span>
25
+ </div><div class="diff-empty">Error loading diff: ${p(i)}</div></div>`}}}function Jt(t,e,n){if(!e&&!n){g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
26
  <button class="diff-back">\u2190 Back</button>
25
- <span class="diff-filename">${p(e)}</span>
26
- </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:s,lineCount:l}=rt(t,n);b.innerHTML=`<div class="git-diff-view"><div class="diff-header">
27
+ <span class="diff-filename">${p(t)}</span>
28
+ </div><div class="diff-empty">No diff content available</div></div>`;return}let{html:i,lineCount:o}=st(e,n);g.innerHTML=`<div class="git-diff-view"><div class="diff-header">
27
29
  <button class="diff-back">\u2190 Back</button>
28
- <span class="diff-filename">${p(e)}</span>
29
- <span class="diff-filesize">${l} lines</span>
30
- </div><div class="diff-content">${s}</div></div>`,b.querySelector(".diff-back")?.addEventListener("click",()=>{b.innerHTML=lt,ze(),y&&b.querySelectorAll(".git-popover-item").forEach(o=>{let c=parseInt(o.dataset.idx||"0",10),r=y.files[c];!r||r.status==="D"||(o.style.cursor="pointer",o.addEventListener("click",()=>ct(r.path)))}),at()})}function ln(e,t,n){if(!t&&!n){ye.textContent="No diff",ge.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:l}=rt(t,n);it.textContent=e,ye.textContent=l+" lines",ge.innerHTML=s}function on(){Pe.style.display="none",be?(w.style.display="",w.classList.add("terminal-pending"),Q(be),be=null):v?(w.style.display="",w.classList.add("terminal-pending"),Ne()):C.style.display="flex"}nn.addEventListener("click",on);function Le(){b.classList.remove("open"),we.classList.remove("open")}we.addEventListener("click",Le),document.addEventListener("keydown",e=>{e.key==="Escape"&&(Le(),Me())});async function dt(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();y=s,j(s);return}}}catch{}y=null,j(null)}var Ee=null;function Ne(){U(),v&&(dt(v),Ee=setInterval(()=>{v&&dt(v)},3e4))}function U(){Ee&&(clearInterval(Ee),Ee=null)}function rn(){if(ve=J.fsRoots||[],ve.length===0){if(v){cn(v);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 ve){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> ${p(e)}`,t.addEventListener("click",()=>R(e)),m.appendChild(t)}}async function cn(e){m.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();t.path?R(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 R(e){D=e,m.innerHTML='<div class="file-tree-empty">Loading...</div>';let t=en(e);try{let n=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();await t,m.innerHTML="",j(y),m.innerHTML+=tn();let s=dn(e);if(s&&!ve.some(o=>e===o)){let o=document.createElement("div");o.className="file-tree-item",o.innerHTML="..",o.addEventListener("click",()=>R(s)),m.appendChild(o)}if(n.dirs)for(let o of n.dirs){let c=o.split("/").pop()||o.split("\\").pop(),r=document.createElement("div");r.className="file-tree-item",r.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> ${p(c)}`,r.addEventListener("click",()=>R(o)),m.appendChild(r)}else m.innerHTML="";let l=n.files||[];for(let o of l){let c=o.split("/").pop()||o.split("\\").pop(),r=Qt(o),f="",g="";if(r)switch(r){case"M":f="git-mod",g='<span class="git-file-status M">M</span>';break;case"A":f="git-add",g='<span class="git-file-status A">A</span>';break;case"D":f="git-del",g='<span class="git-file-status D">D</span>';break;case"?":f="git-untracked",g='<span class="git-file-status ?">?</span>';break;case"R":f="git-mod",g='<span class="git-file-status R">R</span>';break}let B=document.createElement("div");B.className="file-tree-item file"+(f?" "+f:""),B.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${g}${p(c)}`,B.addEventListener("click",()=>un(o)),m.appendChild(B)}}catch{m.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function dn(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function un(e){fe=e,se(),w.style.display="none",u&&(U(),u.destroy(),u=null,v=null),j(null),Re(),C.style.display="none",$.style.display="flex",_t.textContent=e,k.textContent="Loading...",P.style.display="none";try{let t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){let s=await t.json();k.textContent=s.error||"Failed to load",Z.value="";return}let n=await t.json();Z.value=n.content,me=n.content,Qe=n.content,k.textContent=n.size+" bytes",P.style.display=""}catch{k.textContent="Failed to load",Z.value=""}}Z.addEventListener("input",()=>{me=Z.value}),P.addEventListener("click",async()=>{P.textContent="Saving...",P.disabled=!0;try{let e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:fe,content:me})});if(!e.ok){let t=await e.json();k.textContent=t.error||"Save failed";return}Qe=me,k.textContent="Saved"}catch{k.textContent="Save failed"}finally{P.disabled=!1,P.textContent="Save"}}),Xt.addEventListener("click",async()=>{if(confirm("Delete "+fe+"?"))try{let e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:fe})});if(!e.ok){let t=await e.json();k.textContent=t.error||"Delete failed";return}$.style.display="none",C.style.display="flex",D&&R(D)}catch{k.textContent="Delete failed"}}),Jt.addEventListener("click",()=>{$.style.display="none",C.style.display="flex",D&&R(D)}),Zt.addEventListener("click",async()=>{let e=et.value.trim();if(!e)return;let t=D+"/"+e;try{let n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){let s=await n.json();k.textContent=s.error||"Create failed";return}et.value="",D&&R(D)}catch{k.textContent="Create failed"}});var ut=document.getElementById("browser-view"),pt=document.getElementById("br-back"),ft=document.getElementById("br-fwd"),pn=document.getElementById("br-reload"),x=document.getElementById("br-address"),fn=document.getElementById("br-new"),z=document.getElementById("browser-content"),mt=document.getElementById("mobile-keys"),S=[],h=null,ht=0,ke=!1,F="none",xe=null,mn="https://html.duckduckgo.com/html/?q=";function V(){return h?S.find(e=>e.id===h)??null:null}function Te(e){return`<svg viewBox="0 0 24 24" fill="currentColor" width="${e}" height="${e}"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>`}function hn(e){let t=e.trim();return/^https?:\/\//i.test(t)||/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t)?t:!/\s/.test(t)&&/\./.test(t)?"https://"+t:/^(localhost|127\.0\.0\.1)(:\d+)?$/i.test(t)?"http://"+t:mn+encodeURIComponent(t)}function te(){ht++;let e="t"+ht;return S.push({id:e,title:"New Tab",url:null,history:[],historyIdx:-1,error:null,seq:0}),h=e,I(),Ie(),e}function I(){if(De!=="browser")return;let e='<div class="sidebar-section-label">Tabs</div>';for(let n of S){let s=n.id===h?" active":"";e+=`<div class="tab-item${s}" data-tab="${n.id}">
31
- <span class="tab-fav">${Te(16)}</span>
32
- <span class="tab-title">${p(n.title)}</span>
33
- <button class="tab-close" data-close="${n.id}" title="Close tab" aria-label="Close tab">&times;</button>
34
- </div>`}e+='<button class="tab-new" id="tab-new-btn"><svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg> New Tab</button>',m.innerHTML=e,m.querySelectorAll(".tab-item").forEach(n=>{n.addEventListener("click",s=>{s.target.closest(".tab-close")||vt(n.dataset.tab)})}),m.querySelectorAll(".tab-close").forEach(n=>{n.addEventListener("click",s=>{s.stopPropagation(),vn(n.dataset.close)})});let t=document.getElementById("tab-new-btn");t&&t.addEventListener("click",()=>{te(),x.focus()})}function vt(e){h=e,I();let t=V();t&&(x.value=t.url||"",Y(),t.error?ne(t.error):t.url?A(t.id,t.url,!1):Ie())}function vn(e){let t=S.findIndex(n=>n.id===e);if(t!==-1){if(S.splice(t,1),S.length===0){h=null,I(),Ie();return}h===e?(h=S[Math.max(0,t-1)].id,vt(h)):I()}}function Y(){let e=V();pt.disabled=!e||e.historyIdx<=0,ft.disabled=!e||e.historyIdx>=e.history.length-1}function Ie(){z.innerHTML=`<div class="browser-state">
35
- <div class="big">${Te(44)}</div>
36
- <div>Type a URL or search terms in the address bar above.</div>
37
- <div class="hint">Ctrl/Cmd-click any link to open it in a new tab.</div>
38
- </div>`,Y()}function ne(e){z.innerHTML=`<div class="browser-state">
39
- <div class="big" style="color:#ef4444">${Te(44)}</div>
40
- <div>Could not load page</div>
41
- <div class="hint" style="color:#ef4444">${p(e)}</div>
42
- </div>`}function yt(){z.innerHTML=`<div class="browser-state">
43
- <div class="big">${Te(44)}</div>
44
- <div>Loading\u2026</div>
45
- </div>`}async function A(e,t,n){let s=S.find(c=>c.id===e);if(!s)return;let l=hn(t);if(!l)return;let o=++s.seq;n&&(s.history=s.history.slice(0,s.historyIdx+1),s.history.push(l),s.historyIdx=s.history.length-1),s.url=l,s.error=null,h===e&&(x.value=l,Y(),yt());try{let c=await fetch("/api/browse?url="+encodeURIComponent(l)+"&tab="+encodeURIComponent(e)),r=await c.json();if(s.seq!==o)return;if(!c.ok||r.error){let g=r.error||"HTTP "+c.status;s.error=g,h===e&&ne(g),I();return}let f=r.finalUrl||l;s.url=f,s.title=r.title||s.title,s.history[s.historyIdx]=f,h===e&&(x.value=f,Y(),gt(r)),I()}catch(c){if(s.seq!==o)return;s.error="Network error: "+(c instanceof Error?c.message:String(c)),h===e&&ne(s.error),I()}}function yn(){let e=V();!e||e.historyIdx<=0||(e.historyIdx--,A(e.id,e.history[e.historyIdx],!1))}function gn(){let e=V();!e||e.historyIdx>=e.history.length-1||(e.historyIdx++,A(e.id,e.history[e.historyIdx],!1))}function gt(e){let t="";e.truncated&&(t+='<div class="browser-bar"><span class="err">Response truncated \u2014 page may be incomplete.</span></div>'),t+=e.html||"",z.innerHTML=t,wn(),z.scrollTop=0}function wn(){z.querySelectorAll("a[href]").forEach(e=>{e.addEventListener("click",t=>{let n=e.getAttribute("href")||"";if(n.startsWith("/api/browse?")){t.preventDefault();let s=t,l=null;try{l=new URL(n,location.origin).searchParams.get("url")}catch{return}if(!l)return;if(s.ctrlKey||s.metaKey||s.shiftKey){let o=te();A(o,l,!0)}else A(h,l,!0)}else/^(about|blob|file):/i.test(n)&&t.preventDefault()})}),z.querySelectorAll("form").forEach(e=>{e.addEventListener("submit",t=>{t.preventDefault();let n=e.getAttribute("action")||"";if(!n.startsWith("/api/browse/submit?"))return;let s=null;try{s=new URL(n,location.origin).searchParams.get("tab")||h}catch{s=h}if(!s)return;let l=new FormData(e),o=new URLSearchParams;l.forEach((c,r)=>o.append(r,String(c))),bn(s,o)})})}async function bn(e,t){let n=S.find(o=>o.id===e);if(!n)return;let s=++n.seq,l=t.get("__browse_action")||"";h===e&&yt();try{let o=await fetch("/api/browse/submit?tab="+encodeURIComponent(e),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()}),c=await o.json();if(n.seq!==s)return;if(!o.ok||c.error){let f=c.error||"HTTP "+o.status;n.error=f,h===e&&ne(f);return}let r=c.finalUrl||l||n.url||"";n.url=r,n.title=c.title||n.title,n.history=n.history.slice(0,n.historyIdx+1),n.history.push(r),n.historyIdx=n.history.length-1,n.error=null,h===e&&(x.value=r,Y(),gt(c)),I()}catch(o){if(n.seq!==s)return;n.error="Network error: "+(o instanceof Error?o.message:String(o)),h===e&&ne(n.error)}}function Ln(){if(!ke){if(ke=!0,$.style.display!=="none"?(F="files",u&&(U(),u.destroy(),u=null)):u||w.style.display!=="none"?(F="terminal",xe=v,u&&(U(),u.destroy(),u=null),v=null):F="none",w.style.display="none",$.style.display="none",Pe.style.display="none",C.style.display="none",j(null),mt.style.display="none",ut.style.display="flex",!h)te();else{I();let e=V();e&&(x.value=e.url||"",Y(),e.url?A(e.id,e.url,!1):Ie())}he()}}function se(){if(ke){if(ke=!1,ut.style.display="none",mt.style.display="",F==="files")$.style.display="flex";else if(F==="terminal"&&xe){let e=xe;xe=null,Q(e)}else v?(w.style.display="",w.classList.add("terminal-pending"),Ne()):C.style.display="flex";F="none"}}pt.addEventListener("click",yn),ft.addEventListener("click",gn),pn.addEventListener("click",()=>{let e=V();e&&e.url&&A(e.id,e.url,!1)}),fn.addEventListener("click",()=>{te(),x.focus(),x.select()}),x.addEventListener("keydown",e=>{if(e.key==="Enter"){e.preventDefault();let t=h||te();A(t,x.value,!0),x.blur()}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=tt.classList.toggle("open");$e.classList.toggle("open",e)}),$e.addEventListener("click",()=>{tt.classList.remove("open"),$e.classList.remove("open")});var N="",ie=document.createElement("div");ie.className="session-popover-backdrop",document.body.appendChild(ie);var H=document.createElement("div");H.id="session-popover",H.className="session-popover",H.style.display="none",H.innerHTML=`<div class="sp-field">
30
+ <span class="diff-filename">${p(t)}</span>
31
+ <span class="diff-filesize">${o} lines</span>
32
+ </div><div class="diff-content">${i}</div></div>`,g.querySelector(".diff-back")?.addEventListener("click",()=>{g.innerHTML=it,Me(),v&&g.querySelectorAll(".git-popover-item").forEach(l=>{let d=parseInt(l.dataset.idx||"0",10),r=v.files[d];!r||r.status==="D"||(l.style.cursor="pointer",l.addEventListener("click",()=>ot(r.path)))}),nt()})}function Zt(t,e,n){if(!e&&!n){pe.textContent="No diff",fe.innerHTML='<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">No diff content available</div>';return}let{html:i,lineCount:o}=st(e,n);tt.textContent=t,pe.textContent=o+" lines",fe.innerHTML=i}function Gt(){et.style.display="none",he?(L.style.display="",L.classList.add("terminal-pending"),de(he),he=null):y?(L.style.display="",L.classList.add("terminal-pending"),rt()):H.style.display="flex"}Xt.addEventListener("click",Gt);function ve(){g.classList.remove("open"),me.classList.remove("open")}me.addEventListener("click",ve),document.addEventListener("keydown",t=>{t.key==="Escape"&&(ve(),we())});async function lt(t){try{let e=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(t))).json();if(e.path){let n=await fetch("/api/git/status?path="+encodeURIComponent(e.path));if(n.ok){let i=await n.json();v=i,G(i);return}}}catch{}v=null,G(null)}var ye=null;function rt(){ge(),y&&(lt(y),ye=setInterval(()=>{y&&lt(y)},3e4))}function ge(){ye&&(clearInterval(ye),ye=null)}function Qt(){if(ue=U.fsRoots||[],ue.length===0){if(y){en(y);return}h.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}h.innerHTML="";for(let t of ue){let e=document.createElement("div");e.className="file-tree-item",e.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${p(t)}`,e.addEventListener("click",()=>I(t)),h.appendChild(e)}}async function en(t){h.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let e=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(t))).json();e.path?I(e.path):h.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{h.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function I(t){B=t,h.innerHTML='<div class="file-tree-empty">Loading...</div>';let e=Ot(t);try{let n=await(await fetch("/api/fs/list?path="+encodeURIComponent(t))).json();await e,h.innerHTML="",G(v),h.innerHTML+=Ut();let i=tn(t);if(i&&!ue.some(l=>t===l)){let l=document.createElement("div");l.className="file-tree-item",l.innerHTML="..",l.addEventListener("click",()=>I(i)),h.appendChild(l)}if(n.dirs)for(let l of n.dirs){let d=l.split("/").pop()||l.split("\\").pop(),r=document.createElement("div");r.className="file-tree-item",r.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> ${p(d)}`,r.addEventListener("click",()=>I(l)),h.appendChild(r)}else h.innerHTML="";let o=n.files||[];for(let l of o){let d=l.split("/").pop()||l.split("\\").pop(),r=qt(l),u="",f="";if(r)switch(r){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 C=document.createElement("div");C.className="file-tree-item file"+(u?" "+u:""),C.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${f}${p(d)}`,C.addEventListener("click",()=>nn(l)),h.appendChild(C)}}catch{h.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function tn(t){let e=t.lastIndexOf("/");return e<=0?null:t.substring(0,e)}async function nn(t){re=t,L.style.display="none",m&&(ge(),m.destroy(),m=null,y=null),G(null),Ce(),H.style.display="none",X.style.display="flex",Nt.textContent=t,k.textContent="Loading...",D.style.display="none";try{let e=await fetch("/api/file?path="+encodeURIComponent(t));if(!e.ok){let i=await e.json();k.textContent=i.error||"Failed to load",_.value="";return}let n=await e.json();_.value=n.content,ce=n.content,Ue=n.content,k.textContent=n.size+" bytes",D.style.display=""}catch{k.textContent="Failed to load",_.value=""}}_.addEventListener("input",()=>{ce=_.value}),D.addEventListener("click",async()=>{D.textContent="Saving...",D.disabled=!0;try{let t=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:re,content:ce})});if(!t.ok){let e=await t.json();k.textContent=e.error||"Save failed";return}Ue=ce,k.textContent="Saved"}catch{k.textContent="Save failed"}finally{D.disabled=!1,D.textContent="Save"}}),Wt.addEventListener("click",async()=>{if(confirm("Delete "+re+"?"))try{let t=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:re})});if(!t.ok){let e=await t.json();k.textContent=e.error||"Delete failed";return}X.style.display="none",H.style.display="flex",B&&I(B)}catch{k.textContent="Delete failed"}}),Ft.addEventListener("click",()=>{X.style.display="none",H.style.display="flex",B&&I(B)}),Vt.addEventListener("click",async()=>{let t=Xe.value.trim();if(!t)return;let e=B+"/"+t;try{let n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:e})});if(!n.ok){let i=await n.json();k.textContent=i.error||"Create failed";return}Xe.value="",B&&I(B)}catch{k.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let t=_e.classList.toggle("open");Te.classList.toggle("open",t)}),Te.addEventListener("click",()=>{_e.classList.remove("open"),Te.classList.remove("open")});var R="",Q=document.createElement("div");Q.className="session-popover-backdrop",document.body.appendChild(Q);var M=document.createElement("div");M.id="session-popover",M.className="session-popover",M.style.display="none",M.innerHTML=`<div class="sp-field">
46
33
  <label>Rename session</label>
47
34
  <input type="text" id="sp-name" />
48
35
  </div>
@@ -51,5 +38,5 @@ var Ft="https://esm.sh/ghostty-web@0.4.0",Vt=/\x1b\](?:10|11|110|111|104)(?:;[^\
51
38
  <button class="btn" id="sp-cancel">Cancel</button>
52
39
  </div>
53
40
  <hr class="sp-divider" />
54
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(H);function En(e){let t=e.getBoundingClientRect();H.style.top=t.bottom+4+"px",H.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function kn(e,t){N=e;let n=document.getElementById("sp-name");n.value=e,ie.classList.add("open"),H.style.display="block",En(t),setTimeout(()=>n.focus(),50)}function ae(){ie.classList.remove("open"),H.style.display="none"}ie.addEventListener("click",ae),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===N){ae();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:N,newName:e})})).ok)return;ae(),G(),v===N&&Q(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+N+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:N})})).ok)return;ae(),v===N&&(u&&(u.destroy(),u=null),v=null,w.style.display="none",C.style.display="flex"),G()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",ae);var xn={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!u)return;let n=xn[t.dataset.key];n&&(u.sendInput(n),u.focus())});var M=document.getElementById("mk-input"),wt=document.getElementById("mk-send");function bt(){if(!M||!u)return;let e=M.value;e&&(u.sendInput(e+"\r"),M.value="",M.rows=1,u.focus())}if(M&&(M.addEventListener("input",()=>{M.rows=1;let e=M.value.split(`
55
- `).length;M.rows=Math.min(e,4)}),M.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),bt())})),wt&&wt.addEventListener("click",bt),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 We=document.getElementById("process-panel"),le=document.getElementById("proc-list"),Tn=document.getElementById("proc-close"),qe=document.getElementById("proc-backdrop"),In=document.getElementById("header-ram");function Mn(){le.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch("/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 n of e){let s=n.rss,l=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">'+l+'</span><span class="proc-cmd" title="'+p(n.command)+'">'+p(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}le.innerHTML=t;for(let n of le.querySelectorAll(".proc-row")){let s=n.querySelector(".proc-kill");s&&s.addEventListener("click",l=>{let o=l.target.closest(".proc-row");!o||!confirm("Kill PID "+o.dataset.pid+"?")||fetch("/api/system/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(o.dataset.pid,10)})}).then(c=>c.json()).then(c=>{c.ok?o.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 Bn(){We.classList.add("open"),qe.classList.add("open"),Mn()}function Me(){We.classList.remove("open"),qe.classList.remove("open")}In.addEventListener("click",e=>{e.stopPropagation(),We.classList.contains("open")?Me():Bn()}),Tn.addEventListener("click",Me),qe.addEventListener("click",Me),window.__openSession=Q,window.__refreshSidebar=G,G(),Re();
41
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(M);function an(t){let e=t.getBoundingClientRect();M.style.top=e.bottom+4+"px",M.style.left=Math.max(8,Math.min(e.left,window.innerWidth-240))+"px"}function sn(t,e){R=t;let n=document.getElementById("sp-name");n.value=t,Q.classList.add("open"),M.style.display="block",an(e),setTimeout(()=>n.focus(),50)}function ee(){Q.classList.remove("open"),M.style.display="none"}Q.addEventListener("click",ee),document.getElementById("sp-save").addEventListener("click",async()=>{let t=document.getElementById("sp-name").value.trim();if(!t||t===R){ee();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:R,newName:t})})).ok)return;ee(),J(),y===R&&de(t)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+R+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:R})})).ok)return;ee(),y===R&&(m&&(m.destroy(),m=null),y=null,L.style.display="none",H.style.display="flex"),J()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",ee);var on={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",t=>{let e=t.target.closest("button[data-key]");if(!e||!m)return;let n=on[e.dataset.key];n&&(m.sendInput(n),m.focus())});var T=document.getElementById("mk-input"),ct=document.getElementById("mk-send");function dt(){if(!T||!m)return;let t=T.value;t&&(m.sendInput(t+"\r"),T.value="",T.rows=1,m.focus())}if(T&&(T.addEventListener("input",()=>{T.rows=1;let t=T.value.split(`
42
+ `).length;T.rows=Math.min(t,4)}),T.addEventListener("keydown",t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),dt())})),ct&&ct.addEventListener("click",dt),window.visualViewport){let t=document.querySelector(".app-layout"),e=()=>{t&&(t.style.height=window.visualViewport.height+"px"),window.scrollTo(0,0),document.documentElement.scrollTop=0,document.body.scrollTop=0};window.visualViewport.addEventListener("resize",e),window.addEventListener("orientationchange",()=>setTimeout(e,100)),document.addEventListener("touchstart",()=>{(window.scrollY>0||document.documentElement.scrollTop>0)&&window.scrollTo(0,0)},{passive:!0})}var Be=document.getElementById("process-panel"),te=document.getElementById("proc-list"),ln=document.getElementById("proc-close"),Ie=document.getElementById("proc-backdrop"),rn=document.getElementById("header-ram");function cn(){te.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch("/api/system/processes").then(t=>t.json()).then(t=>{if(!Array.isArray(t)||!t.length){te.innerHTML='<div class="proc-empty">No processes</div>';return}let e="";for(let n of t){let i=n.rss,o=i<1048576?(i/1024).toFixed(0)+"K":(i/1048576).toFixed(1)+"M";e+='<div class="proc-row" data-pid="'+n.pid+'"><span class="proc-mem">'+n.mem+'%</span><span class="proc-rss">'+o+'</span><span class="proc-cmd" title="'+p(n.command)+'">'+p(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}te.innerHTML=e;for(let n of te.querySelectorAll(".proc-row")){let i=n.querySelector(".proc-kill");i&&i.addEventListener("click",o=>{let l=o.target.closest(".proc-row");!l||!confirm("Kill PID "+l.dataset.pid+"?")||fetch("/api/system/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pid:parseInt(l.dataset.pid,10)})}).then(d=>d.json()).then(d=>{d.ok?l.style.opacity="0.3":alert("Failed: "+(d.error||"unknown"))}).catch(()=>alert("Network error"))})}}).catch(()=>{te.innerHTML='<div class="proc-empty">Failed to load</div>'})}function dn(){Be.classList.add("open"),Ie.classList.add("open"),cn()}function we(){Be.classList.remove("open"),Ie.classList.remove("open")}rn.addEventListener("click",t=>{t.stopPropagation(),Be.classList.contains("open")?we():dn()}),ln.addEventListener("click",we),Ie.addEventListener("click",we),window.__openSession=de,window.__refreshSidebar=J,J(),Ce();
@@ -1,45 +1,30 @@
1
- import{initTerminal as We}from"./terminal-core.js";const q=window.__TMUX_WEB_SHELL__;let c=null,u=null,pe="sessions",B="",X="",Y="",Le="";const f=document.getElementById("sidebar-content"),E=document.getElementById("main-placeholder"),v=document.getElementById("terminal-container"),I=document.getElementById("file-editor"),Ge=document.getElementById("fe-path"),g=document.getElementById("fe-status"),U=document.getElementById("fe-content"),M=document.getElementById("fe-save"),Oe=document.getElementById("fe-delete"),Ve=document.getElementById("fe-back"),Ee=document.getElementById("fe-new-name"),_e=document.getElementById("fe-new-btn"),ue=document.getElementById("settings-backdrop"),ke=document.getElementById("settings-popover"),xe=document.querySelector(".sidebar");function Ke(){return window.matchMedia("(max-width: 640px)").matches}function me(){Ke()&&xe.classList.add("collapsed")}function Q(){xe.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{Q(),ve("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{Q(),ve("files")}),document.getElementById("mode-browser").addEventListener("click",()=>{Q(),ve("browser")});function ve(e){pe=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));const t=document.getElementById(e==="sessions"?"mode-sessions":e==="files"?"mode-files":"mode-browser");t&&t.classList.add("active"),e==="sessions"?(V(),j()):e==="files"?(V(),st()):(yt(),w())}async function j(){f.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{const s=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(const i of s){const a=document.createElement("div");a.className="session-item"+(i.name===u?" active":""),a.setAttribute("data-session",i.name),a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${i.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
2
- <span>${d(i.name)}</span>
3
- <button class="session-edit-btn" data-session="${d(i.name)}" title="Edit session"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
4
- `,a.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||z(i.name)}),f.appendChild(a);const o=a.querySelector(".session-edit-btn");o&&o.addEventListener("click",l=>{l.stopPropagation(),gt(i.name,o)})}}catch{f.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function d(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function z(e){if(!(u===e&&c)){V(),I.style.display="none",E.style.display="none",v.style.display="",v.classList.add("terminal-pending"),me(),c&&(N(),c.destroy(),c=null),u=e;for(const t of f.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e);try{c=await We(v,e,{terminal:q.terminal,scrollback:q.scrollback,theme:q.theme,renderer:q.renderer}),ge()}catch(t){console.error("[shell] terminal init failed:",t);const n=t&&typeof t=="object"&&"message"in t?t.message:String(t);v.textContent="Failed to open terminal for "+e+": "+n,v.classList.remove("terminal-pending")}}}let ee=[],m=null,Be=!1;function Je(e){if(!m||!m.files.length)return null;const t=m.repoRoot;if(!t||!e.startsWith(t))return null;const n=e.slice(t.length+1);for(const s of m.files)if(s.path===n)return s.status;return null}async function Ze(e){Be=!0;try{const t=await fetch("/api/git/status?path="+encodeURIComponent(e));t.ok?m=await t.json():m=null}catch{m=null}finally{Be=!1}}function Xe(){if(!m||!m.branch)return"";const{branch:e,linesAdded:t,linesRemoved:n}=m;let s=`<div class="sidebar-git"><div class="git-branch-badge">
1
+ import{initTerminal as be}from"./terminal-core.js";const I=window.__TMUX_WEB_SHELL__;let r=null,u=null,ee="sessions",L="",P="",j="",te="";const p=document.getElementById("sidebar-content"),E=document.getElementById("main-placeholder"),v=document.getElementById("terminal-container"),M=document.getElementById("file-editor"),Le=document.getElementById("fe-path"),g=document.getElementById("fe-status"),T=document.getElementById("fe-content"),k=document.getElementById("fe-save"),we=document.getElementById("fe-delete"),Ee=document.getElementById("fe-back"),ne=document.getElementById("fe-new-name"),ke=document.getElementById("fe-new-btn"),K=document.getElementById("settings-backdrop"),se=document.getElementById("settings-popover"),ie=document.querySelector(".sidebar");function Be(){return window.matchMedia("(max-width: 640px)").matches}function X(){Be()&&ie.classList.add("collapsed")}function oe(){ie.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{oe(),ae("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{oe(),ae("files")});function ae(e){ee=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));const t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?S():Re()}async function S(){p.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{const s=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(const i of s){const a=document.createElement("div");a.className="session-item"+(i.name===u?" active":""),a.setAttribute("data-session",i.name),a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${i.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
2
+ <span>${c(i.name)}</span>
3
+ <button class="session-edit-btn" data-session="${c(i.name)}" title="Edit session"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
4
+ `,a.addEventListener("click",d=>{d.target.closest(".session-edit-btn")||A(i.name)}),p.appendChild(a);const o=a.querySelector(".session-edit-btn");o&&o.addEventListener("click",d=>{d.stopPropagation(),je(i.name,o)})}}catch{p.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function c(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function A(e){if(!(u===e&&r)){M.style.display="none",E.style.display="none",v.style.display="",v.classList.add("terminal-pending"),X(),r&&(_(),r.destroy(),r=null),u=e;for(const t of p.querySelectorAll(".session-item"))t.classList.toggle("active",t.getAttribute("data-session")===e);try{r=await be(v,e,{terminal:I.terminal,scrollback:I.scrollback,theme:I.theme,renderer:I.renderer}),ye()}catch(t){console.error("[shell] terminal init failed:",t);const n=t&&typeof t=="object"&&"message"in t?t.message:String(t);v.textContent="Failed to open terminal for "+e+": "+n,v.classList.remove("terminal-pending")}}}let W=[],f=null,le=!1;function xe(e){if(!f||!f.files.length)return null;const t=f.repoRoot;if(!t||!e.startsWith(t))return null;const n=e.slice(t.length+1);for(const s of f.files)if(s.path===n)return s.status;return null}async function Ce(e){le=!0;try{const t=await fetch("/api/git/status?path="+encodeURIComponent(e));t.ok?f=await t.json():f=null}catch{f=null}finally{le=!1}}function Ie(){if(!f||!f.branch)return"";const{branch:e,linesAdded:t,linesRemoved:n}=f;let s=`<div class="sidebar-git"><div class="git-branch-badge">
5
5
  <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>
6
- ${d(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>"}f.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&R&&m&&(e.stopPropagation(),Me())});const W=document.getElementById("header-git"),ye=document.getElementById("git-diff-view"),Ie=document.getElementById("gd-path"),te=document.getElementById("gd-status"),ne=document.getElementById("gd-content"),Ye=document.getElementById("gd-back");let R=null;function D(e){if(!e||!e.branch){W.style.display="none",R=null;return}R=e.repoRoot;const{branch:t,linesAdded:n,linesRemoved:s}=e;let i=`<span class="branch">${d(t)}</span>`;(n>0||s>0)&&(i+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),W.innerHTML=i,W.style.display="block"}W.addEventListener("click",e=>{R&&m&&(e.stopPropagation(),Me())});const y=document.getElementById("git-popover"),se=document.getElementById("git-popover-backdrop");function he(){const e=W.getBoundingClientRect();y.style.top=e.bottom+4+"px",y.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function Ce(){const e=R,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(oe(),e){pe="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));const n=document.getElementById("mode-files");n&&n.classList.add("active"),C(e)}})}let Te="";function Me(){if(!m)return;const e=m.files||[];if(!e.length)y.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
6
+ ${c(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>"}p.addEventListener("click",e=>{e.target.closest(".sidebar-git")&&C&&f&&(e.stopPropagation(),fe())});const H=document.getElementById("header-git"),ce=document.getElementById("git-diff-view"),de=document.getElementById("gd-path"),G=document.getElementById("gd-status"),z=document.getElementById("gd-content"),Me=document.getElementById("gd-back");let C=null;function $(e){if(!e||!e.branch){H.style.display="none",C=null;return}C=e.repoRoot;const{branch:t,linesAdded:n,linesRemoved:s}=e;let i=`<span class="branch">${c(t)}</span>`;(n>0||s>0)&&(i+=`<span class="sep">\xB7</span>+<span class="diff-add">${n}</span> <span class="diff-del">-${s}</span>`),H.innerHTML=i,H.style.display="block"}H.addEventListener("click",e=>{C&&f&&(e.stopPropagation(),fe())});const m=document.getElementById("git-popover"),O=document.getElementById("git-popover-backdrop");function Y(){const e=H.getBoundingClientRect();m.style.top=e.bottom+4+"px",m.style.left=Math.max(8,Math.min(e.left,window.innerWidth-400))+"px"}function re(){const e=C,t=document.getElementById("git-popover-browse");t&&t.addEventListener("click",()=>{if(U(),e){ee="files",document.querySelectorAll(".mode-btn").forEach(s=>s.classList.remove("active"));const n=document.getElementById("mode-files");n&&n.classList.add("active"),w(e)}})}let pe="";function fe(){if(!f)return;const e=f.files||[];if(!e.length)m.innerHTML=`<div class="git-popover-header">No changes \u2014 clean working tree</div>
7
7
  <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++){const s=e[n],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}t+=`<div class="git-popover-item" data-idx="${n}">
8
- ${o}<span class="file-path">${d(s.path)}</span>
8
+ ${o}<span class="file-path">${c(s.path)}</span>
9
9
  <span class="file-add">${i>0?"+"+i:""}</span>
10
10
  <span class="file-del">${a>0?"-"+a:""}</span>
11
- </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',Te=t,y.innerHTML=t,y.querySelectorAll(".git-popover-item").forEach(n=>{const s=parseInt(n.dataset.idx||"0",10),i=e[s];!i||i.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>He(i.path)))})}he(),y.classList.add("open"),se.classList.add("open"),Ce()}function Qe(){return window.innerWidth>=1024}function Se(e,t){const s=(e+(t&&e?`
11
+ </div>`}t+='<div class="git-popover-footer"><button id="git-popover-browse">Browse repository \u2192</button></div>',pe=t,m.innerHTML=t,m.querySelectorAll(".git-popover-item").forEach(n=>{const s=parseInt(n.dataset.idx||"0",10),i=e[s];!i||i.status==="D"||(n.classList.add("clickable"),n.style.cursor="pointer",n.addEventListener("click",()=>me(i.path)))})}Y(),m.classList.add("open"),O.classList.add("open"),re()}function Te(){return window.innerWidth>=1024}function ue(e,t){const s=(e+(t&&e?`
12
12
  `:"")+t).split(`
13
- `);let i="",a=0,o=0;for(const l of s){const r=l;if(r.startsWith("@@")){const h=r.match(/@@\s+-(\d+)[^+]*\+(\d+)/);h&&(a=parseInt(h[1],10)-1,o=parseInt(h[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${d(r)}</span></div>`;continue}if(r.startsWith("diff --git")||r.startsWith("index ")||r.startsWith("--- ")||r.startsWith("+++ ")||r.startsWith("new file")||r.startsWith("deleted file")){r.startsWith("diff --git")&&(i+=`<div class="diff-line header"><span class="ln-body">${d(r)}</span></div>`);continue}if(r.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${d(r)}</span></div>`;else if(r.startsWith("-"))a++,i+=`<div class="diff-line del"><span class="ln-no">${a}</span><span class="ln-body">${d(r)}</span></div>`;else if(r.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${d(r)}</span></div>`;else{if(!r&&!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(r)}</span></div>`}}return{html:i,lineCount:s.length}}let ie=null;async function He(e){const t=R;if(t)if(oe(),V(),Qe()){v.style.display="none",c&&(N(),c.destroy(),c=null,ie=u,u=null),D(null),I.style.display="none",E.style.display="none",ye.style.display="flex",Ie.textContent=e,te.textContent="Loading\u2026",ne.innerHTML="";try{const n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());const s=await n.json();tt(e,s.diff,s.stagedDiff)}catch(n){const s=n instanceof Error?n.message:String(n);te.textContent="Error",ne.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${d(s)}</div>`}}else{y.innerHTML=`<div class="git-diff-view"><div class="diff-header">
13
+ `);let i="",a=0,o=0;for(const d of s){const l=d;if(l.startsWith("@@")){const y=l.match(/@@\s+-(\d+)[^+]*\+(\d+)/);y&&(a=parseInt(y[1],10)-1,o=parseInt(y[2],10)-1),i+=`<div class="diff-line hunk"><span class="ln-body">${c(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">${c(l)}</span></div>`);continue}if(l.startsWith("+"))o++,i+=`<div class="diff-line add"><span class="ln-no">${o}</span><span class="ln-body">${c(l)}</span></div>`;else if(l.startsWith("-"))a++,i+=`<div class="diff-line del"><span class="ln-no">${a}</span><span class="ln-body">${c(l)}</span></div>`;else if(l.startsWith("\\"))i+=`<div class="diff-line"><span class="ln-body">${c(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">${c(l)}</span></div>`}}return{html:i,lineCount:s.length}}let q=null;async function me(e){const t=C;if(t)if(U(),Te()){v.style.display="none",r&&(_(),r.destroy(),r=null,q=u,u=null),$(null),M.style.display="none",E.style.display="none",ce.style.display="flex",de.textContent=e,G.textContent="Loading\u2026",z.innerHTML="";try{const n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());const s=await n.json();He(e,s.diff,s.stagedDiff)}catch(n){const s=n instanceof Error?n.message:String(n);G.textContent="Error",z.innerHTML=`<div class="diff-empty" style="padding:24px;text-align:center;color:var(--panel-muted)">Error: ${c(s)}</div>`}}else{m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
14
14
  <button class="diff-back">\u2190 Back</button>
15
- <span class="diff-filename">${d(e)}</span>
15
+ <span class="diff-filename">${c(e)}</span>
16
16
  <span class="diff-loading">Loading\u2026</span>
17
- </div></div>`,he(),y.classList.add("open"),se.classList.add("open");try{const n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());const s=await n.json();et(e,s.diff,s.stagedDiff)}catch(n){const s=n instanceof Error?n.message:String(n);y.innerHTML=`<div class="git-diff-view"><div class="diff-header">
17
+ </div></div>`,Y(),m.classList.add("open"),O.classList.add("open");try{const n=await fetch("/api/git/diff?path="+encodeURIComponent(t)+"&file="+encodeURIComponent(e));if(!n.ok)throw new Error(await n.text());const s=await n.json();Se(e,s.diff,s.stagedDiff)}catch(n){const s=n instanceof Error?n.message:String(n);m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
18
18
  <button class="diff-back">\u2190 Back</button>
19
- <span class="diff-filename">${d(e)}</span>
20
- </div><div class="diff-empty">Error loading diff: ${d(s)}</div></div>`}}}function et(e,t,n){if(!t&&!n){y.innerHTML=`<div class="git-diff-view"><div class="diff-header">
19
+ <span class="diff-filename">${c(e)}</span>
20
+ </div><div class="diff-empty">Error loading diff: ${c(s)}</div></div>`}}}function Se(e,t,n){if(!t&&!n){m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
21
21
  <button class="diff-back">\u2190 Back</button>
22
- <span class="diff-filename">${d(e)}</span>
23
- </div><div class="diff-empty">No diff content available</div></div>`;return}const{html:s,lineCount:i}=Se(t,n);y.innerHTML=`<div class="git-diff-view"><div class="diff-header">
22
+ <span class="diff-filename">${c(e)}</span>
23
+ </div><div class="diff-empty">No diff content available</div></div>`;return}const{html:s,lineCount:i}=ue(t,n);m.innerHTML=`<div class="git-diff-view"><div class="diff-header">
24
24
  <button class="diff-back">\u2190 Back</button>
25
- <span class="diff-filename">${d(e)}</span>
25
+ <span class="diff-filename">${c(e)}</span>
26
26
  <span class="diff-filesize">${i} lines</span>
27
- </div><div class="diff-content">${s}</div></div>`,y.querySelector(".diff-back")?.addEventListener("click",()=>{y.innerHTML=Te,he(),m&&y.querySelectorAll(".git-popover-item").forEach(a=>{const o=parseInt(a.dataset.idx||"0",10),l=m.files[o];!l||l.status==="D"||(a.style.cursor="pointer",a.addEventListener("click",()=>He(l.path)))}),Ce()})}function tt(e,t,n){if(!t&&!n){te.textContent="No diff",ne.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}=Se(t,n);Ie.textContent=e,te.textContent=i+" lines",ne.innerHTML=s}function nt(){ye.style.display="none",ie?(v.style.display="",v.classList.add("terminal-pending"),z(ie),ie=null):u?(v.style.display="",v.classList.add("terminal-pending"),ge()):E.style.display="flex"}Ye.addEventListener("click",nt);function oe(){y.classList.remove("open"),se.classList.remove("open")}se.addEventListener("click",oe),document.addEventListener("keydown",e=>{e.key==="Escape"&&(oe(),fe())});async function $e(e){try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(n.path){const s=await fetch("/api/git/status?path="+encodeURIComponent(n.path));if(s.ok){const i=await s.json();m=i,D(i);return}}}catch{}m=null,D(null)}let ae=null;function ge(){N(),u&&($e(u),ae=setInterval(()=>{u&&$e(u)},3e4))}function N(){ae&&(clearInterval(ae),ae=null)}function st(){if(ee=q.fsRoots||[],ee.length===0){if(u){it(u);return}f.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}f.innerHTML="";for(const e of ee){const 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> ${d(e)}`,t.addEventListener("click",()=>C(e)),f.appendChild(t)}}async function it(e){f.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();n.path?C(n.path):f.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{f.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function C(e){B=e,f.innerHTML='<div class="file-tree-empty">Loading...</div>';const t=Ze(e);try{const s=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();await t,f.innerHTML="",D(m),f.innerHTML+=Xe();const i=ot(e);if(i&&!ee.some(l=>e===l)){const l=document.createElement("div");l.className="file-tree-item",l.innerHTML="..",l.addEventListener("click",()=>C(i)),f.appendChild(l)}if(s.dirs)for(const o of s.dirs){const l=o.split("/").pop()||o.split("\\").pop(),r=document.createElement("div");r.className="file-tree-item",r.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(l)}`,r.addEventListener("click",()=>C(o)),f.appendChild(r)}else f.innerHTML="";const a=s.files||[];for(const o of a){const l=o.split("/").pop()||o.split("\\").pop(),r=Je(o);let h="",$="";if(r)switch(r){case"M":h="git-mod",$='<span class="git-file-status M">M</span>';break;case"A":h="git-add",$='<span class="git-file-status A">A</span>';break;case"D":h="git-del",$='<span class="git-file-status D">D</span>';break;case"?":h="git-untracked",$='<span class="git-file-status ?">?</span>';break;case"R":h="git-mod",$='<span class="git-file-status R">R</span>';break}const Z=document.createElement("div");Z.className="file-tree-item file"+(h?" "+h:""),Z.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${$}${d(l)}`,Z.addEventListener("click",()=>at(o)),f.appendChild(Z)}}catch{f.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function ot(e){const t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function at(e){X=e,V(),v.style.display="none",c&&(N(),c.destroy(),c=null,u=null),D(null),me(),E.style.display="none",I.style.display="flex",Ge.textContent=e,g.textContent="Loading...",M.style.display="none";try{const t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){const s=await t.json();g.textContent=s.error||"Failed to load",U.value="";return}const n=await t.json();U.value=n.content,Y=n.content,Le=n.content,g.textContent=n.size+" bytes",M.style.display=""}catch{g.textContent="Failed to load",U.value=""}}U.addEventListener("input",()=>{Y=U.value}),M.addEventListener("click",async()=>{M.textContent="Saving...",M.disabled=!0;try{const e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:X,content:Y})});if(!e.ok){const t=await e.json();g.textContent=t.error||"Save failed";return}Le=Y,g.textContent="Saved"}catch{g.textContent="Save failed"}finally{M.disabled=!1,M.textContent="Save"}}),Oe.addEventListener("click",async()=>{if(confirm("Delete "+X+"?"))try{const e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:X})});if(!e.ok){const t=await e.json();g.textContent=t.error||"Delete failed";return}I.style.display="none",E.style.display="flex",B&&C(B)}catch{g.textContent="Delete failed"}}),Ve.addEventListener("click",()=>{I.style.display="none",E.style.display="flex",B&&C(B)}),_e.addEventListener("click",async()=>{const e=Ee.value.trim();if(!e)return;const t=B+"/"+e;try{const n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){const s=await n.json();g.textContent=s.error||"Create failed";return}Ee.value="",B&&C(B)}catch{g.textContent="Create failed"}});const Re=document.getElementById("browser-view"),De=document.getElementById("br-back"),Ne=document.getElementById("br-fwd"),rt=document.getElementById("br-reload"),b=document.getElementById("br-address"),lt=document.getElementById("br-new"),S=document.getElementById("browser-content"),Pe=document.getElementById("mobile-keys");let k=[],p=null,Ae=0,re=!1,P="none",le=null;const ct="https://html.duckduckgo.com/html/?q=";function A(){return p?k.find(e=>e.id===p)??null:null}function ce(e){return`<svg viewBox="0 0 24 24" fill="currentColor" width="${e}" height="${e}"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>`}function dt(e){let t=e.trim();return/^https?:\/\//i.test(t)||/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(t)?t:!/\s/.test(t)&&/\./.test(t)?"https://"+t:/^(localhost|127\.0\.0\.1)(:\d+)?$/i.test(t)?"http://"+t:ct+encodeURIComponent(t)}function G(){Ae++;const e="t"+Ae;return k.push({id:e,title:"New Tab",url:null,history:[],historyIdx:-1,error:null,seq:0}),p=e,w(),de(),e}function w(){if(pe!=="browser")return;let e='<div class="sidebar-section-label">Tabs</div>';for(const n of k){const s=n.id===p?" active":"";e+=`<div class="tab-item${s}" data-tab="${n.id}">
28
- <span class="tab-fav">${ce(16)}</span>
29
- <span class="tab-title">${d(n.title)}</span>
30
- <button class="tab-close" data-close="${n.id}" title="Close tab" aria-label="Close tab">&times;</button>
31
- </div>`}e+='<button class="tab-new" id="tab-new-btn"><svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg> New Tab</button>',f.innerHTML=e,f.querySelectorAll(".tab-item").forEach(n=>{n.addEventListener("click",s=>{s.target.closest(".tab-close")||Fe(n.dataset.tab)})}),f.querySelectorAll(".tab-close").forEach(n=>{n.addEventListener("click",s=>{s.stopPropagation(),ft(n.dataset.close)})});const t=document.getElementById("tab-new-btn");t&&t.addEventListener("click",()=>{G(),b.focus()})}function Fe(e){p=e,w();const t=A();t&&(b.value=t.url||"",F(),t.error?O(t.error):t.url?T(t.id,t.url,!1):de())}function ft(e){const t=k.findIndex(n=>n.id===e);if(t!==-1){if(k.splice(t,1),k.length===0){p=null,w(),de();return}p===e?(p=k[Math.max(0,t-1)].id,Fe(p)):w()}}function F(){const e=A();De.disabled=!e||e.historyIdx<=0,Ne.disabled=!e||e.historyIdx>=e.history.length-1}function de(){S.innerHTML=`<div class="browser-state">
32
- <div class="big">${ce(44)}</div>
33
- <div>Type a URL or search terms in the address bar above.</div>
34
- <div class="hint">Ctrl/Cmd-click any link to open it in a new tab.</div>
35
- </div>`,F()}function O(e){S.innerHTML=`<div class="browser-state">
36
- <div class="big" style="color:#ef4444">${ce(44)}</div>
37
- <div>Could not load page</div>
38
- <div class="hint" style="color:#ef4444">${d(e)}</div>
39
- </div>`}function qe(){S.innerHTML=`<div class="browser-state">
40
- <div class="big">${ce(44)}</div>
41
- <div>Loading\u2026</div>
42
- </div>`}async function T(e,t,n){const s=k.find(o=>o.id===e);if(!s)return;const i=dt(t);if(!i)return;const a=++s.seq;n&&(s.history=s.history.slice(0,s.historyIdx+1),s.history.push(i),s.historyIdx=s.history.length-1),s.url=i,s.error=null,p===e&&(b.value=i,F(),qe());try{const o=await fetch("/api/browse?url="+encodeURIComponent(i)+"&tab="+encodeURIComponent(e)),l=await o.json();if(s.seq!==a)return;if(!o.ok||l.error){const h=l.error||"HTTP "+o.status;s.error=h,p===e&&O(h),w();return}const r=l.finalUrl||i;s.url=r,s.title=l.title||s.title,s.history[s.historyIdx]=r,p===e&&(b.value=r,F(),Ue(l)),w()}catch(o){if(s.seq!==a)return;s.error="Network error: "+(o instanceof Error?o.message:String(o)),p===e&&O(s.error),w()}}function pt(){const e=A();!e||e.historyIdx<=0||(e.historyIdx--,T(e.id,e.history[e.historyIdx],!1))}function ut(){const e=A();!e||e.historyIdx>=e.history.length-1||(e.historyIdx++,T(e.id,e.history[e.historyIdx],!1))}function Ue(e){let t="";e.truncated&&(t+='<div class="browser-bar"><span class="err">Response truncated \u2014 page may be incomplete.</span></div>'),t+=e.html||"",S.innerHTML=t,mt(),S.scrollTop=0}function mt(){S.querySelectorAll("a[href]").forEach(e=>{e.addEventListener("click",t=>{const n=e.getAttribute("href")||"";if(n.startsWith("/api/browse?")){t.preventDefault();const s=t;let i=null;try{i=new URL(n,location.origin).searchParams.get("url")}catch{return}if(!i)return;if(s.ctrlKey||s.metaKey||s.shiftKey){const a=G();T(a,i,!0)}else T(p,i,!0)}else/^(about|blob|file):/i.test(n)&&t.preventDefault()})}),S.querySelectorAll("form").forEach(e=>{e.addEventListener("submit",t=>{t.preventDefault();const n=e.getAttribute("action")||"";if(!n.startsWith("/api/browse/submit?"))return;let s=null;try{s=new URL(n,location.origin).searchParams.get("tab")||p}catch{s=p}if(!s)return;const i=new FormData(e),a=new URLSearchParams;i.forEach((o,l)=>a.append(l,String(o))),vt(s,a)})})}async function vt(e,t){const n=k.find(a=>a.id===e);if(!n)return;const s=++n.seq,i=t.get("__browse_action")||"";p===e&&qe();try{const a=await fetch("/api/browse/submit?tab="+encodeURIComponent(e),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t.toString()}),o=await a.json();if(n.seq!==s)return;if(!a.ok||o.error){const r=o.error||"HTTP "+a.status;n.error=r,p===e&&O(r);return}const l=o.finalUrl||i||n.url||"";n.url=l,n.title=o.title||n.title,n.history=n.history.slice(0,n.historyIdx+1),n.history.push(l),n.historyIdx=n.history.length-1,n.error=null,p===e&&(b.value=l,F(),Ue(o)),w()}catch(a){if(n.seq!==s)return;n.error="Network error: "+(a instanceof Error?a.message:String(a)),p===e&&O(n.error)}}function yt(){if(!re){if(re=!0,I.style.display!=="none"?(P="files",c&&(N(),c.destroy(),c=null)):c||v.style.display!=="none"?(P="terminal",le=u,c&&(N(),c.destroy(),c=null),u=null):P="none",v.style.display="none",I.style.display="none",ye.style.display="none",E.style.display="none",D(null),Pe.style.display="none",Re.style.display="flex",!p)G();else{w();const e=A();e&&(b.value=e.url||"",F(),e.url?T(e.id,e.url,!1):de())}Q()}}function V(){if(re){if(re=!1,Re.style.display="none",Pe.style.display="",P==="files")I.style.display="flex";else if(P==="terminal"&&le){const e=le;le=null,z(e)}else u?(v.style.display="",v.classList.add("terminal-pending"),ge()):E.style.display="flex";P="none"}}De.addEventListener("click",pt),Ne.addEventListener("click",ut),rt.addEventListener("click",()=>{const e=A();e&&e.url&&T(e.id,e.url,!1)}),lt.addEventListener("click",()=>{G(),b.focus(),b.select()}),b.addEventListener("keydown",e=>{if(e.key==="Enter"){e.preventDefault();const t=p||G();T(t,b.value,!0),b.blur()}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=ke.classList.toggle("open");ue.classList.toggle("open",e)}),ue.addEventListener("click",()=>{ke.classList.remove("open"),ue.classList.remove("open")});let H="";const _=document.createElement("div");_.className="session-popover-backdrop",document.body.appendChild(_);const x=document.createElement("div");x.id="session-popover",x.className="session-popover",x.style.display="none",x.innerHTML=`<div class="sp-field">
27
+ </div><div class="diff-content">${s}</div></div>`,m.querySelector(".diff-back")?.addEventListener("click",()=>{m.innerHTML=pe,Y(),f&&m.querySelectorAll(".git-popover-item").forEach(a=>{const o=parseInt(a.dataset.idx||"0",10),d=f.files[o];!d||d.status==="D"||(a.style.cursor="pointer",a.addEventListener("click",()=>me(d.path)))}),re()})}function He(e,t,n){if(!t&&!n){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}=ue(t,n);de.textContent=e,G.textContent=i+" lines",z.innerHTML=s}function $e(){ce.style.display="none",q?(v.style.display="",v.classList.add("terminal-pending"),A(q),q=null):u?(v.style.display="",v.classList.add("terminal-pending"),ye()):E.style.display="flex"}Me.addEventListener("click",$e);function U(){m.classList.remove("open"),O.classList.remove("open")}O.addEventListener("click",U),document.addEventListener("keydown",e=>{e.key==="Escape"&&(U(),J())});async function ve(e){try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();if(n.path){const s=await fetch("/api/git/status?path="+encodeURIComponent(n.path));if(s.ok){const i=await s.json();f=i,$(i);return}}}catch{}f=null,$(null)}let V=null;function ye(){_(),u&&(ve(u),V=setInterval(()=>{u&&ve(u)},3e4))}function _(){V&&(clearInterval(V),V=null)}function Re(){if(W=I.fsRoots||[],W.length===0){if(u){De(u);return}p.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}p.innerHTML="";for(const e of W){const 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> ${c(e)}`,t.addEventListener("click",()=>w(e)),p.appendChild(t)}}async function De(e){p.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();n.path?w(n.path):p.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{p.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function w(e){L=e,p.innerHTML='<div class="file-tree-empty">Loading...</div>';const t=Ce(e);try{const s=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();await t,p.innerHTML="",$(f),p.innerHTML+=Ie();const i=Ne(e);if(i&&!W.some(d=>e===d)){const d=document.createElement("div");d.className="file-tree-item",d.innerHTML="..",d.addEventListener("click",()=>w(i)),p.appendChild(d)}if(s.dirs)for(const o of s.dirs){const d=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> ${c(d)}`,l.addEventListener("click",()=>w(o)),p.appendChild(l)}else p.innerHTML="";const a=s.files||[];for(const o of a){const d=o.split("/").pop()||o.split("\\").pop(),l=xe(o);let y="",x="";if(l)switch(l){case"M":y="git-mod",x='<span class="git-file-status M">M</span>';break;case"A":y="git-add",x='<span class="git-file-status A">A</span>';break;case"D":y="git-del",x='<span class="git-file-status D">D</span>';break;case"?":y="git-untracked",x='<span class="git-file-status ?">?</span>';break;case"R":y="git-mod",x='<span class="git-file-status R">R</span>';break}const F=document.createElement("div");F.className="file-tree-item file"+(y?" "+y:""),F.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${x}${c(d)}`,F.addEventListener("click",()=>Fe(o)),p.appendChild(F)}}catch{p.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function Ne(e){const t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Fe(e){P=e,v.style.display="none",r&&(_(),r.destroy(),r=null,u=null),$(null),X(),E.style.display="none",M.style.display="flex",Le.textContent=e,g.textContent="Loading...",k.style.display="none";try{const t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){const s=await t.json();g.textContent=s.error||"Failed to load",T.value="";return}const n=await t.json();T.value=n.content,j=n.content,te=n.content,g.textContent=n.size+" bytes",k.style.display=""}catch{g.textContent="Failed to load",T.value=""}}T.addEventListener("input",()=>{j=T.value}),k.addEventListener("click",async()=>{k.textContent="Saving...",k.disabled=!0;try{const e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:P,content:j})});if(!e.ok){const t=await e.json();g.textContent=t.error||"Save failed";return}te=j,g.textContent="Saved"}catch{g.textContent="Save failed"}finally{k.disabled=!1,k.textContent="Save"}}),we.addEventListener("click",async()=>{if(confirm("Delete "+P+"?"))try{const e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:P})});if(!e.ok){const t=await e.json();g.textContent=t.error||"Delete failed";return}M.style.display="none",E.style.display="flex",L&&w(L)}catch{g.textContent="Delete failed"}}),Ee.addEventListener("click",()=>{M.style.display="none",E.style.display="flex",L&&w(L)}),ke.addEventListener("click",async()=>{const e=ne.value.trim();if(!e)return;const t=L+"/"+e;try{const n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){const s=await n.json();g.textContent=s.error||"Create failed";return}ne.value="",L&&w(L)}catch{g.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=se.classList.toggle("open");K.classList.toggle("open",e)}),K.addEventListener("click",()=>{se.classList.remove("open"),K.classList.remove("open")});let B="";const R=document.createElement("div");R.className="session-popover-backdrop",document.body.appendChild(R);const b=document.createElement("div");b.id="session-popover",b.className="session-popover",b.style.display="none",b.innerHTML=`<div class="sp-field">
43
28
  <label>Rename session</label>
44
29
  <input type="text" id="sp-name" />
45
30
  </div>
@@ -48,5 +33,5 @@ import{initTerminal as We}from"./terminal-core.js";const q=window.__TMUX_WEB_SHE
48
33
  <button class="btn" id="sp-cancel">Cancel</button>
49
34
  </div>
50
35
  <hr class="sp-divider" />
51
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(x);function ht(e){const t=e.getBoundingClientRect();x.style.top=t.bottom+4+"px",x.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function gt(e,t){H=e;const n=document.getElementById("sp-name");n.value=e,_.classList.add("open"),x.style.display="block",ht(t),setTimeout(()=>n.focus(),50)}function K(){_.classList.remove("open"),x.style.display="none"}_.addEventListener("click",K),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===H){K();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:H,newName:e})})).ok)return;K(),j(),u===H&&z(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+H+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:H})})).ok)return;K(),u===H&&(c&&(c.destroy(),c=null),u=null,v.style.display="none",E.style.display="flex"),j()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",K);const bt={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const t=e.target.closest("button[data-key]");if(!t||!c)return;const n=bt[t.dataset.key];n&&(c.sendInput(n),c.focus())});const L=document.getElementById("mk-input"),je=document.getElementById("mk-send");function ze(){if(!L||!c)return;const e=L.value;e&&(c.sendInput(e+"\r"),L.value="",L.rows=1,c.focus())}if(L&&(L.addEventListener("input",()=>{L.rows=1;const e=L.value.split(`
52
- `).length;L.rows=Math.min(e,4)}),L.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),ze())})),je&&je.addEventListener("click",ze),window.visualViewport){const 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})}const be=document.getElementById("process-panel"),J=document.getElementById("proc-list"),wt=document.getElementById("proc-close"),we=document.getElementById("proc-backdrop"),Lt=document.getElementById("header-ram");function Et(){J.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch("/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 t="";for(const n of e){const s=n.rss,i=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">'+i+'</span><span class="proc-cmd" title="'+d(n.command)+'">'+d(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}J.innerHTML=t;for(const n of J.querySelectorAll(".proc-row")){const s=n.querySelector(".proc-kill");s&&s.addEventListener("click",i=>{const a=i.target.closest(".proc-row");!a||!confirm("Kill PID "+a.dataset.pid+"?")||fetch("/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 kt(){be.classList.add("open"),we.classList.add("open"),Et()}function fe(){be.classList.remove("open"),we.classList.remove("open")}Lt.addEventListener("click",e=>{e.stopPropagation(),be.classList.contains("open")?fe():kt()}),wt.addEventListener("click",fe),we.addEventListener("click",fe),window.__openSession=z,window.__refreshSidebar=j,j(),me();
36
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(b);function Pe(e){const t=e.getBoundingClientRect();b.style.top=t.bottom+4+"px",b.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function je(e,t){B=e;const n=document.getElementById("sp-name");n.value=e,R.classList.add("open"),b.style.display="block",Pe(t),setTimeout(()=>n.focus(),50)}function D(){R.classList.remove("open"),b.style.display="none"}R.addEventListener("click",D),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===B){D();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:B,newName:e})})).ok)return;D(),S(),u===B&&A(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+B+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:B})})).ok)return;D(),u===B&&(r&&(r.destroy(),r=null),u=null,v.style.display="none",E.style.display="flex"),S()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",D);const Ae={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ",enter:"\r",exit:"exit\r",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const t=e.target.closest("button[data-key]");if(!t||!r)return;const n=Ae[t.dataset.key];n&&(r.sendInput(n),r.focus())});const h=document.getElementById("mk-input"),ge=document.getElementById("mk-send");function he(){if(!h||!r)return;const e=h.value;e&&(r.sendInput(e+"\r"),h.value="",h.rows=1,r.focus())}if(h&&(h.addEventListener("input",()=>{h.rows=1;const e=h.value.split(`
37
+ `).length;h.rows=Math.min(e,4)}),h.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),he())})),ge&&ge.addEventListener("click",he),window.visualViewport){const 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})}const Z=document.getElementById("process-panel"),N=document.getElementById("proc-list"),We=document.getElementById("proc-close"),Q=document.getElementById("proc-backdrop"),Ge=document.getElementById("header-ram");function ze(){N.innerHTML='<div class="proc-empty">Loading\u2026</div>',fetch("/api/system/processes").then(e=>e.json()).then(e=>{if(!Array.isArray(e)||!e.length){N.innerHTML='<div class="proc-empty">No processes</div>';return}let t="";for(const n of e){const s=n.rss,i=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">'+i+'</span><span class="proc-cmd" title="'+c(n.command)+'">'+c(n.command)+'</span><button class="proc-kill" title="Kill PID '+n.pid+'">&times;</button></div>'}N.innerHTML=t;for(const n of N.querySelectorAll(".proc-row")){const s=n.querySelector(".proc-kill");s&&s.addEventListener("click",i=>{const a=i.target.closest(".proc-row");!a||!confirm("Kill PID "+a.dataset.pid+"?")||fetch("/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(()=>{N.innerHTML='<div class="proc-empty">Failed to load</div>'})}function Oe(){Z.classList.add("open"),Q.classList.add("open"),ze()}function J(){Z.classList.remove("open"),Q.classList.remove("open")}Ge.addEventListener("click",e=>{e.stopPropagation(),Z.classList.contains("open")?J():Oe()}),We.addEventListener("click",J),Q.addEventListener("click",J),window.__openSession=A,window.__refreshSidebar=S,S(),X();