tmux-weblink 1.0.4 → 1.1.0

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.
@@ -0,0 +1,4 @@
1
+ var dt="https://esm.sh/ghostty-web@0.4.0",mt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function k(a){return a.replace(mt,"")}var S;function ft(){return S||(document.querySelector("link[data-tmux-web-xterm-css]")?S=Promise.resolve():(S=new Promise(a=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>a(),{once:!0}),e.addEventListener("error",()=>a(),{once:!0}),document.head.appendChild(e)}),S))}var X=class j{terminal;fitAddon;container;constructor(e,i,r){this.container=e,this.terminal=i,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,i,r){let[{Terminal:s},{FitAddon:d}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),ft()]),p=new s({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new j(e,p,new d)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=q(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))}},pt=class N{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,i){this.container=e,this.terminal=i,this.terminal.open(e)}static async create(e,i,r){let s=await import(dt);await s.init();let d=new s.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new N(e,d)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){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=q(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let i=Math.floor(e.width/this.charW),r=Math.floor(e.height/this.charH);if(i<10||r<5)return!1;if(i!==this.colsValue||r!==this.rowsValue){this.colsValue=i,this.rowsValue=r,this.terminal.resize(i,r);for(let s of this.resizeCallbacks)s({cols:i,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let i=this.terminal.getMode?.(2004)??!1;this.input(i?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(i=>e(i))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}updateCellMetrics(e=!1){let i=this.container.querySelector("canvas");if(i instanceof HTMLElement&&i.offsetWidth>0&&i.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let r=i.offsetWidth/this.colsValue,s=i.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=r,this.charH=s)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function q(a){let e=a.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(e.width,i.width),height:Math.max(0,Math.min(e.height,i.height-Math.max(0,e.top)))}:e}function wt(a,e,i){return a.innerHTML="",a.classList.add("terminal-pending"),(async()=>{let r;if(i.renderer==="ghostty")try{r=await pt.create(a,i.scrollback,i.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),r=await X.create(a,i.scrollback,i.theme)}else r=await X.create(a,i.scrollback,i.theme);let s,d=0,p,l=null,M=0,m="connecting",b=0,E=!1,T=[],f="",D=!1,w=1e3,O=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(e),U="/api/session/"+encodeURIComponent(e)+"/upload",J=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function _(){return T.join("")+f}async function G(t,n=0){let o=t?r.viewportY():0,u=t?r.baseY():0,h=k(_());if(r.reset(),!h){t||r.scrollToBottom();return}await r.write(h),t?r.scrollToLine(u+o+n):r.scrollToBottom()}function v(t){s?.readyState===WebSocket.OPEN&&s.send(JSON.stringify(t))}function Q(t){let n;try{n=JSON.parse(t)}catch{m==="live"&&(f+=t,r.write(k(t)));return}if(n.type==="auth.required"){let o=localStorage.getItem("tmux-web-token");o?v({type:"auth.token",token:o}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(n.type==="auth.ok"){n.token&&localStorage.setItem("tmux-web-token",n.token),w=1e3,g(),v({type:"resize",cols:r.cols,rows:r.rows}),c();return}if(n.type==="auth.failed"){if(n.permanentLock||n.retryAfterMs&&n.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(n.message);return}return}if(n.type==="snapshot"&&typeof n.data=="string"){let o=k(n.data);T=[o],f="",b=typeof n.lines=="number"?n.lines:i.terminal.initialLines,m="live",r.reset(),r.write(o).then(()=>r.scrollToBottom());return}if(n.type==="window_changed"&&typeof n.activeIndex=="number"){let o=new URL(location.href);o.searchParams.get("window")!==String(n.activeIndex)&&(o.searchParams.set("window",String(n.activeIndex)),history.replaceState(history.state,"",o)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:n.windows}));return}if(n.type==="history"&&typeof n.data=="string"){E=!1,n.lines>0&&n.data&&(T.unshift(n.data),b+=n.lines,G(!0,n.lines));return}if(n.type==="data"&&typeof n.data=="string"){let o=k(n.data);if(m==="connecting"){m="live",f=o,r.write(o);return}m==="live"&&(f+=o,r.write(o))}}function z(){let t=O,n=localStorage.getItem("tmux-web-token");n&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(n)),s=new WebSocket(t),s.onopen=()=>{m="connecting",b=0,E=!1,T=[],f="",r.reset(),w=1e3},s.onmessage=o=>{typeof o.data=="string"&&Q(o.data)},s.onclose=()=>{D||(m="connecting",setTimeout(()=>{w=Math.min(w*2,1e4),D||z()},w))},s.onerror=()=>s?.close()}function x(t){v({type:"input",data:t})}function g(){return r.fit()}function Y(){a.classList.remove("terminal-pending")}function c(){d&&cancelAnimationFrame(d),p&&clearTimeout(p),g(),d=requestAnimationFrame(()=>{d=0,g()&&Y()}),p=setTimeout(()=>{g()&&Y()},120)}function Z(){c(),setTimeout(c,50),setTimeout(c,150),setTimeout(c,300)}function B(t){return typeof t=="string"&&t.startsWith("image/")}function H(t){return t?.files?.length?t.files[0]??null:null}function $(t){if(!t?.items)return null;for(let n=0;n<t.items.length;n++){let o=t.items[n];if(o?.kind==="file"&&B(o.type))return o.getAsFile()}return null}async function tt(t){let n=new FormData,o=t instanceof File&&t.name?t.name:"upload";n.append("file",t,o);let u=localStorage.getItem("tmux-web-token"),h={};u&&(h.Authorization="Bearer "+u);let C=await fetch(U,{method:"POST",body:n,headers:h});if(!C.ok){let R="upload failed";try{let K=await C.json();K.error&&(R=K.error)}catch{}throw new Error(R)}let W=await C.json();if(!W.path)throw new Error("no path in response");return W.path}async function P(t){try{let n=await tt(t);x(n)}catch{}}r.onData(t=>x(t)),r.onResize(({cols:t,rows:n})=>v({type:"resize",cols:t,rows:n})),r.onScroll(()=>{m!=="live"||E||!r.isNearScrollbackTop()||(E=!0,v({type:"load_history",before:b}))}),r.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let n=await navigator.clipboard.read();for(let o of n)for(let u of o.types)if(B(u)){let h=await o.getType(u);await P(h);return}}}catch{}try{let n=await navigator.clipboard?.readText();n&&r.pasteText(A(n))}catch{}})(),!1):!0);let L=0;function A(t){return t.split(`\r
2
+ `).join(`
3
+ `).split(`
4
+ `).join("\r")}async function et(t){let n=$(t.clipboardData);if(n){t.preventDefault(),t.stopPropagation();let h=Date.now();if(h-L<50)return;L=h,await P(n);return}let o=t.clipboardData?.getData("text/plain");if(!o)return;t.preventDefault(),t.stopPropagation();let u=Date.now();u-L<50||(L=u,r.pasteText(A(o)))}let y=0;function nt(t){t.preventDefault(),y++,H(t.dataTransfer)&&a.classList.add("terminal-drag-over")}function rt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function it(t){t.preventDefault(),y--,y<=0&&(y=0,a.classList.remove("terminal-drag-over"))}async function ot(t){t.preventDefault(),y=0,a.classList.remove("terminal-drag-over");let n=H(t.dataTransfer);n&&await P(n)}function V(t){!J||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||r.isFocused()&&(t.preventDefault(),t.stopPropagation(),x("\x1B"))}a.addEventListener("paste",et),a.addEventListener("dragenter",nt),a.addEventListener("dragover",rt),a.addEventListener("dragleave",it),a.addEventListener("drop",ot),document.addEventListener("keydown",V,!0);function at(t){if(t.touches.length!==1){l=null;return}let n=t.touches[0];n&&(l={startX:n.clientX,startY:n.clientY,lastX:n.clientX,lastY:n.clientY,scrolling:!1},t.stopPropagation())}function st(t){if(!l||t.touches.length!==1)return;let n=t.touches[0];if(!n)return;let o=n.clientY-l.startY,u=n.clientX-l.startX;if(!l.scrolling){if(Math.abs(o)<8||Math.abs(o)<Math.abs(u))return;l.scrolling=!0,M=Date.now()+500}t.preventDefault(),t.stopPropagation(),ht(-(n.clientY-l.lastY),n.clientX,n.clientY),l.lastX=n.clientX,l.lastY=n.clientY}function lt(t){if(!l)return;let n=l.scrolling;l=null,t.stopPropagation(),n?(M=Date.now()+500,t.preventDefault()):r.focus()}function ct(t){l=null,t.stopPropagation()}function ut(t){t.pointerType==="touch"&&Date.now()<M&&(t.preventDefault(),t.stopPropagation())}function ht(t,n,o){(a.querySelector(".xterm-screen")??a).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:n,clientY:o,bubbles:!0,cancelable:!0}))}a.addEventListener("touchstart",at,{passive:!0,capture:!0}),a.addEventListener("touchmove",st,{passive:!1,capture:!0}),a.addEventListener("touchend",lt,{passive:!1,capture:!0}),a.addEventListener("touchcancel",ct,{passive:!0,capture:!0}),a.addEventListener("pointerup",ut,!0);function I(){c()}window.addEventListener("resize",I),window.visualViewport?.addEventListener("resize",c),window.visualViewport?.addEventListener("scroll",c);let F=new ResizeObserver(()=>c());return F.observe(a),document.fonts?.ready.then(()=>{c(),setTimeout(c,50),setTimeout(c,200)}),a.addEventListener("focusin",Z,!0),requestAnimationFrame(()=>{g()&&Y(),z(),c()}),{destroy(){if(D=!0,s){try{s.close(1e3,"session switch")}catch{}s=void 0}a.innerHTML="",a.classList.remove("terminal-pending"),window.removeEventListener("resize",I),F.disconnect(),document.removeEventListener("keydown",V,!0)},focus(){r.focus()},sendInput(t){x(t)}}})()}export{wt as a};
@@ -1,4 +1,4 @@
1
- import{a as F}from"./chunk-7KLKPQJJ.js";var h=window.__TMUX_WEB_SHELL__,l=null,c=null,D="sessions",m="",B="",x="",H="",i=document.getElementById("sidebar-content"),g=document.getElementById("main-placeholder"),f=document.getElementById("terminal-container"),k=document.getElementById("file-editor"),P=document.getElementById("fe-path"),o=document.getElementById("fe-status"),E=document.getElementById("fe-content"),y=document.getElementById("fe-save"),R=document.getElementById("fe-delete"),V=document.getElementById("fe-back"),S=document.getElementById("fe-new-name"),$=document.getElementById("fe-new-btn"),T=document.getElementById("settings-backdrop"),N=document.getElementById("settings-popover"),j=document.querySelector(".sidebar");function J(){return window.matchMedia("(max-width: 640px)").matches}function O(){J()&&j.classList.add("collapsed")}function z(){j.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{z(),_("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{z(),_("files")});function _(e){D=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?L():U()}async function L(){i.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 a=document.createElement("div");a.className="session-item"+(n.name===c?" active":""),a.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>
1
+ import{a as F}from"./chunk-O3DBTEWH.js";var h=window.__TMUX_WEB_SHELL__,l=null,c=null,D="sessions",m="",B="",x="",H="",i=document.getElementById("sidebar-content"),g=document.getElementById("main-placeholder"),f=document.getElementById("terminal-container"),k=document.getElementById("file-editor"),P=document.getElementById("fe-path"),o=document.getElementById("fe-status"),E=document.getElementById("fe-content"),y=document.getElementById("fe-save"),R=document.getElementById("fe-delete"),V=document.getElementById("fe-back"),S=document.getElementById("fe-new-name"),$=document.getElementById("fe-new-btn"),T=document.getElementById("settings-backdrop"),N=document.getElementById("settings-popover"),j=document.querySelector(".sidebar");function J(){return window.matchMedia("(max-width: 640px)").matches}function O(){J()&&j.classList.add("collapsed")}function z(){j.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{z(),_("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{z(),_("files")});function _(e){D=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?L():U()}async function L(){i.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 a=document.createElement("div");a.className="session-item"+(n.name===c?" active":""),a.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>
2
2
  <span>${b(n.name)}</span>
3
3
  <button class="session-edit-btn" data-session="${b(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>
4
4
  `,a.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||M(n.name)}),i.appendChild(a);let s=a.querySelector(".session-edit-btn");s&&s.addEventListener("click",p=>{p.stopPropagation(),Z(n.name,s)})}}catch{i.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 b(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function M(e){if(!(c===e&&l)){k.style.display="none",g.style.display="none",f.style.display="",f.classList.add("terminal-pending"),O(),l&&(l.destroy(),l=null),c=e;try{l=await F(f,e,{terminal:h.terminal,scrollback:h.scrollback,theme:h.theme,renderer:h.renderer})}catch(t){console.error("[shell] terminal init failed:",t);let n=t&&typeof t=="object"&&"message"in t?t.message:String(t);f.textContent="Failed to open terminal for "+e+": "+n,f.classList.remove("terminal-pending")}}}var I=[];function U(){if(I=h.fsRoots||[],I.length===0){if(c){q(c);return}i.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}i.innerHTML="";for(let e of I){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> ${b(e)}`,t.addEventListener("click",()=>u(e)),i.appendChild(t)}}async function q(e){i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();t.path?u(t.path):i.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{i.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function u(e){m=e,i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();i.innerHTML="";let n=A(e);if(n&&!I.some(s=>e===s)){let s=document.createElement("div");s.className="file-tree-item",s.innerHTML="..",s.addEventListener("click",()=>u(n)),i.appendChild(s)}if(t.dirs)for(let s of t.dirs){let p=s.split("/").pop()||s.split("\\").pop(),d=document.createElement("div");d.className="file-tree-item",d.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> ${b(p)}`,d.addEventListener("click",()=>u(s)),i.appendChild(d)}else i.innerHTML="";let a=t.files||[];for(let s of a){let p=s.split("/").pop()||s.split("\\").pop(),d=document.createElement("div");d.className="file-tree-item file",d.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${b(p)}`,d.addEventListener("click",()=>W(s)),i.appendChild(d)}}catch{i.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function A(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function W(e){B=e,f.style.display="none",l&&(l.destroy(),l=null,c=null),O(),g.style.display="none",k.style.display="flex",P.textContent=e,o.textContent="Loading...",y.style.display="none";try{let t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){let a=await t.json();o.textContent=a.error||"Failed to load",E.value="";return}let n=await t.json();E.value=n.content,x=n.content,H=n.content,o.textContent=n.size+" bytes",y.style.display=""}catch{o.textContent="Failed to load",E.value=""}}E.addEventListener("input",()=>{x=E.value}),y.addEventListener("click",async()=>{y.textContent="Saving...",y.disabled=!0;try{let e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B,content:x})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Save failed";return}H=x,o.textContent="Saved"}catch{o.textContent="Save failed"}finally{y.disabled=!1,y.textContent="Save"}}),R.addEventListener("click",async()=>{if(confirm("Delete "+B+"?"))try{let e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Delete failed";return}k.style.display="none",g.style.display="flex",m&&u(m)}catch{o.textContent="Delete failed"}}),V.addEventListener("click",()=>{k.style.display="none",g.style.display="flex",m&&u(m)}),$.addEventListener("click",async()=>{let e=S.value.trim();if(!e)return;let t=m+"/"+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 a=await n.json();o.textContent=a.error||"Create failed";return}S.value="",m&&u(m)}catch{o.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=N.classList.toggle("open");T.classList.toggle("open",e)}),T.addEventListener("click",()=>{N.classList.remove("open"),T.classList.remove("open")});var v="",w=document.createElement("div");w.className="session-popover-backdrop",document.body.appendChild(w);var r=document.createElement("div");r.id="session-popover",r.className="session-popover",r.style.display="none",r.innerHTML=`<div class="sp-field">
@@ -1 +1 @@
1
- import{a as t}from"./chunk-7KLKPQJJ.js";var e=window.__TMUX_WEB_TERMINAL__;if(!e)throw new Error("missing tmux-web terminal config");var r=document.getElementById("terminal-container");if(!r)throw new Error("missing terminal container");var{sessionName:o,...m}=e;t(r,o,m).then(n=>{window.tmuxWeb={sendInput:i=>n.sendInput(i),focusTerminal:()=>n.focus()}});
1
+ import{a as t}from"./chunk-O3DBTEWH.js";var e=window.__TMUX_WEB_TERMINAL__;if(!e)throw new Error("missing tmux-web terminal config");var r=document.getElementById("terminal-container");if(!r)throw new Error("missing terminal container");var{sessionName:o,...m}=e;t(r,o,m).then(n=>{window.tmuxWeb={sendInput:i=>n.sendInput(i),focusTerminal:()=>n.focus()}});
@@ -1,4 +1,4 @@
1
- const pt="https://esm.sh/ghostty-web@0.4.0",wt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function M(a){return a.replace(wt,"")}let E;function gt(){return E||(document.querySelector("link[data-tmux-web-xterm-css]")?E=Promise.resolve():(E=new Promise(e=>{const r=document.createElement("link");r.rel="stylesheet",r.href="/assets/xterm.css",r.setAttribute("data-tmux-web-xterm-css",""),r.addEventListener("load",()=>e(),{once:!0}),r.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(r)}),E))}class Y{terminal;fitAddon;container;constructor(e,r,i){this.container=e,this.terminal=r,this.fitAddon=i,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,r,i){const[{Terminal:s},{FitAddon:m}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),gt()]),g=new s({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:r,convertEol:!1,theme:i});return new Y(e,g,new m)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(r=>this.terminal.write(e,r))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){const e=N(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){const e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}}class R{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,r){this.container=e,this.terminal=r,this.terminal.open(e)}static async create(e,r,i){const s=await import(pt);await s.init();const m=new s.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:r,convertEol:!1,theme:i});return new R(e,m)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(r=>this.terminal.write(e,r))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){const e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){const e=N(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);const r=Math.floor(e.width/this.charW),i=Math.floor(e.height/this.charH);if(r<10||i<5)return!1;if(r!==this.colsValue||i!==this.rowsValue){this.colsValue=r,this.rowsValue=i,this.terminal.resize(r,i);for(const s of this.resizeCallbacks)s({cols:r,rows:i})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){const r=this.terminal.getMode?.(2004)??!1;this.input(r?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(r=>e(r))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){const e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}updateCellMetrics(e=!1){const r=this.container.querySelector("canvas");if(r instanceof HTMLElement&&r.offsetWidth>0&&r.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){const i=r.offsetWidth/this.colsValue,s=r.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=i,this.charH=s)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}}function N(a){const e=a.getBoundingClientRect(),r=window.visualViewport;return r?{width:Math.min(e.width,r.width),height:Math.max(0,Math.min(e.height,r.height-Math.max(0,e.top)))}:e}function vt(a,e,r){return a.innerHTML="",a.classList.add("terminal-pending"),(async()=>{let i;if(r.renderer==="ghostty")try{i=await R.create(a,r.scrollback,r.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),i=await Y.create(a,r.scrollback,r.theme)}else i=await Y.create(a,r.scrollback,r.theme);let s,m=0,g,l=null,C=0,p="connecting",L=0,S=!1,k=[],w="",P=!1,y=1e3,F=!1,h="",f;const J=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(e),G="/api/session/"+encodeURIComponent(e)+"/upload",Q=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function Z(){return k.join("")+w}async function $(t,n=0){const o=t?i.viewportY():0,u=t?i.baseY():0,d=M(Z());if(i.reset(),!d){t||i.scrollToBottom();return}await i.write(d),t?i.scrollToLine(u+o+n):i.scrollToBottom()}function v(t){s?.readyState===WebSocket.OPEN&&s.send(JSON.stringify(t))}function tt(t){let n;try{n=JSON.parse(t)}catch{p==="live"&&(w+=t,i.write(M(t)));return}if(n.type==="auth.required"){const o=localStorage.getItem("tmux-web-token");o?v({type:"auth.token",token:o}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(n.type==="auth.ok"){n.token&&localStorage.setItem("tmux-web-token",n.token),y=1e3,b(),v({type:"resize",cols:i.cols,rows:i.rows}),c();return}if(n.type==="auth.failed"){if(n.permanentLock||n.retryAfterMs&&n.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(n.message);return}return}if(n.type==="snapshot"&&typeof n.data=="string"){const o=M(n.data);k=[o],w="",L=typeof n.lines=="number"?n.lines:r.terminal.initialLines,p="live",i.reset(),i.write(o).then(()=>i.scrollToBottom());return}if(n.type==="window_changed"&&typeof n.activeIndex=="number"){const o=new URL(location.href);o.searchParams.get("window")!==String(n.activeIndex)&&(o.searchParams.set("window",String(n.activeIndex)),history.replaceState(history.state,"",o)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:n.windows}));return}if(n.type==="history"&&typeof n.data=="string"){S=!1,n.lines>0&&n.data&&(k.unshift(n.data),L+=n.lines,$(!0,n.lines));return}if(n.type==="data"&&typeof n.data=="string"){const o=M(n.data);if(p==="connecting"){p="live",w=o,i.write(o);return}p==="live"&&(w+=o,i.write(o))}}function W(){let t=J;const n=localStorage.getItem("tmux-web-token");n&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(n)),s=new WebSocket(t),s.onopen=()=>{p="connecting",L=0,S=!1,k=[],w="",i.reset(),y=1e3},s.onmessage=o=>{typeof o.data=="string"&&tt(o.data)},s.onclose=()=>{P||(p="connecting",setTimeout(()=>{y=Math.min(y*2,1e4),P||W()},y))},s.onerror=()=>s?.close()}function T(t){v({type:"input",data:t})}function I(){h&&(T(h),h="")}function b(){return i.fit()}function z(){a.classList.remove("terminal-pending")}function c(){m&&cancelAnimationFrame(m),g&&clearTimeout(g),b(),m=requestAnimationFrame(()=>{m=0,b()&&z()}),g=setTimeout(()=>{b()&&z()},120)}function et(){c(),setTimeout(c,50),setTimeout(c,150),setTimeout(c,300)}function A(t){return typeof t=="string"&&t.startsWith("image/")}function V(t){return t?.files?.length?t.files[0]??null:null}function nt(t){if(!t?.items)return null;for(let n=0;n<t.items.length;n++){const o=t.items[n];if(o?.kind==="file"&&A(o.type))return o.getAsFile()}return null}async function rt(t){const n=new FormData,o=t instanceof File&&t.name?t.name:"upload";n.append("file",t,o);const u=localStorage.getItem("tmux-web-token"),d={};u&&(d.Authorization="Bearer "+u);const B=await fetch(G,{method:"POST",body:n,headers:d});if(!B.ok){let j="upload failed";try{const q=await B.json();q.error&&(j=q.error)}catch{}throw new Error(j)}const _=await B.json();if(!_.path)throw new Error("no path in response");return _.path}async function H(t){try{const n=await rt(t);T(n)}catch{}}a.addEventListener("compositionstart",()=>{F=!0},!0),a.addEventListener("compositionend",t=>{F=!1,clearTimeout(f),f=void 0,h="",t.data&&T(t.data)},!0),i.onData(t=>{if(!F){if(h&&t.startsWith(h)&&t.length>h.length){h=t,clearTimeout(f),f=setTimeout(()=>{I(),f=void 0},40);return}h&&(clearTimeout(f),f=void 0,I()),clearTimeout(f),h=t,f=setTimeout(()=>{I(),f=void 0},15)}}),i.onResize(({cols:t,rows:n})=>v({type:"resize",cols:t,rows:n})),i.onScroll(()=>{p!=="live"||S||!i.isNearScrollbackTop()||(S=!0,v({type:"load_history",before:L}))}),i.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){const n=await navigator.clipboard.read();for(const o of n)for(const u of o.types)if(A(u)){const d=await o.getType(u);await H(d);return}}}catch{}try{const n=await navigator.clipboard?.readText();n&&i.pasteText(O(n))}catch{}})(),!1):!0);let D=0;function O(t){return t.split(`\r
1
+ const ht="https://esm.sh/ghostty-web@0.4.0",ft=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function k(o){return o.replace(ft,"")}let b;function dt(){return b||(document.querySelector("link[data-tmux-web-xterm-css]")?b=Promise.resolve():(b=new Promise(e=>{const n=document.createElement("link");n.rel="stylesheet",n.href="/assets/xterm.css",n.setAttribute("data-tmux-web-xterm-css",""),n.addEventListener("load",()=>e(),{once:!0}),n.addEventListener("error",()=>e(),{once:!0}),document.head.appendChild(n)}),b))}class D{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){const[{Terminal:s},{FitAddon:f}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),dt()]),p=new s({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new D(e,p,new f)}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(){const e=X(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){const e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}}class z{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){const s=await import(ht);await s.init();const f=new s.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:i});return new z(e,f)}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(){const e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){const e=X(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);const 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(const s of this.resizeCallbacks)s({cols:n,rows:i})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){const 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(){const e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}updateCellMetrics(e=!1){const n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){const i=n.offsetWidth/this.colsValue,s=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=i,this.charH=s)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}}function X(o){const e=o.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 pt(o,e,n){return o.innerHTML="",o.classList.add("terminal-pending"),(async()=>{let i;if(n.renderer==="ghostty")try{i=await z.create(o,n.scrollback,n.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),i=await D.create(o,n.scrollback,n.theme)}else i=await D.create(o,n.scrollback,n.theme);let s,f=0,p,l=null,M=0,d="connecting",T=0,x=!1,E=[],m="",Y=!1,w=1e3;const j=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(e),q="/api/session/"+encodeURIComponent(e)+"/upload",_=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function N(){return E.join("")+m}async function J(t,r=0){const a=t?i.viewportY():0,u=t?i.baseY():0,h=k(N());if(i.reset(),!h){t||i.scrollToBottom();return}await i.write(h),t?i.scrollToLine(u+a+r):i.scrollToBottom()}function g(t){s?.readyState===WebSocket.OPEN&&s.send(JSON.stringify(t))}function G(t){let r;try{r=JSON.parse(t)}catch{d==="live"&&(m+=t,i.write(k(t)));return}if(r.type==="auth.required"){const a=localStorage.getItem("tmux-web-token");a?g({type:"auth.token",token:a}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(r.type==="auth.ok"){r.token&&localStorage.setItem("tmux-web-token",r.token),w=1e3,y(),g({type:"resize",cols:i.cols,rows:i.rows}),c();return}if(r.type==="auth.failed"){if(r.permanentLock||r.retryAfterMs&&r.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(r.message);return}return}if(r.type==="snapshot"&&typeof r.data=="string"){const a=k(r.data);E=[a],m="",T=typeof r.lines=="number"?r.lines:n.terminal.initialLines,d="live",i.reset(),i.write(a).then(()=>i.scrollToBottom());return}if(r.type==="window_changed"&&typeof r.activeIndex=="number"){const a=new URL(location.href);a.searchParams.get("window")!==String(r.activeIndex)&&(a.searchParams.set("window",String(r.activeIndex)),history.replaceState(history.state,"",a)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:r.windows}));return}if(r.type==="history"&&typeof r.data=="string"){x=!1,r.lines>0&&r.data&&(E.unshift(r.data),T+=r.lines,J(!0,r.lines));return}if(r.type==="data"&&typeof r.data=="string"){const a=k(r.data);if(d==="connecting"){d="live",m=a,i.write(a);return}d==="live"&&(m+=a,i.write(a))}}function H(){let t=j;const r=localStorage.getItem("tmux-web-token");r&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(r)),s=new WebSocket(t),s.onopen=()=>{d="connecting",T=0,x=!1,E=[],m="",i.reset(),w=1e3},s.onmessage=a=>{typeof a.data=="string"&&G(a.data)},s.onclose=()=>{Y||(d="connecting",setTimeout(()=>{w=Math.min(w*2,1e4),Y||H()},w))},s.onerror=()=>s?.close()}function L(t){g({type:"input",data:t})}function y(){return i.fit()}function C(){o.classList.remove("terminal-pending")}function c(){f&&cancelAnimationFrame(f),p&&clearTimeout(p),y(),f=requestAnimationFrame(()=>{f=0,y()&&C()}),p=setTimeout(()=>{y()&&C()},120)}function Q(){c(),setTimeout(c,50),setTimeout(c,150),setTimeout(c,300)}function I(t){return typeof t=="string"&&t.startsWith("image/")}function R(t){return t?.files?.length?t.files[0]??null:null}function Z(t){if(!t?.items)return null;for(let r=0;r<t.items.length;r++){const a=t.items[r];if(a?.kind==="file"&&I(a.type))return a.getAsFile()}return null}async function $(t){const r=new FormData,a=t instanceof File&&t.name?t.name:"upload";r.append("file",t,a);const u=localStorage.getItem("tmux-web-token"),h={};u&&(h.Authorization="Bearer "+u);const F=await fetch(q,{method:"POST",body:r,headers:h});if(!F.ok){let K="upload failed";try{const U=await F.json();U.error&&(K=U.error)}catch{}throw new Error(K)}const O=await F.json();if(!O.path)throw new Error("no path in response");return O.path}async function P(t){try{const r=await $(t);L(r)}catch{}}i.onData(t=>L(t)),i.onResize(({cols:t,rows:r})=>g({type:"resize",cols:t,rows:r})),i.onScroll(()=>{d!=="live"||x||!i.isNearScrollbackTop()||(x=!0,g({type:"load_history",before:T}))}),i.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){const r=await navigator.clipboard.read();for(const a of r)for(const u of a.types)if(I(u)){const h=await a.getType(u);await P(h);return}}}catch{}try{const r=await navigator.clipboard?.readText();r&&i.pasteText(A(r))}catch{}})(),!1):!0);let S=0;function A(t){return t.split(`\r
2
2
  `).join(`
3
3
  `).split(`
4
- `).join("\r")}async function it(t){const n=nt(t.clipboardData);if(n){t.preventDefault(),t.stopPropagation();const d=Date.now();if(d-D<50)return;D=d,await H(n);return}const o=t.clipboardData?.getData("text/plain");if(!o)return;t.preventDefault(),t.stopPropagation();const u=Date.now();u-D<50||(D=u,i.pasteText(O(o)))}let x=0;function ot(t){t.preventDefault(),x++,V(t.dataTransfer)&&a.classList.add("terminal-drag-over")}function at(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function st(t){t.preventDefault(),x--,x<=0&&(x=0,a.classList.remove("terminal-drag-over"))}async function lt(t){t.preventDefault(),x=0,a.classList.remove("terminal-drag-over");const n=V(t.dataTransfer);n&&await H(n)}function K(t){!Q||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||i.isFocused()&&(t.preventDefault(),t.stopPropagation(),T("\x1B"))}a.addEventListener("paste",it),a.addEventListener("dragenter",ot),a.addEventListener("dragover",at),a.addEventListener("dragleave",st),a.addEventListener("drop",lt),document.addEventListener("keydown",K,!0);function ct(t){if(t.touches.length!==1){l=null;return}const n=t.touches[0];n&&(l={startX:n.clientX,startY:n.clientY,lastX:n.clientX,lastY:n.clientY,scrolling:!1},t.stopPropagation())}function ut(t){if(!l||t.touches.length!==1)return;const n=t.touches[0];if(!n)return;const o=n.clientY-l.startY,u=n.clientX-l.startX;if(!l.scrolling){if(Math.abs(o)<8||Math.abs(o)<Math.abs(u))return;l.scrolling=!0,C=Date.now()+500}t.preventDefault(),t.stopPropagation(),mt(-(n.clientY-l.lastY),n.clientX,n.clientY),l.lastX=n.clientX,l.lastY=n.clientY}function ft(t){if(!l)return;const n=l.scrolling;l=null,t.stopPropagation(),n?(C=Date.now()+500,t.preventDefault()):i.focus()}function ht(t){l=null,t.stopPropagation()}function dt(t){t.pointerType==="touch"&&Date.now()<C&&(t.preventDefault(),t.stopPropagation())}function mt(t,n,o){(a.querySelector(".xterm-screen")??a).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:n,clientY:o,bubbles:!0,cancelable:!0}))}a.addEventListener("touchstart",ct,{passive:!0,capture:!0}),a.addEventListener("touchmove",ut,{passive:!1,capture:!0}),a.addEventListener("touchend",ft,{passive:!1,capture:!0}),a.addEventListener("touchcancel",ht,{passive:!0,capture:!0}),a.addEventListener("pointerup",dt,!0);function U(){c()}window.addEventListener("resize",U),window.visualViewport?.addEventListener("resize",c),window.visualViewport?.addEventListener("scroll",c);const X=new ResizeObserver(()=>c());return X.observe(a),document.fonts?.ready.then(()=>{c(),setTimeout(c,50),setTimeout(c,200)}),a.addEventListener("focusin",et,!0),requestAnimationFrame(()=>{b()&&z(),W(),c()}),{destroy(){if(P=!0,clearTimeout(f),f=void 0,s){try{s.close(1e3,"session switch")}catch{}s=void 0}a.innerHTML="",a.classList.remove("terminal-pending"),window.removeEventListener("resize",U),X.disconnect(),document.removeEventListener("keydown",K,!0)},focus(){i.focus()},sendInput(t){T(t)}}})()}export{vt as initTerminal};
4
+ `).join("\r")}async function tt(t){const r=Z(t.clipboardData);if(r){t.preventDefault(),t.stopPropagation();const h=Date.now();if(h-S<50)return;S=h,await P(r);return}const a=t.clipboardData?.getData("text/plain");if(!a)return;t.preventDefault(),t.stopPropagation();const u=Date.now();u-S<50||(S=u,i.pasteText(A(a)))}let v=0;function et(t){t.preventDefault(),v++,R(t.dataTransfer)&&o.classList.add("terminal-drag-over")}function rt(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function nt(t){t.preventDefault(),v--,v<=0&&(v=0,o.classList.remove("terminal-drag-over"))}async function it(t){t.preventDefault(),v=0,o.classList.remove("terminal-drag-over");const r=R(t.dataTransfer);r&&await P(r)}function B(t){!_||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||i.isFocused()&&(t.preventDefault(),t.stopPropagation(),L("\x1B"))}o.addEventListener("paste",tt),o.addEventListener("dragenter",et),o.addEventListener("dragover",rt),o.addEventListener("dragleave",nt),o.addEventListener("drop",it),document.addEventListener("keydown",B,!0);function at(t){if(t.touches.length!==1){l=null;return}const r=t.touches[0];r&&(l={startX:r.clientX,startY:r.clientY,lastX:r.clientX,lastY:r.clientY,scrolling:!1},t.stopPropagation())}function ot(t){if(!l||t.touches.length!==1)return;const r=t.touches[0];if(!r)return;const a=r.clientY-l.startY,u=r.clientX-l.startX;if(!l.scrolling){if(Math.abs(a)<8||Math.abs(a)<Math.abs(u))return;l.scrolling=!0,M=Date.now()+500}t.preventDefault(),t.stopPropagation(),ut(-(r.clientY-l.lastY),r.clientX,r.clientY),l.lastX=r.clientX,l.lastY=r.clientY}function st(t){if(!l)return;const r=l.scrolling;l=null,t.stopPropagation(),r?(M=Date.now()+500,t.preventDefault()):i.focus()}function lt(t){l=null,t.stopPropagation()}function ct(t){t.pointerType==="touch"&&Date.now()<M&&(t.preventDefault(),t.stopPropagation())}function ut(t,r,a){(o.querySelector(".xterm-screen")??o).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:r,clientY:a,bubbles:!0,cancelable:!0}))}o.addEventListener("touchstart",at,{passive:!0,capture:!0}),o.addEventListener("touchmove",ot,{passive:!1,capture:!0}),o.addEventListener("touchend",st,{passive:!1,capture:!0}),o.addEventListener("touchcancel",lt,{passive:!0,capture:!0}),o.addEventListener("pointerup",ct,!0);function V(){c()}window.addEventListener("resize",V),window.visualViewport?.addEventListener("resize",c),window.visualViewport?.addEventListener("scroll",c);const W=new ResizeObserver(()=>c());return W.observe(o),document.fonts?.ready.then(()=>{c(),setTimeout(c,50),setTimeout(c,200)}),o.addEventListener("focusin",Q,!0),requestAnimationFrame(()=>{y()&&C(),H(),c()}),{destroy(){if(Y=!0,s){try{s.close(1e3,"session switch")}catch{}s=void 0}o.innerHTML="",o.classList.remove("terminal-pending"),window.removeEventListener("resize",V),W.disconnect(),document.removeEventListener("keydown",B,!0)},focus(){i.focus()},sendInput(t){L(t)}}})()}export{pt as initTerminal};
@@ -126,7 +126,58 @@ import{icon as e}from"./icons.js";function n(){return`
126
126
  }
127
127
  #type-modal .type-modal-footer #type-send-enter {
128
128
  border-color: var(--panel-success); color: var(--panel-success);
129
- }`}function a(){return`
129
+ }
130
+
131
+ /* \u2500\u2500 Floating input bar (narrow-screen voice-input helper) \u2500\u2500 */
132
+ #float-bar { display: none; }
133
+ @media (max-width: 767px) {
134
+ #float-bar {
135
+ display: flex; align-items: flex-end; gap: 6px;
136
+ flex-shrink: 0;
137
+ padding: 8px 10px;
138
+ padding-bottom: calc(8px + env(safe-area-inset-bottom));
139
+ background: var(--panel-bg);
140
+ border-top: 1px solid var(--panel-border);
141
+ }
142
+ #float-bar textarea {
143
+ flex: 1; resize: none; box-sizing: border-box;
144
+ min-height: 38px; max-height: 120px;
145
+ background: var(--terminal-bg, rgba(0,0,0,0.28));
146
+ color: var(--page-fg);
147
+ border: 1px solid var(--panel-border);
148
+ border-radius: 8px; padding: 8px 10px;
149
+ font-family: var(--font-mono); font-size: var(--text-base); line-height: 1.4;
150
+ outline: none;
151
+ }
152
+ #float-bar textarea:focus {
153
+ border-color: var(--panel-accent);
154
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--panel-accent) 12%, transparent);
155
+ }
156
+ #float-bar textarea::placeholder { color: var(--panel-muted); opacity: 0.6; }
157
+ #float-bar .float-bar-actions {
158
+ display: flex; flex-direction: column; gap: 4px; flex-shrink: 0;
159
+ }
160
+ #float-bar .float-bar-actions button {
161
+ display: flex; align-items: center; justify-content: center;
162
+ min-width: 38px; min-height: 38px;
163
+ background: none; border: 1px solid var(--panel-border);
164
+ border-radius: 8px; color: var(--panel-muted); cursor: pointer;
165
+ font-size: var(--text-sm); font-family: var(--font-mono);
166
+ transition: color 0.15s, background 0.15s, border-color 0.15s;
167
+ }
168
+ #float-bar .float-bar-actions button:hover,
169
+ #float-bar .float-bar-actions button:focus-visible {
170
+ color: var(--panel-accent); border-color: var(--panel-accent);
171
+ background: color-mix(in srgb, var(--panel-accent) 8%, transparent);
172
+ }
173
+ #float-bar .float-bar-actions #float-send {
174
+ color: var(--panel-success); border-color: var(--panel-success);
175
+ }
176
+ #float-bar .float-bar-actions #float-send:hover {
177
+ background: color-mix(in srgb, var(--panel-success) 12%, transparent);
178
+ }
179
+ }
180
+ `}function a(){return`
130
181
  <div id="mobile-toolbar">
131
182
  <button id="mic-toggle" type="button" title="Voice input" aria-label="Voice input">
132
183
  ${e("microphone")}
@@ -153,6 +204,12 @@ import{icon as e}from"./icons.js";function n(){return`
153
204
  <button id="type-send" type="button">Send</button>
154
205
  <button id="type-send-enter" type="button">Send &#9166;</button>
155
206
  </div>
207
+ </div>`}function r(){return`
208
+ <div id="float-bar">
209
+ <textarea id="float-input" placeholder="Type or voice input\u2026" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" rows="1"></textarea>
210
+ <div class="float-bar-actions">
211
+ <button id="float-send" type="button" title="Send (Enter)">&#9166;</button>
212
+ </div>
156
213
  </div>`}function i(t){return`(function() {
157
214
  const micBtn = document.getElementById('mic-toggle');
158
215
  const typeBtn = document.getElementById('type-toggle');
@@ -310,4 +367,44 @@ import{icon as e}from"./icons.js";function n(){return`
310
367
  try { recognition.stop(); } catch (e) { /* ignore */ }
311
368
  }
312
369
  }
313
- })();`}export{n as mobileToolbarCSS,a as mobileToolbarHTML,i as mobileToolbarScript};
370
+
371
+ // \u2500\u2500 Floating input bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
372
+ const floatInput = document.getElementById('float-input');
373
+ const floatSend = document.getElementById('float-send');
374
+
375
+ if (floatInput && floatSend) {
376
+ function sendFloat() {
377
+ const text = floatInput.value;
378
+ if (text && window.tmuxWeb && window.tmuxWeb.sendInput) {
379
+ window.tmuxWeb.sendInput(text + '\r');
380
+ }
381
+ floatInput.value = '';
382
+ floatInput.rows = 1;
383
+ if (window.tmuxWeb && window.tmuxWeb.focusTerminal) window.tmuxWeb.focusTerminal();
384
+ }
385
+
386
+ // Auto-resize: grow rows up to 4 as content grows
387
+ floatInput.addEventListener('input', () => {
388
+ floatInput.rows = 1;
389
+ const lines = floatInput.value.split('
390
+ ').length;
391
+ floatInput.rows = Math.min(lines, 4);
392
+ });
393
+
394
+ // Enter sends, Shift+Enter inserts newline
395
+ floatInput.addEventListener('keydown', (e) => {
396
+ if (e.key === 'Enter' && !e.shiftKey) {
397
+ e.preventDefault();
398
+ sendFloat();
399
+ }
400
+ });
401
+
402
+ floatSend.addEventListener('click', sendFloat);
403
+
404
+ // Focus the float input when bar becomes visible (narrow screen)
405
+ const narrowMq = window.matchMedia('(max-width: 767px)');
406
+ narrowMq.addEventListener('change', (e) => {
407
+ if (e.matches) setTimeout(() => floatInput.focus(), 100);
408
+ });
409
+ }
410
+ })();`}export{r as floatBarHTML,n as mobileToolbarCSS,a as mobileToolbarHTML,i as mobileToolbarScript};
@@ -1,4 +1,4 @@
1
- import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as c}from"../html.js";import{notesDrawerCSS as v,notesDrawerHTML as y,notesDrawerScript as $}from"../notes-drawer.js";import{schedulerDrawerCSS as k,schedulerDrawerHTML as S,schedulerDrawerScript as L}from"../scheduler-drawer.js";import{windowsDrawerCSS as D,windowsDrawerHTML as E,windowsDrawerScript as F}from"../windows-drawer.js";import{sessionsDrawerCSS as M,sessionsDrawerHTML as I,sessionsDrawerScript as T}from"../sessions-drawer.js";import{mobileToolbarCSS as C,mobileToolbarHTML as B,mobileToolbarScript as O}from"../mobile-toolbar.js";import{icon as m,extIcon as W}from"../icons.js";import{commandbarButtonHTML as z,commandbarCSS as H,commandbarHTML as P,commandbarScript as R}from"../commandbar.js";import{drawerResizeCSS as A,drawerResizeHandleHTML as h,drawerResizeScript as x}from"../drawer-resize.js";import{themeSwitcherButtonHTML as j,themeSwitcherScript as J}from"../shared-layout.js";function _(){return`
1
+ import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as c}from"../html.js";import{notesDrawerCSS as v,notesDrawerHTML as y,notesDrawerScript as $}from"../notes-drawer.js";import{schedulerDrawerCSS as k,schedulerDrawerHTML as S,schedulerDrawerScript as L}from"../scheduler-drawer.js";import{windowsDrawerCSS as D,windowsDrawerHTML as E,windowsDrawerScript as F}from"../windows-drawer.js";import{sessionsDrawerCSS as M,sessionsDrawerHTML as I,sessionsDrawerScript as T}from"../sessions-drawer.js";import{mobileToolbarCSS as B,mobileToolbarHTML as C,mobileToolbarScript as O}from"../mobile-toolbar.js";import{floatBarHTML as W}from"../mobile-toolbar.js";import{icon as m,extIcon as z}from"../icons.js";import{commandbarButtonHTML as H,commandbarCSS as P,commandbarHTML as R,commandbarScript as A}from"../commandbar.js";import{drawerResizeCSS as j,drawerResizeHandleHTML as h,drawerResizeScript as x}from"../drawer-resize.js";import{themeSwitcherButtonHTML as J,themeSwitcherScript as _}from"../shared-layout.js";function U(){return`
2
2
  .ext-backdrop {
3
3
  position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 999;
4
4
  opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
@@ -18,7 +18,7 @@ import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as
18
18
  transform: translateX(100%); transition: transform 0.25s ease;
19
19
  }
20
20
  .ext-panel.open { transform: translateX(0); }
21
- ${A()}
21
+ ${j()}
22
22
  .ext-drawer .drawer-header,
23
23
  .ext-panel .drawer-header {
24
24
  display: flex; justify-content: space-between; align-items: center;
@@ -35,25 +35,25 @@ import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as
35
35
  .ext-panel .drawer-header button:hover { color: var(--panel-accent); }
36
36
  .ext-drawer iframe { flex: 1; border: none; width: 100%; height: 0; }
37
37
  .ext-panel iframe { flex: 1; border: none; width: 100%; height: 100%; }
38
- `}function U(e){const n=e.id;return`
39
- <div id="ext-${n}-backdrop" class="ext-backdrop"></div>
40
- <div id="ext-${n}-drawer" class="ext-drawer resizable-drawer">
38
+ `}function X(e){const a=e.id;return`
39
+ <div id="ext-${a}-backdrop" class="ext-backdrop"></div>
40
+ <div id="ext-${a}-drawer" class="ext-drawer resizable-drawer">
41
41
  ${h()}
42
42
  <div class="drawer-header">
43
43
  <span>${i(e.icon)} ${i(e.name)}</span>
44
- <button id="ext-${n}-close">&times;</button>
44
+ <button id="ext-${a}-close">&times;</button>
45
45
  </div>
46
- <iframe id="ext-${n}-frame" src="/ext/${n}/ui/index.html"></iframe>
47
- </div>`}function X(e){const n=e.id,t=e.panel;if(!t)return"";const r=t.title??e.name;return`
48
- <div id="ext-${n}-panel-backdrop" class="ext-backdrop"></div>
49
- <div id="ext-${n}-panel" class="ext-panel resizable-drawer">
46
+ <iframe id="ext-${a}-frame" src="/ext/${a}/ui/index.html"></iframe>
47
+ </div>`}function V(e){const a=e.id,t=e.panel;if(!t)return"";const r=t.title??e.name;return`
48
+ <div id="ext-${a}-panel-backdrop" class="ext-backdrop"></div>
49
+ <div id="ext-${a}-panel" class="ext-panel resizable-drawer">
50
50
  ${h()}
51
51
  <div class="drawer-header">
52
52
  <span>${i(e.icon)} ${i(r)}</span>
53
- <button id="ext-${n}-panel-close">&times;</button>
53
+ <button id="ext-${a}-panel-close">&times;</button>
54
54
  </div>
55
- <iframe id="ext-${n}-panel-frame" src="/ext/${n}/ui/${c(t.entry)}"></iframe>
56
- </div>`}function V(e,n){const t=e.id,r=JSON.stringify(e.config),o=e.panel?.entry??null,d=e.panel?.defaultWidth??960;return`
55
+ <iframe id="ext-${a}-panel-frame" src="/ext/${a}/ui/${c(t.entry)}"></iframe>
56
+ </div>`}function N(e,a){const t=e.id,r=JSON.stringify(e.config),o=e.panel?.entry??null,d=e.panel?.defaultWidth??960;return`
57
57
  ${x(`ext-${t}-drawer`,`tmux-web:drawer-width:ext:${t}`,360)}
58
58
  ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
59
59
  (function() {
@@ -113,7 +113,7 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
113
113
 
114
114
  function sendMessages(targetFrame) {
115
115
  if (!targetFrame || !targetFrame.contentWindow) return;
116
- targetFrame.contentWindow.postMessage({ type: 'ext:context', context: { session: ${JSON.stringify(n)}, host: location.origin } }, '*');
116
+ targetFrame.contentWindow.postMessage({ type: 'ext:context', context: { session: ${JSON.stringify(a)}, host: location.origin } }, '*');
117
117
  targetFrame.contentWindow.postMessage({ type: 'ext:config', config: cfg }, '*');
118
118
  }
119
119
 
@@ -150,7 +150,7 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
150
150
  sendMessages(panelFrame);
151
151
  }
152
152
  });
153
- }());`}function re(e,n=[],t){const r=n.filter(a=>a.slot==="sidebar"),{commandbarEnabled:o=!1,commandbarSessions:d=[],quickCommands:g=[],agentsEnabled:b=!1,theme:s,renderer:f="xterm"}=t,l=t.terminal??{initialLines:1e3,historyChunk:500,syncIdleMs:200,syncMaxMs:3e3},u=l.initialLines+2*l.historyChunk,p=[{label:"Switch window",meta:`Windows in ${e}`,subView:"windows"},{label:"Quick Commands",meta:"Paste configured command",subView:"quickCommands"},{label:"Send Command",meta:"Send input to active window",clickTargetId:"type-toggle"},{label:"Open sessions sidebar",meta:"Recent and pinned sessions",clickTargetId:"sessions-toggle"},{label:"Open notes",meta:`Notes for ${e}`,clickTargetId:"notes-toggle"},{label:"Open scheduler",meta:`Schedule command in ${e}`,clickTargetId:"sched-toggle"}];return b&&p.push({label:"View All Agents",meta:"Running agents",href:"/agents"}),`<!DOCTYPE html>
153
+ }());`}function ie(e,a=[],t){const r=a.filter(n=>n.slot==="sidebar"),{commandbarEnabled:o=!1,commandbarSessions:d=[],quickCommands:g=[],agentsEnabled:b=!1,theme:s,renderer:f="xterm"}=t,l=t.terminal??{initialLines:1e3,historyChunk:500,syncIdleMs:200,syncMaxMs:3e3},u=l.initialLines+2*l.historyChunk,p=[{label:"Switch window",meta:`Windows in ${e}`,subView:"windows"},{label:"Quick Commands",meta:"Paste configured command",subView:"quickCommands"},{label:"Send Command",meta:"Send input to active window",clickTargetId:"type-toggle"},{label:"Open sessions sidebar",meta:"Recent and pinned sessions",clickTargetId:"sessions-toggle"},{label:"Open notes",meta:`Notes for ${e}`,clickTargetId:"notes-toggle"},{label:"Open scheduler",meta:`Schedule command in ${e}`,clickTargetId:"sched-toggle"}];return b&&p.push({label:"View All Agents",meta:"Running agents",href:"/agents"}),`<!DOCTYPE html>
154
154
  <html lang="en">
155
155
  <head>
156
156
  <meta charset="UTF-8" />
@@ -266,13 +266,13 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
266
266
  @media (max-width: 380px) {
267
267
  header .status { display: none; }
268
268
  }
269
- ${o?H():""}
269
+ ${o?P():""}
270
270
  ${v()}
271
271
  ${k()}
272
272
  ${D()}
273
273
  ${M()}
274
- ${C()}
275
- ${_()}
274
+ ${B()}
275
+ ${U()}
276
276
  @media (prefers-reduced-motion: reduce) {
277
277
  *, *::before, *::after {
278
278
  animation-duration: 0.01ms !important;
@@ -287,32 +287,33 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
287
287
  <header>
288
288
  <div class="brand"><a href="/" aria-label="Go to home">tmux<span>-weblink</span></a></div>
289
289
  <span class="session">${i(e)}</span>
290
- ${o?z("Sessions"):""}
290
+ ${o?H("Sessions"):""}
291
291
  <button class="notes-btn" id="notes-toggle" title="Session notes" aria-label="Session notes">
292
292
  ${m("notes")}
293
293
  </button>
294
294
  <button class="sched-btn" id="sched-toggle" title="Schedule command" aria-label="Schedule command">
295
295
  ${m("schedule")}
296
296
  </button>
297
- ${r.map(a=>`<button class="ext-btn" id="ext-${a.id}-toggle" title="${c(a.name)}" aria-label="${c(a.name)}">${W(a.id,a.icon)}</button>`).join(`
297
+ ${r.map(n=>`<button class="ext-btn" id="ext-${n.id}-toggle" title="${c(n.name)}" aria-label="${c(n.name)}">${z(n.id,n.icon)}</button>`).join(`
298
298
  `)}
299
- ${j(s.template)}
299
+ ${J(s.template)}
300
300
  <div class="status">
301
301
  <div class="dot" id="status-dot"></div>
302
302
  <span id="status-text">connecting</span>
303
303
  </div>
304
304
  </header>
305
- <script>${J()}</script>
305
+ <script>${_()}</script>
306
306
  <div id="terminal-container" class="terminal-pending"></div>
307
- ${B()}
308
- ${o?P():""}
307
+ ${C()}
308
+ ${W()}
309
+ ${o?R():""}
309
310
  ${y(`Notes - ${e}`)}
310
311
  ${S(`Scheduler - ${e}`)}
311
312
  ${E(`Windows - ${e}`)}
312
313
  ${I()}
313
- ${r.map(a=>U(a)).join(`
314
+ ${r.map(n=>X(n)).join(`
314
315
  `)}
315
- ${r.map(a=>X(a)).join(`
316
+ ${r.map(n=>V(n)).join(`
316
317
  `)}
317
318
 
318
319
  <script type="module">
@@ -348,7 +349,7 @@ ${T(e)}
348
349
  ${O(e)}
349
350
 
350
351
  // ========== COMMANDBAR ==========
351
- ${o?R(d,p,{sessionName:e},g):""}
352
+ ${o?A(d,p,{sessionName:e},g):""}
352
353
 
353
354
  // ========== NOTES ==========
354
355
  // (notes and scheduler scripts already included above; extensions below)
@@ -357,8 +358,8 @@ ${o?R(d,p,{sessionName:e},g):""}
357
358
  ${r.length>0?`<script>
358
359
  // Extension bootstrap: plain script (not module) so it runs before the module
359
360
  // awaits terminal-client import, avoiding a race where iframes load during that await.
360
- ${r.map(a=>V(a,e)).join(`
361
+ ${r.map(n=>N(n,e)).join(`
361
362
  `)}
362
363
  </script>`:""}
363
364
  </body>
364
- </html>`}export{re as renderTerminal};
365
+ </html>`}export{ie as renderTerminal};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +0,0 @@
1
- var wt="https://esm.sh/ghostty-web@0.4.0",vt=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function M(o){return o.replace(vt,"")}var D;function gt(){return D||(document.querySelector("link[data-tmux-web-xterm-css]")?D=Promise.resolve():(D=new Promise(o=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>o(),{once:!0}),e.addEventListener("error",()=>o(),{once:!0}),document.head.appendChild(e)}),D))}var O=class J{terminal;fitAddon;container;constructor(e,i,r){this.container=e,this.terminal=i,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,i,r){let[{Terminal:s},{FitAddon:f}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),gt()]),v=new s({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new J(e,v,new f)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=U(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))}},yt=class _{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,i){this.container=e,this.terminal=i,this.terminal.open(e)}static async create(e,i,r){let s=await import(wt);await s.init();let f=new s.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:i,convertEol:!1,theme:r});return new _(e,f)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(i=>this.terminal.write(e,i))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){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=U(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let i=Math.floor(e.width/this.charW),r=Math.floor(e.height/this.charH);if(i<10||r<5)return!1;if(i!==this.colsValue||r!==this.rowsValue){this.colsValue=i,this.rowsValue=r,this.terminal.resize(i,r);for(let s of this.resizeCallbacks)s({cols:i,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let i=this.terminal.getMode?.(2004)??!1;this.input(i?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(i=>e(i))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}updateCellMetrics(e=!1){let i=this.container.querySelector("canvas");if(i instanceof HTMLElement&&i.offsetWidth>0&&i.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let r=i.offsetWidth/this.colsValue,s=i.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=r,this.charH=s)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}};function U(o){let e=o.getBoundingClientRect(),i=window.visualViewport;return i?{width:Math.min(e.width,i.width),height:Math.max(0,Math.min(e.height,i.height-Math.max(0,e.top)))}:e}function bt(o,e,i){return o.innerHTML="",o.classList.add("terminal-pending"),(async()=>{let r;if(i.renderer==="ghostty")try{r=await yt.create(o,i.scrollback,i.theme)}catch{console.error("[tmux-web] ghostty-web failed; falling back to xterm.js"),r=await O.create(o,i.scrollback,i.theme)}else r=await O.create(o,i.scrollback,i.theme);let s,f=0,v,l=null,Y=0,p="connecting",x=0,L=!1,k=[],w="",P=!1,g=1e3,C=!1,d="",u,G=(location.protocol==="https:"?"wss:":"ws:")+"//"+location.host+"/ws/"+encodeURIComponent(e),Q="/api/session/"+encodeURIComponent(e)+"/upload",Z=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function $(){return k.join("")+w}async function tt(t,n=0){let a=t?r.viewportY():0,h=t?r.baseY():0,m=M($());if(r.reset(),!m){t||r.scrollToBottom();return}await r.write(m),t?r.scrollToLine(h+a+n):r.scrollToBottom()}function y(t){s?.readyState===WebSocket.OPEN&&s.send(JSON.stringify(t))}function et(t){let n;try{n=JSON.parse(t)}catch{p==="live"&&(w+=t,r.write(M(t)));return}if(n.type==="auth.required"){let a=localStorage.getItem("tmux-web-token");a?y({type:"auth.token",token:a}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(n.type==="auth.ok"){n.token&&localStorage.setItem("tmux-web-token",n.token),g=1e3,T(),y({type:"resize",cols:r.cols,rows:r.rows}),c();return}if(n.type==="auth.failed"){if(n.permanentLock||n.retryAfterMs&&n.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(n.message);return}return}if(n.type==="snapshot"&&typeof n.data=="string"){let a=M(n.data);k=[a],w="",x=typeof n.lines=="number"?n.lines:i.terminal.initialLines,p="live",r.reset(),r.write(a).then(()=>r.scrollToBottom());return}if(n.type==="window_changed"&&typeof n.activeIndex=="number"){let a=new URL(location.href);a.searchParams.get("window")!==String(n.activeIndex)&&(a.searchParams.set("window",String(n.activeIndex)),history.replaceState(history.state,"",a)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:n.windows}));return}if(n.type==="history"&&typeof n.data=="string"){L=!1,n.lines>0&&n.data&&(k.unshift(n.data),x+=n.lines,tt(!0,n.lines));return}if(n.type==="data"&&typeof n.data=="string"){let a=M(n.data);if(p==="connecting"){p="live",w=a,r.write(a);return}p==="live"&&(w+=a,r.write(a))}}function V(){let t=G,n=localStorage.getItem("tmux-web-token");n&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(n)),s=new WebSocket(t),s.onopen=()=>{p="connecting",x=0,L=!1,k=[],w="",r.reset(),g=1e3},s.onmessage=a=>{typeof a.data=="string"&&et(a.data)},s.onclose=()=>{P||(p="connecting",setTimeout(()=>{g=Math.min(g*2,1e4),P||V()},g))},s.onerror=()=>s?.close()}function b(t){y({type:"input",data:t})}function z(){d&&(b(d),d="")}function T(){return r.fit()}function B(){o.classList.remove("terminal-pending")}function c(){f&&cancelAnimationFrame(f),v&&clearTimeout(v),T(),f=requestAnimationFrame(()=>{f=0,T()&&B()}),v=setTimeout(()=>{T()&&B()},120)}function nt(){c(),setTimeout(c,50),setTimeout(c,150),setTimeout(c,300)}function I(t){return typeof t=="string"&&t.startsWith("image/")}function W(t){return t?.files?.length?t.files[0]??null:null}function rt(t){if(!t?.items)return null;for(let n=0;n<t.items.length;n++){let a=t.items[n];if(a?.kind==="file"&&I(a.type))return a.getAsFile()}return null}async function it(t){let n=new FormData,a=t instanceof File&&t.name?t.name:"upload";n.append("file",t,a);let h=localStorage.getItem("tmux-web-token"),m={};h&&(m.Authorization="Bearer "+h);let A=await fetch(Q,{method:"POST",body:n,headers:m});if(!A.ok){let j="upload failed";try{let N=await A.json();N.error&&(j=N.error)}catch{}throw new Error(j)}let q=await A.json();if(!q.path)throw new Error("no path in response");return q.path}async function H(t){try{let n=await it(t);b(n)}catch{}}o.addEventListener("compositionstart",()=>{C=!0},!0),o.addEventListener("compositionend",t=>{C=!1,clearTimeout(u),u=void 0,d="",t.data&&b(t.data)},!0),r.onData(t=>{if(!C){if(d&&t.startsWith(d)&&t.length>d.length){d=t,clearTimeout(u),u=setTimeout(()=>{z(),u=void 0},40);return}d&&(clearTimeout(u),u=void 0,z()),clearTimeout(u),d=t,u=setTimeout(()=>{z(),u=void 0},15)}}),r.onResize(({cols:t,rows:n})=>y({type:"resize",cols:t,rows:n})),r.onScroll(()=>{p!=="live"||L||!r.isNearScrollbackTop()||(L=!0,y({type:"load_history",before:x}))}),r.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let n=await navigator.clipboard.read();for(let a of n)for(let h of a.types)if(I(h)){let m=await a.getType(h);await H(m);return}}}catch{}try{let n=await navigator.clipboard?.readText();n&&r.pasteText(F(n))}catch{}})(),!1):!0);let S=0;function F(t){return t.split(`\r
2
- `).join(`
3
- `).split(`
4
- `).join("\r")}async function at(t){let n=rt(t.clipboardData);if(n){t.preventDefault(),t.stopPropagation();let m=Date.now();if(m-S<50)return;S=m,await H(n);return}let a=t.clipboardData?.getData("text/plain");if(!a)return;t.preventDefault(),t.stopPropagation();let h=Date.now();h-S<50||(S=h,r.pasteText(F(a)))}let E=0;function ot(t){t.preventDefault(),E++,W(t.dataTransfer)&&o.classList.add("terminal-drag-over")}function st(t){t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}function lt(t){t.preventDefault(),E--,E<=0&&(E=0,o.classList.remove("terminal-drag-over"))}async function ct(t){t.preventDefault(),E=0,o.classList.remove("terminal-drag-over");let n=W(t.dataTransfer);n&&await H(n)}function R(t){!Z||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||r.isFocused()&&(t.preventDefault(),t.stopPropagation(),b("\x1B"))}o.addEventListener("paste",at),o.addEventListener("dragenter",ot),o.addEventListener("dragover",st),o.addEventListener("dragleave",lt),o.addEventListener("drop",ct),document.addEventListener("keydown",R,!0);function ut(t){if(t.touches.length!==1){l=null;return}let n=t.touches[0];n&&(l={startX:n.clientX,startY:n.clientY,lastX:n.clientX,lastY:n.clientY,scrolling:!1},t.stopPropagation())}function ht(t){if(!l||t.touches.length!==1)return;let n=t.touches[0];if(!n)return;let a=n.clientY-l.startY,h=n.clientX-l.startX;if(!l.scrolling){if(Math.abs(a)<8||Math.abs(a)<Math.abs(h))return;l.scrolling=!0,Y=Date.now()+500}t.preventDefault(),t.stopPropagation(),pt(-(n.clientY-l.lastY),n.clientX,n.clientY),l.lastX=n.clientX,l.lastY=n.clientY}function dt(t){if(!l)return;let n=l.scrolling;l=null,t.stopPropagation(),n?(Y=Date.now()+500,t.preventDefault()):r.focus()}function mt(t){l=null,t.stopPropagation()}function ft(t){t.pointerType==="touch"&&Date.now()<Y&&(t.preventDefault(),t.stopPropagation())}function pt(t,n,a){(o.querySelector(".xterm-screen")??o).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:n,clientY:a,bubbles:!0,cancelable:!0}))}o.addEventListener("touchstart",ut,{passive:!0,capture:!0}),o.addEventListener("touchmove",ht,{passive:!1,capture:!0}),o.addEventListener("touchend",dt,{passive:!1,capture:!0}),o.addEventListener("touchcancel",mt,{passive:!0,capture:!0}),o.addEventListener("pointerup",ft,!0);function K(){c()}window.addEventListener("resize",K),window.visualViewport?.addEventListener("resize",c),window.visualViewport?.addEventListener("scroll",c);let X=new ResizeObserver(()=>c());return X.observe(o),document.fonts?.ready.then(()=>{c(),setTimeout(c,50),setTimeout(c,200)}),o.addEventListener("focusin",nt,!0),requestAnimationFrame(()=>{T()&&B(),V(),c()}),{destroy(){if(P=!0,clearTimeout(u),u=void 0,s){try{s.close(1e3,"session switch")}catch{}s=void 0}o.innerHTML="",o.classList.remove("terminal-pending"),window.removeEventListener("resize",K),X.disconnect(),document.removeEventListener("keydown",R,!0)},focus(){r.focus()},sendInput(t){b(t)}}})()}export{bt as a};