tmux-weblink 0.3.1 → 0.3.2
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,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var U="https://esm.sh/ghostty-web@0.4.0",J=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function y(t){return t.replace(J,"")}var b;function G(){return b||(document.querySelector("link[data-tmux-web-xterm-css]")?b=Promise.resolve():(b=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)}),b))}var Q=class q{terminal;fitAddon;container;constructor(e,n,r){this.container=e,this.terminal=n,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,n,r){let[{Terminal:o},{FitAddon:h}]=await Promise.all([import("./xterm-BXEES47M.js"),import("./addon-fit-D5N335P2.js"),G()]),g=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new q(e,g,new h)}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=H(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))}},Z=class O{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,r){let o=await import(U);await o.init();let h=new o.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new O(e,h)}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=H(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),r=Math.floor(e.height/this.charH);if(n<10||r<5)return!1;if(n!==this.colsValue||r!==this.rowsValue){this.colsValue=n,this.rowsValue=r,this.terminal.resize(n,r);for(let o of this.resizeCallbacks)o({cols:n,rows:r})}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))}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 r=n.offsetWidth/this.colsValue,o=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=r,this.charH=o)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}},W=window.__TMUX_WEB_TERMINAL__;if(!W)throw new Error("missing tmux-web terminal config");var u=W,V=document.getElementById("terminal-container");if(!V)throw new Error("missing terminal container");var i=V;function $(){let t=document.createElement("div");t.textContent="ghostty-web unavailable \u2014 using xterm.js",t.style.cssText='position:fixed;bottom:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#fbbf24;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(251,191,36,0.4);border-radius:6px;',document.body.appendChild(t),setTimeout(()=>t.remove(),6e3)}async function ee(){if(u.renderer==="ghostty")try{return await Z.create(i,u.scrollback,u.theme)}catch(t){console.error("[tmux-web] ghostty-web failed to load; falling back to xterm.js:",t),$()}return Q.create(i,u.scrollback,u.theme)}var a=await ee(),c,x=0,C,s=null,B=0,d="connecting",E=0,T=!1,k=[],m="";function te(){return k.join("")+m}async function ne(t,e=0){let n=t?a.viewportY():0,r=t?a.baseY():0,o=y(te());if(a.reset(),!o){t||a.scrollToBottom();return}await a.write(o),t?a.scrollToLine(r+n+e):a.scrollToBottom()}function re(t){let e;try{e=JSON.parse(t)}catch{d==="live"&&(m+=t,a.write(y(t)));return}if(e.type==="auth.required"){let n=localStorage.getItem("tmux-web-token");n?w({type:"auth.token",token:n}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),L(!0),f=1e3,p(),w({type:"resize",cols:a.cols,rows:a.rows}),l();return}if(e.type==="auth.failed"){if(L(!1),e.permanentLock||e.retryAfterMs&&e.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(e.message);return}let n=document.createElement("div");n.textContent="Auth failed: "+e.message,n.style.cssText='position:fixed;top:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#f87171;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(248,113,113,0.4);border-radius:6px;',document.body.appendChild(n),setTimeout(()=>n.remove(),4e3);return}if(e.type==="snapshot"&&typeof e.data=="string"){let n=y(e.data);k=[n],m="",E=typeof e.lines=="number"?e.lines:u.terminal.initialLines,d="live",a.reset(),a.write(n).then(()=>a.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){let n=new URL(location.href);n.searchParams.get("window")!==String(e.activeIndex)&&(n.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",n)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){T=!1,e.lines>0&&e.data&&(k.unshift(e.data),E+=e.lines,ne(!0,e.lines));return}if(e.type==="data"&&typeof e.data=="string"){let n=y(e.data);if(d==="connecting"){d="live",m=n,a.write(n);return}d==="live"&&(m+=n,a.write(n))}}function H(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 p(){return a.fit()}function P(){i.classList.remove("terminal-pending")}function l(){x&&cancelAnimationFrame(x),C&&clearTimeout(C),p(),x=requestAnimationFrame(()=>{x=0,p()&&P()}),C=setTimeout(()=>{p()&&P()},120)}function F(){l(),setTimeout(l,50),setTimeout(l,150),setTimeout(l,300)}var R=document.getElementById("status-dot"),S=document.getElementById("status-text");function L(t){R&&(R.className="dot"+(t?" connected":"")),S&&(S.textContent=t?"connected":"reconnecting")}var ae=location.protocol==="https:"?"wss:":"ws:",ie=ae+"//"+location.host+"/ws/"+encodeURIComponent(u.sessionName),oe="/api/session/"+encodeURIComponent(u.sessionName)+"/upload",f=1e3,se=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function w(t){c?.readyState===WebSocket.OPEN&&c.send(JSON.stringify(t))}function X(){let t=ie,e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),c=new WebSocket(t),c.onopen=()=>{d="connecting",E=0,T=!1,k=[],m="",a.reset(),f=1e3},c.onmessage=n=>{typeof n.data=="string"&&re(n.data)},c.onclose=()=>{L(!1),d="connecting",setTimeout(()=>{f=Math.min(f*2,1e4),X()},f)},c.onerror=()=>c?.close()}function M(t){w({type:"input",data:t})}function le(t){M(t)}function K(t){return t.split(`\r
|
|
2
2
|
`).join(`
|
|
3
3
|
`).split(`
|
|
4
|
-
`).join("\r")}function
|
|
4
|
+
`).join("\r")}function N(t){return typeof t=="string"&&t.startsWith("image/")}function _(t){return t?.files?.length?t.files[0]??null:null}function ce(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){let n=t.items[e];if(n?.kind==="file"&&N(n.type))return n.getAsFile()}return null}function D(t){t&&S?S.textContent=t:L(c?.readyState===WebSocket.OPEN)}async function ue(t){let e=new FormData,n=t instanceof File&&t.name?t.name:"upload";e.append("file",t,n);let r=localStorage.getItem("tmux-web-token"),o={};r&&(o.Authorization="Bearer "+r);let h=await fetch(oe,{method:"POST",body:e,headers:o});if(!h.ok){let z="upload failed";try{let A=await h.json();A.error&&(z=A.error)}catch{}throw new Error(z)}let g=await h.json();if(!g.path)throw new Error("no path in response");return g.path}async function I(t){try{D("uploading...");let e=await ue(t);le(e),D(null)}catch(e){console.warn("image upload:",e),D("upload failed"),setTimeout(()=>D(null),2e3)}}a.onData(t=>M(t)),a.onResize(({cols:t,rows:e})=>w({type:"resize",cols:t,rows:e})),window.tmuxWeb={sendInput:M,focusTerminal:j},a.onScroll(()=>{d!=="live"||T||!a.isNearScrollbackTop()||(T=!0,w({type:"load_history",before:E}))}),a.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let e=await navigator.clipboard.read();for(let n of e)for(let r of n.types)if(N(r)){let o=await n.getType(r);await I(o);return}}}catch{}try{let e=await navigator.clipboard?.readText();e&&a.pasteText(K(e))}catch{}})(),!1):!0);var Y=0;async function de(t){let e=ce(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();let o=Date.now();if(o-Y<50)return;Y=o,await I(e);return}let n=t.clipboardData?.getData("text/plain");if(!n)return;t.preventDefault(),t.stopPropagation();let r=Date.now();r-Y<50||(Y=r,a.pasteText(K(n)))}i.addEventListener("paste",de);var v=0;i.addEventListener("dragenter",t=>{t.preventDefault(),v++,_(t.dataTransfer)&&i.classList.add("terminal-drag-over")}),i.addEventListener("dragover",t=>{t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}),i.addEventListener("dragleave",t=>{t.preventDefault(),v--,v<=0&&(v=0,i.classList.remove("terminal-drag-over"))}),i.addEventListener("drop",async t=>{t.preventDefault(),v=0,i.classList.remove("terminal-drag-over");let e=_(t.dataTransfer);e&&await I(e)}),document.addEventListener("keydown",t=>{!se||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||a.isFocused()&&(t.preventDefault(),t.stopPropagation(),M("\x1B"))},!0);function he(t,e,n){(i.querySelector(".xterm-screen")??i).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:e,clientY:n,bubbles:!0,cancelable:!0}))}function j(){a.focus(),F()}i.addEventListener("touchstart",t=>{if(t.touches.length!==1){s=null;return}let e=t.touches[0];e&&(s={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,scrolling:!1},t.stopPropagation())},{passive:!0,capture:!0}),i.addEventListener("touchmove",t=>{if(!s||t.touches.length!==1)return;let e=t.touches[0];if(!e)return;let n=e.clientY-s.startY,r=e.clientX-s.startX;if(!s.scrolling){if(Math.abs(n)<8||Math.abs(n)<Math.abs(r))return;s.scrolling=!0,B=Date.now()+500}t.preventDefault(),t.stopPropagation(),he(-(e.clientY-s.lastY),e.clientX,e.clientY),s.lastX=e.clientX,s.lastY=e.clientY},{passive:!1,capture:!0}),i.addEventListener("touchend",t=>{if(!s)return;let e=s.scrolling;s=null,t.stopPropagation(),e?(B=Date.now()+500,t.preventDefault()):j()},{passive:!1,capture:!0}),i.addEventListener("touchcancel",t=>{s=null,t.stopPropagation()},{passive:!0,capture:!0}),i.addEventListener("pointerup",t=>{t.pointerType==="touch"&&Date.now()<B&&(t.preventDefault(),t.stopPropagation())},!0),window.addEventListener("resize",l),window.visualViewport?.addEventListener("resize",l),window.visualViewport?.addEventListener("scroll",l),new ResizeObserver(l).observe(i),document.fonts?.ready.then(()=>{l(),setTimeout(l,50),setTimeout(l,200)}),i.addEventListener("focusin",F,!0),requestAnimationFrame(()=>{p()&&P(),X(),l()});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const q="https://esm.sh/ghostty-web@0.4.0",J=/\x1b\](?:10|11|110|111|104)(?:;[^\x07\x1b]*)?\x1b?(?:\\|\x07)/g;function v(e){return e.replace(J,"")}let m;function G(){return m||(document.querySelector("link[data-tmux-web-xterm-css]")?m=Promise.resolve():(m=new Promise(t=>{const n=document.createElement("link");n.rel="stylesheet",n.href="/assets/xterm.css",n.setAttribute("data-tmux-web-xterm-css",""),n.addEventListener("load",()=>t(),{once:!0}),n.addEventListener("error",()=>t(),{once:!0}),document.head.appendChild(n)}),m))}class F{terminal;fitAddon;container;constructor(t,n,r){this.container=t,this.terminal=n,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(t)}static async create(t,n,r){const[{Terminal:a},{FitAddon:d}]=await Promise.all([import("@xterm/xterm"),import("@xterm/addon-fit"),G()]),b=new a({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new F(t,b,new d)}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(){const t=V(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(){const t=document.activeElement;return!!t&&(t===this.container||this.container.contains(t))}}class W{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,r){const a=await import(q);await a.init();const d=new a.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new W(t,d)}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(){const 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(){const t=V(this.container);if(t.width<=0||t.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);const n=Math.floor(t.width/this.charW),r=Math.floor(t.height/this.charH);if(n<10||r<5)return!1;if(n!==this.colsValue||r!==this.rowsValue){this.colsValue=n,this.rowsValue=r,this.terminal.resize(n,r);for(const a of this.resizeCallbacks)a({cols:n,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(t){const 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(){const t=document.activeElement;return!!t&&(t===this.container||t===this.terminal.textarea||this.container.contains(t))}updateCellMetrics(t=!1){const n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){const r=n.offsetWidth/this.colsValue,a=n.offsetHeight/this.rowsValue;(t||!this.charW||!this.charH)&&(this.charW=r,this.charH=a)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}}const R=window.__TMUX_WEB_TERMINAL__;if(!R)throw new Error("missing tmux-web terminal config");const u=R,H=document.getElementById("terminal-container");if(!H)throw new Error("missing terminal container");const o=H;function Q(){const e=document.createElement("div");e.textContent="ghostty-web unavailable \u2014 using xterm.js",e.style.cssText='position:fixed;bottom:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#fbbf24;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(251,191,36,0.4);border-radius:6px;',document.body.appendChild(e),setTimeout(()=>e.remove(),6e3)}async function Z(){if(u.renderer==="ghostty")try{return await W.create(o,u.scrollback,u.theme)}catch(e){console.error("[tmux-web] ghostty-web failed to load; falling back to xterm.js:",e),Q()}return F.create(o,u.scrollback,u.theme)}const i=await Z();let c,x=0,P,s=null,Y=0,f="connecting",T=0,E=!1,k=[],h="";function $(){return k.join("")+h}async function tt(e,t=0){const n=e?i.viewportY():0,r=e?i.baseY():0,a=v($());if(i.reset(),!a){e||i.scrollToBottom();return}await i.write(a),e?i.scrollToLine(r+n+t):i.scrollToBottom()}function et(e){let t;try{t=JSON.parse(e)}catch{f==="live"&&(h+=e,i.write(v(e)));return}if(t.type==="auth.required"){const n=localStorage.getItem("tmux-web-token");n?g({type:"auth.token",token:n}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(t.type==="auth.ok"){t.token&&localStorage.setItem("tmux-web-token",t.token),L(!0),w=1e3,p(),g({type:"resize",cols:i.cols,rows:i.rows}),l();return}if(t.type==="auth.failed"){if(L(!1),t.permanentLock||t.retryAfterMs&&t.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(t.message);return}const n=document.createElement("div");n.textContent="Auth failed: "+t.message,n.style.cssText='position:fixed;top:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#f87171;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(248,113,113,0.4);border-radius:6px;',document.body.appendChild(n),setTimeout(()=>n.remove(),4e3);return}if(t.type==="snapshot"&&typeof t.data=="string"){const n=v(t.data);k=[n],h="",T=typeof t.lines=="number"?t.lines:u.terminal.initialLines,f="live",i.reset(),i.write(n).then(()=>i.scrollToBottom());return}if(t.type==="window_changed"&&typeof t.activeIndex=="number"){const n=new URL(location.href);n.searchParams.get("window")!==String(t.activeIndex)&&(n.searchParams.set("window",String(t.activeIndex)),history.replaceState(history.state,"",n)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:t.windows}));return}if(t.type==="history"&&typeof t.data=="string"){E=!1,t.lines>0&&t.data&&(k.unshift(t.data),T+=t.lines,tt(!0,t.lines));return}if(t.type==="data"&&typeof t.data=="string"){const n=v(t.data);if(f==="connecting"){f="live",h=n,i.write(n);return}f==="live"&&(h+=n,i.write(n))}}function V(e){const 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 p(){return i.fit()}function I(){o.classList.remove("terminal-pending")}function l(){x&&cancelAnimationFrame(x),P&&clearTimeout(P),p(),x=requestAnimationFrame(()=>{x=0,p()&&I()}),P=setTimeout(()=>{p()&&I()},120)}function N(){l(),setTimeout(l,50),setTimeout(l,150),setTimeout(l,300)}const O=document.getElementById("status-dot"),S=document.getElementById("status-text");function L(e){O&&(O.className="dot"+(e?" connected":"")),S&&(S.textContent=e?"connected":"reconnecting")}const nt=location.protocol==="https:"?"wss:":"ws:",rt=nt+"//"+location.host+"/ws/"+encodeURIComponent(u.sessionName),it="/api/session/"+encodeURIComponent(u.sessionName)+"/upload";let w=1e3;const ot=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function g(e){c?.readyState===WebSocket.OPEN&&c.send(JSON.stringify(e))}function _(){let e=rt;const t=localStorage.getItem("tmux-web-token");t&&(e+=(e.includes("?")?"&":"?")+"token="+encodeURIComponent(t)),c=new WebSocket(e),c.onopen=()=>{f="connecting",T=0,E=!1,k=[],h="",i.reset(),w=1e3},c.onmessage=n=>{typeof n.data=="string"&&et(n.data)},c.onclose=()=>{L(!1),f="connecting",setTimeout(()=>{w=Math.min(w*2,1e4),_()},w)},c.onerror=()=>c?.close()}function C(e){g({type:"input",data:e})}function at(e){C(e)}function K(e){return e.split(`\r
|
|
2
2
|
`).join(`
|
|
3
3
|
`).split(`
|
|
4
|
-
`).join("\r")}function
|
|
4
|
+
`).join("\r")}function U(e){return typeof e=="string"&&e.startsWith("image/")}function X(e){return e?.files?.length?e.files[0]??null:null}function st(e){if(!e?.items)return null;for(let t=0;t<e.items.length;t++){const n=e.items[t];if(n?.kind==="file"&&U(n.type))return n.getAsFile()}return null}function D(e){e&&S?S.textContent=e:L(c?.readyState===WebSocket.OPEN)}async function lt(e){const t=new FormData,n=e instanceof File&&e.name?e.name:"upload";t.append("file",e,n);const r=localStorage.getItem("tmux-web-token"),a={};r&&(a.Authorization="Bearer "+r);const d=await fetch(it,{method:"POST",body:t,headers:a});if(!d.ok){let z="upload failed";try{const A=await d.json();A.error&&(z=A.error)}catch{}throw new Error(z)}const b=await d.json();if(!b.path)throw new Error("no path in response");return b.path}async function B(e){try{D("uploading...");const t=await lt(e);at(t),D(null)}catch(t){console.warn("image upload:",t),D("upload failed"),setTimeout(()=>D(null),2e3)}}i.onData(e=>C(e)),i.onResize(({cols:e,rows:t})=>g({type:"resize",cols:e,rows:t})),window.tmuxWeb={sendInput:C,focusTerminal:j},i.onScroll(()=>{f!=="live"||E||!i.isNearScrollbackTop()||(E=!0,g({type:"load_history",before:T}))}),i.attachCustomKeyEventHandler(e=>e.type!=="keydown"?!0:(e.ctrlKey||e.metaKey)&&e.code==="KeyV"?(e.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){const t=await navigator.clipboard.read();for(const n of t)for(const r of n.types)if(U(r)){const a=await n.getType(r);await B(a);return}}}catch{}try{const t=await navigator.clipboard?.readText();t&&i.pasteText(K(t))}catch{}})(),!1):!0);let M=0;async function ct(e){const t=st(e.clipboardData);if(t){e.preventDefault(),e.stopPropagation();const a=Date.now();if(a-M<50)return;M=a,await B(t);return}const n=e.clipboardData?.getData("text/plain");if(!n)return;e.preventDefault(),e.stopPropagation();const r=Date.now();r-M<50||(M=r,i.pasteText(K(n)))}o.addEventListener("paste",ct);let y=0;o.addEventListener("dragenter",e=>{e.preventDefault(),y++,X(e.dataTransfer)&&o.classList.add("terminal-drag-over")}),o.addEventListener("dragover",e=>{e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy")}),o.addEventListener("dragleave",e=>{e.preventDefault(),y--,y<=0&&(y=0,o.classList.remove("terminal-drag-over"))}),o.addEventListener("drop",async e=>{e.preventDefault(),y=0,o.classList.remove("terminal-drag-over");const t=X(e.dataTransfer);t&&await B(t)}),document.addEventListener("keydown",e=>{!ot||e.key!=="Escape"||e.defaultPrevented||e.repeat||e.metaKey||e.ctrlKey||e.altKey||i.isFocused()&&(e.preventDefault(),e.stopPropagation(),C("\x1B"))},!0);function ut(e,t,n){(o.querySelector(".xterm-screen")??o).dispatchEvent(new WheelEvent("wheel",{deltaY:e,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:t,clientY:n,bubbles:!0,cancelable:!0}))}function j(){i.focus(),N()}o.addEventListener("touchstart",e=>{if(e.touches.length!==1){s=null;return}const t=e.touches[0];t&&(s={startX:t.clientX,startY:t.clientY,lastX:t.clientX,lastY:t.clientY,scrolling:!1},e.stopPropagation())},{passive:!0,capture:!0}),o.addEventListener("touchmove",e=>{if(!s||e.touches.length!==1)return;const t=e.touches[0];if(!t)return;const n=t.clientY-s.startY,r=t.clientX-s.startX;if(!s.scrolling){if(Math.abs(n)<8||Math.abs(n)<Math.abs(r))return;s.scrolling=!0,Y=Date.now()+500}e.preventDefault(),e.stopPropagation(),ut(-(t.clientY-s.lastY),t.clientX,t.clientY),s.lastX=t.clientX,s.lastY=t.clientY},{passive:!1,capture:!0}),o.addEventListener("touchend",e=>{if(!s)return;const t=s.scrolling;s=null,e.stopPropagation(),t?(Y=Date.now()+500,e.preventDefault()):j()},{passive:!1,capture:!0}),o.addEventListener("touchcancel",e=>{s=null,e.stopPropagation()},{passive:!0,capture:!0}),o.addEventListener("pointerup",e=>{e.pointerType==="touch"&&Date.now()<Y&&(e.preventDefault(),e.stopPropagation())},!0),window.addEventListener("resize",l),window.visualViewport?.addEventListener("resize",l),window.visualViewport?.addEventListener("scroll",l),new ResizeObserver(l).observe(o),document.fonts?.ready.then(()=>{l(),setTimeout(l,50),setTimeout(l,200)}),o.addEventListener("focusin",N,!0),requestAnimationFrame(()=>{p()&&I(),_(),l()});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
function s(){return`
|
|
2
|
+
#terminal-layout {
|
|
3
|
+
display: flex;
|
|
4
|
+
flex: 1;
|
|
5
|
+
min-height: 0;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
}
|
|
8
|
+
#session-sidebar {
|
|
9
|
+
width: 220px;
|
|
10
|
+
flex-shrink: 0;
|
|
11
|
+
background: var(--panel-bg);
|
|
12
|
+
border-right: 1px solid var(--panel-border);
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
transition: width 0.2s ease, border-color 0.2s ease;
|
|
17
|
+
}
|
|
18
|
+
#session-sidebar.collapsed {
|
|
19
|
+
width: 0;
|
|
20
|
+
border-right-color: transparent;
|
|
21
|
+
}
|
|
22
|
+
#session-sidebar .sidebar-header {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
padding: 10px 12px;
|
|
27
|
+
border-bottom: 1px solid var(--panel-border);
|
|
28
|
+
font-size: var(--text-xs);
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
letter-spacing: 0.05em;
|
|
31
|
+
text-transform: uppercase;
|
|
32
|
+
color: var(--panel-accent);
|
|
33
|
+
font-family: var(--font-mono);
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
}
|
|
36
|
+
#session-sidebar .sidebar-list {
|
|
37
|
+
flex: 1;
|
|
38
|
+
overflow-y: auto;
|
|
39
|
+
padding: 8px 0;
|
|
40
|
+
scrollbar-width: thin;
|
|
41
|
+
scrollbar-color: var(--panel-border) transparent;
|
|
42
|
+
}
|
|
43
|
+
#session-sidebar .sidebar-list::-webkit-scrollbar { width: 4px; }
|
|
44
|
+
#session-sidebar .sidebar-list::-webkit-scrollbar-track { background: transparent; }
|
|
45
|
+
#session-sidebar .sidebar-list::-webkit-scrollbar-thumb {
|
|
46
|
+
background: var(--panel-border); border-radius: 2px;
|
|
47
|
+
}
|
|
48
|
+
#session-sidebar .sidebar-list::-webkit-scrollbar-thumb:hover { background: var(--panel-muted); }
|
|
49
|
+
#session-sidebar .sidebar-empty {
|
|
50
|
+
padding: 16px;
|
|
51
|
+
text-align: center;
|
|
52
|
+
color: var(--panel-muted);
|
|
53
|
+
font-size: var(--text-xs);
|
|
54
|
+
font-family: var(--font-mono);
|
|
55
|
+
}
|
|
56
|
+
#session-sidebar .sidebar-row {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
width: 100%;
|
|
61
|
+
min-height: 40px;
|
|
62
|
+
padding: 8px 12px;
|
|
63
|
+
background: none;
|
|
64
|
+
border: none;
|
|
65
|
+
border-bottom: 1px solid var(--panel-border);
|
|
66
|
+
color: var(--page-fg);
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
text-align: left;
|
|
69
|
+
font-family: var(--font-mono);
|
|
70
|
+
font-size: var(--text-sm);
|
|
71
|
+
transition: background 0.15s;
|
|
72
|
+
text-decoration: none;
|
|
73
|
+
}
|
|
74
|
+
#session-sidebar .sidebar-row:last-child { border-bottom: none; }
|
|
75
|
+
#session-sidebar .sidebar-row:hover { background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
|
|
76
|
+
#session-sidebar .sidebar-row.current {
|
|
77
|
+
background: color-mix(in srgb, var(--panel-accent) 12%, transparent);
|
|
78
|
+
color: var(--panel-accent);
|
|
79
|
+
font-weight: 500;
|
|
80
|
+
}
|
|
81
|
+
#session-sidebar .sidebar-row-name {
|
|
82
|
+
flex: 1;
|
|
83
|
+
min-width: 0;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
white-space: nowrap;
|
|
87
|
+
}
|
|
88
|
+
#session-sidebar .sidebar-row-dot {
|
|
89
|
+
width: 8px;
|
|
90
|
+
height: 8px;
|
|
91
|
+
border-radius: 50%;
|
|
92
|
+
background: var(--panel-muted);
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
#session-sidebar .sidebar-row.current .sidebar-row-dot { background: var(--panel-accent); }
|
|
96
|
+
#session-sidebar .sidebar-row.attached .sidebar-row-dot { background: var(--panel-success); }
|
|
97
|
+
#session-sidebar-toggle {
|
|
98
|
+
display: none;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
background: none;
|
|
102
|
+
border: none;
|
|
103
|
+
color: var(--panel-muted);
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
min-width: 44px;
|
|
106
|
+
min-height: 44px;
|
|
107
|
+
padding: 8px;
|
|
108
|
+
border-radius: 10px;
|
|
109
|
+
transition: color 0.15s, background 0.15s;
|
|
110
|
+
font-size: var(--text-sm);
|
|
111
|
+
}
|
|
112
|
+
#session-sidebar-toggle:hover { color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
|
|
113
|
+
#session-sidebar-toggle svg { width: 18px; height: 18px; fill: currentColor; }
|
|
114
|
+
@media (max-width: 768px) {
|
|
115
|
+
#session-sidebar {
|
|
116
|
+
position: fixed;
|
|
117
|
+
left: 0;
|
|
118
|
+
top: 0;
|
|
119
|
+
height: 100%;
|
|
120
|
+
z-index: 1000;
|
|
121
|
+
width: min(280px, 80vw);
|
|
122
|
+
transform: translateX(-100%);
|
|
123
|
+
transition: transform 0.25s ease;
|
|
124
|
+
}
|
|
125
|
+
#session-sidebar.open { transform: translateX(0); }
|
|
126
|
+
#session-sidebar.collapsed { width: min(280px, 80vw); }
|
|
127
|
+
#session-sidebar-backdrop {
|
|
128
|
+
position: fixed;
|
|
129
|
+
inset: 0;
|
|
130
|
+
background: rgba(0,0,0,0.45);
|
|
131
|
+
z-index: 999;
|
|
132
|
+
opacity: 0;
|
|
133
|
+
pointer-events: none;
|
|
134
|
+
transition: opacity 0.2s ease;
|
|
135
|
+
}
|
|
136
|
+
#session-sidebar-backdrop.open { opacity: 1; pointer-events: auto; }
|
|
137
|
+
#session-sidebar-toggle { display: flex; }
|
|
138
|
+
}
|
|
139
|
+
@media (min-width: 769px) {
|
|
140
|
+
#session-sidebar-backdrop { display: none; }
|
|
141
|
+
}
|
|
142
|
+
`}function i(e){return`
|
|
143
|
+
<div id="session-sidebar-backdrop"></div>
|
|
144
|
+
<aside id="session-sidebar" aria-label="Sessions">
|
|
145
|
+
<div class="sidebar-header">
|
|
146
|
+
<span>Sessions</span>
|
|
147
|
+
<button type="button" id="session-sidebar-close" class="header-icon-btn" aria-label="Close sessions">\xD7</button>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="sidebar-list" id="session-sidebar-list">
|
|
150
|
+
<div class="sidebar-empty">Loading sessions\u2026</div>
|
|
151
|
+
</div>
|
|
152
|
+
</aside>`}function n(e){return`
|
|
153
|
+
(function () {
|
|
154
|
+
const SIDEBAR_CURRENT = ${JSON.stringify(e)};
|
|
155
|
+
const sidebar = document.getElementById('session-sidebar');
|
|
156
|
+
const backdrop = document.getElementById('session-sidebar-backdrop');
|
|
157
|
+
const list = document.getElementById('session-sidebar-list');
|
|
158
|
+
const toggleBtn = document.getElementById('session-sidebar-toggle');
|
|
159
|
+
const closeBtn = document.getElementById('session-sidebar-close');
|
|
160
|
+
|
|
161
|
+
function isMobile() {
|
|
162
|
+
return window.innerWidth <= 768;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function openSidebar() {
|
|
166
|
+
if (isMobile()) {
|
|
167
|
+
sidebar.classList.add('open');
|
|
168
|
+
backdrop.classList.add('open');
|
|
169
|
+
} else {
|
|
170
|
+
sidebar.classList.remove('collapsed');
|
|
171
|
+
try { localStorage.setItem('tmux-web:session-sidebar', 'open'); } catch {}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function closeSidebar() {
|
|
176
|
+
if (isMobile()) {
|
|
177
|
+
sidebar.classList.remove('open');
|
|
178
|
+
backdrop.classList.remove('open');
|
|
179
|
+
} else {
|
|
180
|
+
sidebar.classList.add('collapsed');
|
|
181
|
+
try { localStorage.setItem('tmux-web:session-sidebar', 'collapsed'); } catch {}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function toggleSidebar() {
|
|
186
|
+
if (isMobile()) {
|
|
187
|
+
sidebar.classList.contains('open') ? closeSidebar() : openSidebar();
|
|
188
|
+
} else {
|
|
189
|
+
sidebar.classList.contains('collapsed') ? openSidebar() : closeSidebar();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function renderSessions(sessions) {
|
|
194
|
+
list.innerHTML = '';
|
|
195
|
+
if (!sessions || !sessions.length) {
|
|
196
|
+
list.innerHTML = '<div class="sidebar-empty">No tmux sessions</div>';
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
sessions.forEach((session) => {
|
|
200
|
+
const row = document.createElement('a');
|
|
201
|
+
row.className = 'sidebar-row' +
|
|
202
|
+
(session.name === SIDEBAR_CURRENT ? ' current' : '') +
|
|
203
|
+
(session.attached ? ' attached' : '');
|
|
204
|
+
row.href = '/s/' + encodeURIComponent(session.name);
|
|
205
|
+
row.title = session.name + ' \xB7 ' + session.windows + ' window' + (session.windows === 1 ? '' : 's');
|
|
206
|
+
|
|
207
|
+
const dot = document.createElement('span');
|
|
208
|
+
dot.className = 'sidebar-row-dot';
|
|
209
|
+
|
|
210
|
+
const name = document.createElement('span');
|
|
211
|
+
name.className = 'sidebar-row-name';
|
|
212
|
+
name.textContent = session.name;
|
|
213
|
+
|
|
214
|
+
row.appendChild(dot);
|
|
215
|
+
row.appendChild(name);
|
|
216
|
+
list.appendChild(row);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function renderSidebar() {
|
|
221
|
+
const cfg = window.__TMUX_WEB_TERMINAL__;
|
|
222
|
+
const data = cfg && cfg.sidebarSessions ? cfg.sidebarSessions : { pinned: [], recent: [] };
|
|
223
|
+
const sessions = (data.recent || []).concat(data.pinned ? data.pinned.map((p) => ({
|
|
224
|
+
name: p.sessionName,
|
|
225
|
+
windows: p.windows ?? 0,
|
|
226
|
+
attached: p.attached ?? false,
|
|
227
|
+
})) : []);
|
|
228
|
+
const seen = new Set();
|
|
229
|
+
const unique = [];
|
|
230
|
+
sessions.forEach((s) => { if (!seen.has(s.name)) { seen.add(s.name); unique.push(s); } });
|
|
231
|
+
unique.sort((a, b) => {
|
|
232
|
+
if (a.name === SIDEBAR_CURRENT) return -1;
|
|
233
|
+
if (b.name === SIDEBAR_CURRENT) return 1;
|
|
234
|
+
return a.name.localeCompare(b.name);
|
|
235
|
+
});
|
|
236
|
+
renderSessions(unique);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (toggleBtn) toggleBtn.addEventListener('click', toggleSidebar);
|
|
240
|
+
if (closeBtn) closeBtn.addEventListener('click', closeSidebar);
|
|
241
|
+
if (backdrop) backdrop.addEventListener('click', closeSidebar);
|
|
242
|
+
|
|
243
|
+
// Restore desktop collapsed state.
|
|
244
|
+
try {
|
|
245
|
+
const saved = localStorage.getItem('tmux-web:session-sidebar');
|
|
246
|
+
if (saved === 'collapsed' && !isMobile()) sidebar.classList.add('collapsed');
|
|
247
|
+
} catch {}
|
|
248
|
+
|
|
249
|
+
renderSidebar();
|
|
250
|
+
})();
|
|
251
|
+
`}function o(){return`<button type="button" id="session-sidebar-toggle" class="header-icon-btn" title="Toggle sessions sidebar" aria-label="Toggle sessions sidebar">
|
|
252
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
|
|
253
|
+
</button>`}export{s as sessionSidebarCSS,i as sessionSidebarHTML,n as sessionSidebarScript,o as sessionSidebarToggleButtonHTML};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e={template:"warm-clay",shell:{pageBg:"#faf8f5",pageFg:"#2d2926",panelBg:"#ffffff",panelBorder:"#ebe5df",panelMuted:"#7b726b",panelAccent:"#b86b52",panelAccentOn:"#ffffff",panelSuccess:"#6b9e7c",terminalBg:"#
|
|
1
|
+
const e={template:"warm-clay",shell:{pageBg:"#faf8f5",pageFg:"#2d2926",panelBg:"#ffffff",panelBorder:"#ebe5df",panelMuted:"#7b726b",panelAccent:"#b86b52",panelAccentOn:"#ffffff",panelSuccess:"#6b9e7c",terminalBg:"#faf8f5",headerGradient:"linear-gradient(180deg, rgba(255,255,255,0.98), rgba(250,248,245,0.98))"},terminal:{foreground:"#2d2926",background:"#faf8f5",cursor:"#2d2926",cursorAccent:"#faf8f5",selectionBackground:"rgba(184, 107, 82, 0.35)",selectionForeground:"#2d2926",black:"#2d2926",red:"#b94a4a",green:"#3d7a4f",yellow:"#9a7a2f",blue:"#3b6b9e",magenta:"#8a5a8a",cyan:"#2f8a8a",white:"#7b726b",brightBlack:"#5a524c",brightRed:"#c94f4f",brightGreen:"#4a9060",brightYellow:"#b08630",brightBlue:"#4477b0",brightMagenta:"#9c6a9c",brightCyan:"#389898",brightWhite:"#2d2926"}};export{e as warmClayTheme};
|