tmux-weblink 0.3.0 → 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.
- package/README.md +2 -2
- package/dist/assets/terminal-client.js +2 -2
- package/dist/assets/{xterm-NI46XXQY.js → xterm-BXEES47M.js} +2 -2
- package/dist/browser/terminal-client.js +2 -2
- package/dist/index.js +1 -1
- package/dist/lib/scheduler.js +1 -1
- package/dist/lib/session-sidebar.js +253 -0
- package/dist/lib/themes/warm-clay.js +1 -1
- package/package.json +2 -2
|
@@ -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()});
|
package/dist/index.js
CHANGED
|
@@ -9,4 +9,4 @@ import{chmodSync as Oe,existsSync as Y,readdirSync as le,statSync as Z}from"node
|
|
|
9
9
|
\x1B[31mFailed to attach to tmux session "${r}": ${c.message}\x1B[0m\r
|
|
10
10
|
`}),oe(e,1011,"pty spawn failed");return}H.add(i);let g=-1,m="";P=Lt(r,({activeIndex:c,windows:j})=>{x(e,{type:"window_changed",activeIndex:c,windows:j});const L=j.map(S=>S.index).join(","),$=c!==g||L!==m;g=c,m=L,$&&(K(r),or())}),B=setTimeout(ce,Q),i.onData(c=>{if(E){Ue();return}x(e,{type:"data",data:c})}),i.onExit(({exitCode:c})=>{F(),i&&H.delete(i),i=null,x(e,{type:"data",data:`\r
|
|
11
11
|
\x1B[2m--- tmux exited (code ${c}) ---\x1B[0m\r
|
|
12
|
-
`}),oe(e,1e3,"pty exited")})}function J(g,m){o.authenticated=!0,o.authTimeout&&(clearTimeout(o.authTimeout),o.authTimeout=null),u("auth_success",{ip:n,method:g,tokenName:m}),x(e,{type:"auth.ok",setupMode:!l.passwordHash}),ue()}function C(g,m={}){x(e,{type:"auth.failed",message:g,...m})}async function We(g){let m;try{m=JSON.parse(g)}catch{return!1}if(m.type==="auth.token"&&typeof m.token=="string"){const c=p.validateToken(m.token);if(!c){u("token_auth_failed",{ip:n});const j=R.recordFailure(n);return j.permanentLock&&(p.revokeAll(),u("permanent_lock",{ip:n,failures:j.failures})),C("Invalid or expired token",{permanentLock:j.permanentLock,retryAfterMs:j.retryAfterMs}),!0}return p.touch(c.tokenHash),J("token",c.name),!0}if(m.type==="auth"&&typeof m.password=="string"){const c=R.check(n);if(!c.allowed)return u("rate_limited",{ip:n,retryAfterMs:c.retryAfterMs,permanentLock:c.permanentLock}),C("Too many attempts",{permanentLock:c.permanentLock,retryAfterMs:c.retryAfterMs}),!0;if(!l.passwordHash){if(!l.security.allowRemoteSetup&&!Se(n))return u("setup_rejected_remote",{ip:n}),C("First-run setup must be performed from localhost"),!0;if(U)return C("Password setup in progress"),!0;const S=ye(m.password);if(S)return C(S),!0;U=!0;try{l.passwordHash=await fe(m.password),pe(l),u("password_set",{ip:n})}finally{U=!1}const{stored:X,plaintext:He}=p.createAccessToken("setup",l.security.tokenTtlDays);return x(e,{type:"auth.ok",setupMode:!0,token:He}),u("token_created",{ip:n,name:X.name,tokenId:X.id}),u("auth_success",{ip:n,method:"password",tokenName:X.name}),ue(),!0}if(!l.passwordHash)return C("Server not configured"),!0;if(!await he(m.password,l.passwordHash)){const S=R.recordFailure(n);return u("auth_failed",{ip:n,method:"password",failures:S.failures,permanentLock:S.permanentLock}),S.permanentLock&&(p.revokeAll(),u("permanent_lock",{ip:n,failures:S.failures})),C("Incorrect password",{permanentLock:S.permanentLock,retryAfterMs:S.retryAfterMs}),!0}R.recordSuccess(n);const L=`ws-${n}`,{plaintext:$}=p.createAccessToken(L,l.security.tokenTtlDays);return J("password",L),x(e,{type:"auth.ok",setupMode:!1,token:$}),!0}return!1}s?(p.touch(s.tokenHash),J("token",s.name)):(x(e,{type:"auth.required",setupMode:!l.passwordHash}),o.authTimeout=setTimeout(()=>{o.authenticated||(u("auth_timeout",{ip:n}),C("Authentication timeout"),oe(e,4e3,"Auth timeout"))},l.security.authTimeoutMs)),e.on("message",async g=>{const m=typeof g=="string"?g:g.toString("utf-8");if(!o.authenticated){if(m.length>1e6){x(e,{type:"auth.failed",message:"Message too large"});return}await We(m)||C("Authentication required");return}if(!i)return;try{if(wt(m,i))return}catch{return}let c;try{c=JSON.parse(m)}catch{return}if(c.type==="load_history"&&typeof c.before=="number"){const j=Math.max(0,Math.floor(c.before));try{const{data:L,lines:$}=Bt(N,j,f);x(e,{type:"history",data:xe(L),before:j,lines:$})}catch{x(e,{type:"history",data:"",before:j,lines:0})}}}),e.on("close",()=>{o.authTimeout&&clearTimeout(o.authTimeout),W.delete(e),u("ws_disconnected",{ip:n}),F(),P&&(P(),P=null),i&&(H.delete(i),i.kill(),i=null)}),e.on("error",()=>{o.authTimeout&&clearTimeout(o.authTimeout),W.delete(e),F(),P&&(P(),P=null),i&&(H.delete(i),i.kill(),i=null)})});function De(){_.cleanup(),Dt();for(const[e,t]of W){try{e.close(1001,"Server shutting down")}catch{}t.authTimeout&&clearTimeout(t.authTimeout)}W.clear();for(const e of H)try{e.kill()}catch{}H.clear();for(const e of Ce)try{e.kill("SIGTERM")}catch{}R.dispose(),process.exit(0)}process.on("SIGINT",De),process.on("SIGTERM",De),setInterval(()=>{p.purgeExpired()},
|
|
12
|
+
`}),oe(e,1e3,"pty exited")})}function J(g,m){o.authenticated=!0,o.authTimeout&&(clearTimeout(o.authTimeout),o.authTimeout=null),u("auth_success",{ip:n,method:g,tokenName:m}),x(e,{type:"auth.ok",setupMode:!l.passwordHash}),ue()}function C(g,m={}){x(e,{type:"auth.failed",message:g,...m})}async function We(g){let m;try{m=JSON.parse(g)}catch{return!1}if(m.type==="auth.token"&&typeof m.token=="string"){const c=p.validateToken(m.token);if(!c){u("token_auth_failed",{ip:n});const j=R.recordFailure(n);return j.permanentLock&&(p.revokeAll(),u("permanent_lock",{ip:n,failures:j.failures})),C("Invalid or expired token",{permanentLock:j.permanentLock,retryAfterMs:j.retryAfterMs}),!0}return p.touch(c.tokenHash),J("token",c.name),!0}if(m.type==="auth"&&typeof m.password=="string"){const c=R.check(n);if(!c.allowed)return u("rate_limited",{ip:n,retryAfterMs:c.retryAfterMs,permanentLock:c.permanentLock}),C("Too many attempts",{permanentLock:c.permanentLock,retryAfterMs:c.retryAfterMs}),!0;if(!l.passwordHash){if(!l.security.allowRemoteSetup&&!Se(n))return u("setup_rejected_remote",{ip:n}),C("First-run setup must be performed from localhost"),!0;if(U)return C("Password setup in progress"),!0;const S=ye(m.password);if(S)return C(S),!0;U=!0;try{l.passwordHash=await fe(m.password),pe(l),u("password_set",{ip:n})}finally{U=!1}const{stored:X,plaintext:He}=p.createAccessToken("setup",l.security.tokenTtlDays);return x(e,{type:"auth.ok",setupMode:!0,token:He}),u("token_created",{ip:n,name:X.name,tokenId:X.id}),u("auth_success",{ip:n,method:"password",tokenName:X.name}),ue(),!0}if(!l.passwordHash)return C("Server not configured"),!0;if(!await he(m.password,l.passwordHash)){const S=R.recordFailure(n);return u("auth_failed",{ip:n,method:"password",failures:S.failures,permanentLock:S.permanentLock}),S.permanentLock&&(p.revokeAll(),u("permanent_lock",{ip:n,failures:S.failures})),C("Incorrect password",{permanentLock:S.permanentLock,retryAfterMs:S.retryAfterMs}),!0}R.recordSuccess(n);const L=`ws-${n}`,{plaintext:$}=p.createAccessToken(L,l.security.tokenTtlDays);return J("password",L),x(e,{type:"auth.ok",setupMode:!1,token:$}),!0}return!1}s?(p.touch(s.tokenHash),J("token",s.name)):(x(e,{type:"auth.required",setupMode:!l.passwordHash}),o.authTimeout=setTimeout(()=>{o.authenticated||(u("auth_timeout",{ip:n}),C("Authentication timeout"),oe(e,4e3,"Auth timeout"))},l.security.authTimeoutMs)),e.on("message",async g=>{const m=typeof g=="string"?g:g.toString("utf-8");if(!o.authenticated){if(m.length>1e6){x(e,{type:"auth.failed",message:"Message too large"});return}await We(m)||C("Authentication required");return}if(!i)return;try{if(wt(m,i))return}catch{return}let c;try{c=JSON.parse(m)}catch{return}if(c.type==="load_history"&&typeof c.before=="number"){const j=Math.max(0,Math.floor(c.before));try{const{data:L,lines:$}=Bt(N,j,f);x(e,{type:"history",data:xe(L),before:j,lines:$})}catch{x(e,{type:"history",data:"",before:j,lines:0})}}}),e.on("close",()=>{o.authTimeout&&clearTimeout(o.authTimeout),W.delete(e),u("ws_disconnected",{ip:n}),F(),P&&(P(),P=null),i&&(H.delete(i),i.kill(),i=null)}),e.on("error",()=>{o.authTimeout&&clearTimeout(o.authTimeout),W.delete(e),F(),P&&(P(),P=null),i&&(H.delete(i),i.kill(),i=null)})});function De(){_.cleanup(),Dt();for(const[e,t]of W){try{e.close(1001,"Server shutting down")}catch{}t.authTimeout&&clearTimeout(t.authTimeout)}W.clear();for(const e of H)try{e.kill()}catch{}H.clear();for(const e of Ce)try{e.kill("SIGTERM")}catch{}R.dispose(),process.exit(0)}process.on("SIGINT",De),process.on("SIGTERM",De),setInterval(()=>{p.purgeExpired()},3600*1e3).unref();
|
package/dist/lib/scheduler.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFileSync as o}from"node:child_process";import{randomUUID as c}from"node:crypto";import{ARM_SCAN_INTERVAL_MS as u,isValidDelayMs as h,MAX_TIMER_MS as n}from"./schedule-delay.js";const T=
|
|
1
|
+
import{execFileSync as o}from"node:child_process";import{randomUUID as c}from"node:crypto";import{ARM_SCAN_INTERVAL_MS as u,isValidDelayMs as h,MAX_TIMER_MS as n}from"./schedule-delay.js";const T=10080*60*1e3;function m(a,e,s){const t=`${a}:${e}`;o("tmux",["send-keys","-t",t,"-l",s],{timeout:5e3}),o("tmux",["send-keys","-t",t,"Enter"],{timeout:5e3})}function w(a){if(!a||typeof a!="object")return!1;const e=a;return typeof e.delayMs=="number"&&h(e.delayMs)}function y(a){if(!a||typeof a!="object")return!1;const e=a;return typeof e.sessionName=="string"&&e.sessionName.length>0&&typeof e.windowIndex=="number"&&Number.isInteger(e.windowIndex)&&e.windowIndex>=0&&typeof e.text=="string"&&e.text.length>0&&e.text.length<=4096&&typeof e.delayMs=="number"&&h(e.delayMs)}class I{deps;scheduledTasks=new Map;now;setTimer;clearTimer;setIntervalFn;clearIntervalFn;armScanIntervalMs;createId;sendKeys;onError;onMissedTask;historyRetentionMs;scanIntervalHandle=null;constructor(e){this.deps=e,this.now=e.now??Date.now,this.setTimer=e.setTimer??setTimeout,this.clearTimer=e.clearTimer??clearTimeout,this.setIntervalFn=e.setInterval??setInterval,this.clearIntervalFn=e.clearInterval??clearInterval,this.armScanIntervalMs=e.armScanIntervalMs??u,this.createId=e.createId??c,this.sendKeys=e.sendKeys??m,this.onError=e.onError??(s=>console.error(s)),this.onMissedTask=e.onMissedTask??(()=>{}),this.historyRetentionMs=e.historyRetentionMs??T}record(e){this.deps.db.data.triggeredTasks??=[],this.deps.db.data.triggeredTasks.push(e);const s=this.now()-this.historyRetentionMs;this.deps.db.data.triggeredTasks=this.deps.db.data.triggeredTasks.filter(t=>t.triggeredAt>=s)}listTriggered(){this.deps.db.data.triggeredTasks??=[];const e=this.now()-this.historyRetentionMs;return this.deps.db.data.triggeredTasks.filter(s=>s.triggeredAt>=e).sort((s,t)=>t.triggeredAt-s.triggeredAt)}async restoreFromDb(){const e=this.now(),s=[];for(const r of this.deps.db.data.scheduledTasks)r.fireAt<=e?(s.push(r.id),this.onMissedTask(r),this.record({...r,triggeredAt:e,status:"missed"})):this.registerTask(r);this.deps.db.data.triggeredTasks??=[];const t=this.deps.db.data.triggeredTasks.length,i=e-this.historyRetentionMs;this.deps.db.data.triggeredTasks=this.deps.db.data.triggeredTasks.filter(r=>r.triggeredAt>=i);const d=this.deps.db.data.triggeredTasks.length!==t;s.length&&(this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(r=>!s.includes(r.id))),(s.length||d)&&await this.deps.db.write(),this.scanPending(),this.startScanLoop()}list(e){return[...this.scheduledTasks.values()].filter(s=>!e||s.sessionName===e).map(({id:s,sessionName:t,windowIndex:i,text:d,fireAt:r,createdAt:l})=>({id:s,sessionName:t,windowIndex:i,text:d,fireAt:r,createdAt:l,remainingMs:Math.max(0,r-this.now())})).sort((s,t)=>s.fireAt-t.fireAt)}async create(e){const s=this.createId(),t=this.now(),i={id:s,sessionName:e.sessionName,windowIndex:e.windowIndex,text:e.text,fireAt:t+e.delayMs,createdAt:t};return this.registerTask(i),this.deps.db.data.scheduledTasks.push(i),await this.deps.db.write(),this.tryArm(i.id),i}async reschedule(e,s){const t=this.scheduledTasks.get(e);if(!t)return null;this.disarmTask(e);const i={...t,fireAt:this.now()+s};this.registerTask(i);const d=this.deps.db.data.scheduledTasks.findIndex(r=>r.id===e);return d>=0&&(this.deps.db.data.scheduledTasks[d]=i),await this.deps.db.write(),this.tryArm(e),i}async delete(e){return this.scheduledTasks.get(e)?(this.disarmTask(e),this.scheduledTasks.delete(e),this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(t=>t.id!==e),await this.deps.db.write(),!0):!1}cleanup(){if(this.scanIntervalHandle!=null){try{this.clearIntervalFn(this.scanIntervalHandle)}catch{}this.scanIntervalHandle=null}for(const e of this.scheduledTasks.values())if(e.timeoutHandle!=null)try{this.clearTimer(e.timeoutHandle)}catch{}this.scheduledTasks.clear()}scanPending(){for(const e of[...this.scheduledTasks.values()]){const s=e.fireAt-this.now();if(s<=0){e.timeoutHandle!=null&&this.disarmTask(e.id),this.fireTask(e);continue}s<=n&&e.timeoutHandle==null&&this.armTask(e)}}registerTask(e){this.scheduledTasks.set(e.id,{...e,timeoutHandle:null})}disarmTask(e){const s=this.scheduledTasks.get(e);s&&s.timeoutHandle!=null&&(this.clearTimer(s.timeoutHandle),s.timeoutHandle=null)}tryArm(e){const s=this.scheduledTasks.get(e);if(!s)return;const t=s.fireAt-this.now();if(t<=0){this.fireTask(s);return}t<=n&&s.timeoutHandle==null&&this.armTask(s)}armTask(e){const s=e.fireAt-this.now();if(s<=0){this.fireTask(e);return}this.disarmTask(e.id);const t=Math.min(s,n),i=this.setTimer(()=>this.onTimer(e.id),t);this.scheduledTasks.set(e.id,{...e,timeoutHandle:i})}onTimer(e){const s=this.scheduledTasks.get(e);if(s){if(s.timeoutHandle=null,s.fireAt<=this.now()){this.fireTask(s);return}this.armTask(s)}}startScanLoop(){this.scanIntervalHandle==null&&(this.scanIntervalHandle=this.setIntervalFn(()=>this.scanPending(),this.armScanIntervalMs))}fireTask(e){if(!this.scheduledTasks.has(e.id))return;this.disarmTask(e.id);const s=this.now(),t={id:e.id,sessionName:e.sessionName,windowIndex:e.windowIndex,text:e.text,fireAt:e.fireAt,createdAt:e.createdAt};try{this.sendKeys(e.sessionName,e.windowIndex,e.text),this.record({...t,triggeredAt:s,status:"ok"})}catch(i){const d=String(i?.message??i);this.onError(`[scheduler] send-keys to ${e.sessionName}:${e.windowIndex} failed: ${d}`),this.record({...t,triggeredAt:s,status:"error",error:d})}this.scheduledTasks.delete(e.id),this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(i=>i.id!==e.id),this.deps.db.write().catch(this.onError)}}export{I as SchedulerService,w as isValidRescheduleInput,y as isValidScheduleInput,m as sendTmuxKeys};
|
|
@@ -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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmux-weblink",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
|
-
"url": "https://github.com/Willlee11/tmux-weblink"
|
|
39
|
+
"url": "git+https://github.com/Willlee11/tmux-weblink.git"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=22"
|