tmux-weblink 0.3.4 → 1.0.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.
- package/dist/assets/chunk-O3DBTEWH.js +4 -0
- package/dist/assets/shell-client.js +13 -0
- package/dist/assets/terminal-client.js +1 -4
- package/dist/browser/shell-client.js +13 -0
- package/dist/browser/terminal-client.js +1 -4
- package/dist/browser/terminal-core.js +4 -0
- package/dist/frontend.js +1 -1
- package/dist/index.js +7 -7
- package/dist/lib/fs-access.js +1 -0
- package/dist/lib/icons.js +1 -1
- package/dist/lib/pages/files-index.js +263 -0
- package/dist/lib/pages/shell.js +463 -0
- package/dist/lib/shared-layout.js +86 -31
- package/dist/lib/tmux-windows.js +4 -4
- package/package.json +1 -1
|
@@ -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};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{a as F}from"./chunk-O3DBTEWH.js";var h=window.__TMUX_WEB_SHELL__,i=null,p=null,D="sessions",r="",C="",x="",M="",l=document.getElementById("sidebar-content"),g=document.getElementById("main-placeholder"),y=document.getElementById("terminal-container"),k=document.getElementById("file-editor"),P=document.getElementById("fe-path"),o=document.getElementById("fe-status"),E=document.getElementById("fe-content"),f=document.getElementById("fe-save"),R=document.getElementById("fe-delete"),V=document.getElementById("fe-back"),H=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 _(){J()&&j.classList.add("collapsed")}function O(){j.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{O(),z("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{O(),z("files")});function z(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"?b():U()}async function b(){l.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===p?" 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
|
+
<span>${L(n.name)}</span>
|
|
3
|
+
<button class="session-edit-btn" data-session="${L(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
|
+
`,a.addEventListener("click",m=>{m.target.closest(".session-edit-btn")||S(n.name)}),l.appendChild(a);let s=a.querySelector(".session-edit-btn");s&&s.addEventListener("click",m=>{m.stopPropagation(),X(n.name,s)})}}catch{l.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 L(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function S(e){if(!(p===e&&i)){k.style.display="none",g.style.display="none",y.style.display="",y.classList.add("terminal-pending"),_(),i&&(i.destroy(),i=null),p=e;try{i=await F(y,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);y.textContent="Failed to open terminal for "+e+": "+n,y.classList.remove("terminal-pending")}}}var I=[];function U(){if(I=h.fsRoots||[],I.length===0){l.innerHTML='<div class="file-tree-info">Set <code>TMUX_WEB_FS_ROOTS</code> to browse files.</div>';return}l.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> ${L(e)}`,t.addEventListener("click",()=>v(e)),l.appendChild(t)}}async function v(e){r=e,l.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();l.innerHTML="";let n=q(e);if(n&&!I.some(s=>e===s)){let s=document.createElement("div");s.className="file-tree-item",s.innerHTML="..",s.addEventListener("click",()=>v(n)),l.appendChild(s)}if(t.dirs)for(let s of t.dirs){let m=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> ${L(m)}`,d.addEventListener("click",()=>v(s)),l.appendChild(d)}else l.innerHTML="";let a=t.files||[];for(let s of a){let m=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> ${L(m)}`,d.addEventListener("click",()=>W(s)),l.appendChild(d)}}catch{l.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function q(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function W(e){C=e,y.style.display="none",i&&(i.destroy(),i=null,p=null),_(),g.style.display="none",k.style.display="flex",P.textContent=e,o.textContent="Loading...",f.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,M=n.content,o.textContent=n.size+" bytes",f.style.display=""}catch{o.textContent="Failed to load",E.value=""}}E.addEventListener("input",()=>{x=E.value}),f.addEventListener("click",async()=>{f.textContent="Saving...",f.disabled=!0;try{let e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:C,content:x})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Save failed";return}M=x,o.textContent="Saved"}catch{o.textContent="Save failed"}finally{f.disabled=!1,f.textContent="Save"}}),R.addEventListener("click",async()=>{if(confirm("Delete "+C+"?"))try{let e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:C})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Delete failed";return}k.style.display="none",g.style.display="flex",r&&v(r)}catch{o.textContent="Delete failed"}}),V.addEventListener("click",()=>{k.style.display="none",g.style.display="flex",r&&v(r)}),$.addEventListener("click",async()=>{let e=H.value.trim();if(!e)return;let t=r+"/"+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}H.value="",r&&v(r)}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 u="",w=document.createElement("div");w.className="session-popover-backdrop",document.body.appendChild(w);var c=document.createElement("div");c.id="session-popover",c.className="session-popover",c.style.display="none",c.innerHTML=`<div class="sp-field">
|
|
5
|
+
<label>Rename session</label>
|
|
6
|
+
<input type="text" id="sp-name" />
|
|
7
|
+
</div>
|
|
8
|
+
<div class="sp-actions">
|
|
9
|
+
<button class="btn primary" id="sp-save">Save</button>
|
|
10
|
+
<button class="btn" id="sp-cancel">Cancel</button>
|
|
11
|
+
</div>
|
|
12
|
+
<hr class="sp-divider" />
|
|
13
|
+
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(c);function A(e){let t=e.getBoundingClientRect();c.style.top=t.bottom+4+"px",c.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function X(e,t){u=e;let n=document.getElementById("sp-name");n.value=e,w.classList.add("open"),c.style.display="block",A(t),setTimeout(()=>n.focus(),50)}function B(){w.classList.remove("open"),c.style.display="none"}w.addEventListener("click",B),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===u){B();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:u,newName:e})})).ok)return;B(),b(),p===u&&S(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+u+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:u})})).ok)return;B(),p===u&&(i&&(i.destroy(),i=null),p=null,y.style.display="none",g.style.display="flex"),b()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",B);var Z={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ","ctrl-c":"","ctrl-d":"",enter:"\r",exit:"exit\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!i)return;let n=Z[t.dataset.key];n&&(i.sendInput(n),i.focus())}),window.__openSession=S,window.__refreshSidebar=b,b();
|
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
`).join(`
|
|
3
|
-
`).split(`
|
|
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
|
+
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()}});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import{initTerminal as z}from"./terminal-core.js";const h=window.__TMUX_WEB_SHELL__;let o=null,f=null,D="sessions",m="",k="",B="",H="";const l=document.getElementById("sidebar-content"),b=document.getElementById("main-placeholder"),u=document.getElementById("terminal-container"),S=document.getElementById("file-editor"),R=document.getElementById("fe-path"),c=document.getElementById("fe-status"),E=document.getElementById("fe-content"),y=document.getElementById("fe-save"),V=document.getElementById("fe-delete"),$=document.getElementById("fe-back"),N=document.getElementById("fe-new-name"),q=document.getElementById("fe-new-btn"),T=document.getElementById("settings-backdrop"),j=document.getElementById("settings-popover"),F=document.querySelector(".sidebar");function J(){return window.matchMedia("(max-width: 640px)").matches}function O(){J()&&F.classList.add("collapsed")}function _(){F.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{_(),P("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{_(),P("files")});function P(e){D=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));const t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?L():U()}async function L(){l.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{const r=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(const p of r){const s=document.createElement("div");s.className="session-item"+(p.name===f?" active":""),s.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${p.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
|
|
2
|
+
<span>${w(p.name)}</span>
|
|
3
|
+
<button class="session-edit-btn" data-session="${w(p.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
|
+
`,s.addEventListener("click",a=>{a.target.closest(".session-edit-btn")||M(p.name)}),l.appendChild(s);const i=s.querySelector(".session-edit-btn");i&&i.addEventListener("click",a=>{a.stopPropagation(),Z(p.name,i)})}}catch{l.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}const e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function w(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function M(e){if(!(f===e&&o)){S.style.display="none",b.style.display="none",u.style.display="",u.classList.add("terminal-pending"),O(),o&&(o.destroy(),o=null),f=e;try{o=await z(u,e,{terminal:h.terminal,scrollback:h.scrollback,theme:h.theme,renderer:h.renderer})}catch(t){console.error("[shell] terminal init failed:",t);const n=t&&typeof t=="object"&&"message"in t?t.message:String(t);u.textContent="Failed to open terminal for "+e+": "+n,u.classList.remove("terminal-pending")}}}let I=[];function U(){if(I=h.fsRoots||[],I.length===0){l.innerHTML='<div class="file-tree-info">Set <code>TMUX_WEB_FS_ROOTS</code> to browse files.</div>';return}l.innerHTML="";for(const e of I){const t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${w(e)}`,t.addEventListener("click",()=>g(e)),l.appendChild(t)}}async function g(e){m=e,l.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();l.innerHTML="";const r=W(e);if(r&&!I.some(i=>e===i)){const i=document.createElement("div");i.className="file-tree-item",i.innerHTML="..",i.addEventListener("click",()=>g(r)),l.appendChild(i)}if(n.dirs)for(const s of n.dirs){const i=s.split("/").pop()||s.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item",a.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> ${w(i)}`,a.addEventListener("click",()=>g(s)),l.appendChild(a)}else l.innerHTML="";const p=n.files||[];for(const s of p){const i=s.split("/").pop()||s.split("\\").pop(),a=document.createElement("div");a.className="file-tree-item file",a.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${w(i)}`,a.addEventListener("click",()=>A(s)),l.appendChild(a)}}catch{l.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function W(e){const t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function A(e){k=e,u.style.display="none",o&&(o.destroy(),o=null,f=null),O(),b.style.display="none",S.style.display="flex",R.textContent=e,c.textContent="Loading...",y.style.display="none";try{const t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){const r=await t.json();c.textContent=r.error||"Failed to load",E.value="";return}const n=await t.json();E.value=n.content,B=n.content,H=n.content,c.textContent=n.size+" bytes",y.style.display=""}catch{c.textContent="Failed to load",E.value=""}}E.addEventListener("input",()=>{B=E.value}),y.addEventListener("click",async()=>{y.textContent="Saving...",y.disabled=!0;try{const e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:k,content:B})});if(!e.ok){const t=await e.json();c.textContent=t.error||"Save failed";return}H=B,c.textContent="Saved"}catch{c.textContent="Save failed"}finally{y.disabled=!1,y.textContent="Save"}}),V.addEventListener("click",async()=>{if(confirm("Delete "+k+"?"))try{const e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:k})});if(!e.ok){const t=await e.json();c.textContent=t.error||"Delete failed";return}S.style.display="none",b.style.display="flex",m&&g(m)}catch{c.textContent="Delete failed"}}),$.addEventListener("click",()=>{S.style.display="none",b.style.display="flex",m&&g(m)}),q.addEventListener("click",async()=>{const e=N.value.trim();if(!e)return;const t=m+"/"+e;try{const n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){const r=await n.json();c.textContent=r.error||"Create failed";return}N.value="",m&&g(m)}catch{c.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=j.classList.toggle("open");T.classList.toggle("open",e)}),T.addEventListener("click",()=>{j.classList.remove("open"),T.classList.remove("open")});let v="";const C=document.createElement("div");C.className="session-popover-backdrop",document.body.appendChild(C);const d=document.createElement("div");d.id="session-popover",d.className="session-popover",d.style.display="none",d.innerHTML=`<div class="sp-field">
|
|
5
|
+
<label>Rename session</label>
|
|
6
|
+
<input type="text" id="sp-name" />
|
|
7
|
+
</div>
|
|
8
|
+
<div class="sp-actions">
|
|
9
|
+
<button class="btn primary" id="sp-save">Save</button>
|
|
10
|
+
<button class="btn" id="sp-cancel">Cancel</button>
|
|
11
|
+
</div>
|
|
12
|
+
<hr class="sp-divider" />
|
|
13
|
+
<button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(d);function X(e){const t=e.getBoundingClientRect();d.style.top=t.bottom+4+"px",d.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Z(e,t){v=e;const n=document.getElementById("sp-name");n.value=e,C.classList.add("open"),d.style.display="block",X(t),setTimeout(()=>n.focus(),50)}function x(){C.classList.remove("open"),d.style.display="none"}C.addEventListener("click",x),document.getElementById("sp-save").addEventListener("click",async()=>{const e=document.getElementById("sp-name").value.trim();if(!e||e===v){x();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:v,newName:e})})).ok)return;x(),L(),f===v&&M(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+v+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:v})})).ok)return;x(),f===v&&(o&&(o.destroy(),o=null),f=null,u.style.display="none",b.style.display="flex"),L()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",x);const G={esc:"\x1B",tab:" ","s-tab":"\x1B[Z",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",space:" ","ctrl-c":"","ctrl-d":"",enter:"\r",exit:"exit\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const t=e.target.closest("button[data-key]");if(!t||!o)return;const n=G[t.dataset.key];n&&(o.sendInput(n),o.focus())}),window.__openSession=M,window.__refreshSidebar=L,L();
|
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
`).join(`
|
|
3
|
-
`).split(`
|
|
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()});
|
|
1
|
+
import{initTerminal as t}from"./terminal-core.js";const i=window.__TMUX_WEB_TERMINAL__;if(!i)throw new Error("missing tmux-web terminal config");const e=document.getElementById("terminal-container");if(!e)throw new Error("missing terminal container");const{sessionName:r,...m}=i;t(e,r,m).then(n=>{window.tmuxWeb={sendInput:o=>n.sendInput(o),focusTerminal:()=>n.focus()}});
|
|
@@ -0,0 +1,4 @@
|
|
|
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
|
+
`).join(`
|
|
3
|
+
`).split(`
|
|
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};
|
package/dist/frontend.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{renderTerminal as o}from"./lib/pages/terminal.js";import{renderLoginPage as d}from"./lib/pages/login.js";import{renderLanding as
|
|
1
|
+
import{renderTerminal as o}from"./lib/pages/terminal.js";import{renderLoginPage as d}from"./lib/pages/login.js";import{renderLanding as x}from"./lib/pages/landing.js";import{renderNotesIndex as f}from"./lib/pages/notes-index.js";import{renderNotesPage as g}from"./lib/pages/notes-page.js";import{renderSettings as s,renderThemeSettings as a}from"./lib/pages/settings.js";import{renderScheduleIndex as I}from"./lib/pages/schedule-index.js";import{renderAgentsIndex as h}from"./lib/pages/agents-index.js";import{renderHistoryIndex as c}from"./lib/pages/history-index.js";import{renderQuickCommandsPage as L}from"./lib/pages/quick-commands.js";import{renderFilesIndex as T}from"./lib/pages/files-index.js";import{renderShell as y}from"./lib/pages/shell.js";export{h as renderAgentsIndex,T as renderFilesIndex,c as renderHistoryIndex,x as renderLanding,d as renderLoginPage,f as renderNotesIndex,g as renderNotesPage,L as renderQuickCommandsPage,I as renderScheduleIndex,s as renderSettings,y as renderShell,o as renderTerminal,a as renderThemeSettings};
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{chmodSync as
|
|
2
|
+
import{chmodSync as $e,existsSync as K,mkdirSync as Be,readdirSync as pe,readFileSync as Ve,statSync as D,unlinkSync as Ge,writeFileSync as ze}from"node:fs";import{readFile as Ke}from"node:fs/promises";import{createRequire as Qe}from"node:module";import g from"node:path";import{fileURLToPath as Xe}from"node:url";import{Hono as Ye}from"hono";import{serve as Je}from"@hono/node-server";import{WebSocketServer as Ze,WebSocket as fe}from"ws";import*as et from"node-pty";try{const e=g.dirname(Qe(import.meta.url).resolve("node-pty/package.json")),t=g.join(e,"prebuilds");if(K(t))for(const r of pe(t)){const n=g.join(t,r,"spawn-helper");K(n)&&!(D(n).mode&73)&&$e(n,493)}}catch{}import{listSessions as v}from"./sessions.js";import{renderTerminal as tt,renderLoginPage as rt,renderNotesIndex as nt,renderNotesPage as ot,renderSettings as st,renderThemeSettings as at,renderScheduleIndex as it,renderAgentsIndex as dt,renderHistoryIndex as ut,renderQuickCommandsPage as ct,renderFilesIndex as lt,renderShell as mt}from"./frontend.js";import{loadSecurityConfig as pt,saveSecurityConfig as he}from"./lib/security-config.js";import{hashPassword as ye,verifyPassword as we,validatePassword as ge,TokenStore as ft}from"./lib/auth.js";import{RateLimiter as ht}from"./lib/rateLimiter.js";import{atomicWriteFileSync as yt}from"./lib/atomicWrite.js";import{resolveFsPath as Q,resolveFsRoots as je,MAX_FILE_BYTES as ke,walkRecursive as wt}from"./lib/fs-access.js";import{audit as c}from"./lib/auditLog.js";import{db as j}from"./lib/db.js";import{recordSessionAccess as gt,getSessionAccessMap as q}from"./lib/session-access.js";import{listWindowHistory as jt,clearWindowHistory as kt}from"./lib/window-history.js";import{loadExtensions as vt,spawnExtensionBackend as xt,registerExtensionRoutes as Tt}from"./lib/ext-loader.js";import{SchedulerService as bt,isValidScheduleInput as St,isValidRescheduleInput as At}from"./lib/scheduler.js";import{getScheduleDelayError as ve}from"./lib/schedule-delay.js";import{handleClientMessage as qt}from"./lib/ws-message.js";import{loadDotEnv as It}from"./lib/load-env.js";import{cmdAdd as _t,cmdRemove as Ct,cmdList as Rt,cmdSetup as Et,cmdTheme as Mt,printUsage as xe,printVersion as Ot}from"./lib/cli.js";import{readSettings as ne,writeSettings as Pt}from"./lib/settings.js";import{readActiveTheme as Lt,setActiveThemeTemplate as Te}from"./lib/theme-store.js";import{isThemeTemplateId as be,THEME_TEMPLATE_IDS as Nt}from"./lib/themes/index.js";import{installPlugin as Dt,uninstallPlugin as Ut}from"./lib/plugins.js";import{buildCommandbarSessions as C}from"./lib/commandbar.js";import{pinView as Wt,unpinView as Ft,listPinnedViews as Ht}from"./lib/pinned-views.js";import{listWindowLabels as $t,setWindowLabel as Bt}from"./lib/window-labels.js";import{captureAndStoreWindows as oe,getStoredWindows as Se}from"./lib/session-windows.js";import{acquireControlClient as Vt,killAllControlClients as Gt}from"./lib/tmux-control.js";import{buildSidebarSessions as zt}from"./lib/sessions-sidebar.js";import{createQuickCommand as Kt,deleteQuickCommand as Qt,listQuickCommands as se,updateQuickCommand as Xt}from"./lib/quick-commands.js";import{getSessionPaneTarget as Yt,capturePaneTail as Jt,capturePaneHistoryChunk as Zt,isAlternateScreen as er,toCrlf as Ae}from"./lib/tmux-capture.js";import{readTerminalBufferConfig as tr}from"./lib/terminal-config.js";import{ImageUploadError as rr,saveUploadedImage as nr}from"./lib/image-upload.js";import{listSessionWindows as or,selectSessionWindow as sr,newSessionWindow as ar,renameSessionWindow as ir,newTmuxSession as dr,renameSession as ur,killSession as cr,TmuxWindowsError as X}from"./lib/tmux-windows.js";import{getActivePaneInfo as lr}from"./lib/tmux-panes.js";import{recordWatchedPane as mr}from"./lib/watched-panes.js";import{probeWatchedPanes as pr,startBackgroundWatch as fr,getCachedAgentStatuses as hr,requestProbe as yr}from"./lib/agents-watch.js";It();const B=tr(),l=pt(),h=new ft,E=new ht;let U=!1;const Y="tmux-web-token",wr=365;function qe(e){return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}function J(e){if(l.security.trustProxy){const t=(e.req.header("x-forwarded-for")||"").split(",")[0].trim();if(t)return t}return e.env?.incoming?.socket?.remoteAddress||"unknown"}function gr(e){if(l.security.trustProxy){const t=(e.headers["x-forwarded-for"]||"").split(",")[0].trim();if(t)return t}return e.socket.remoteAddress||"unknown"}function ae(e){const t=e.req.header("authorization");if(t?.startsWith("Bearer "))return t.slice(7);const r=e.req.header("cookie");if(!r)return null;const n=r.match(new RegExp(`(?:^|;\\s*)${Y}=([^;]+)`));return n?decodeURIComponent(n[1]):null}function Ie(e){const t=e.headers.authorization;if(typeof t=="string"&&t.startsWith("Bearer "))return t.slice(7);const r=e.headers.cookie;if(typeof r!="string")return null;const n=r.match(new RegExp(`(?:^|;\\s*)${Y}=([^;]+)`));return n?decodeURIComponent(n[1]):null}function kn(e){const t=ae(e);return t?h.validateToken(t):null}function vn(e){const t=Ie(e);return t?h.validateToken(t):null}function _e(e,t){const r=e.req.header("x-forwarded-proto")==="https"||e.req.url.startsWith("https:");e.header("Set-Cookie",`${Y}=${encodeURIComponent(t)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${wr*86400}${r?"; Secure":""}`)}function jr(e){e.header("Set-Cookie",`${Y}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`)}function i(){return async(e,t)=>{const r=ae(e);return!r||!h.validateToken(r)?(c("http_unauthorized",{ip:J(e)}),e.json({error:"unauthorized"},401)):t()}}function kr(e){const t=encodeURIComponent(e.req.url);return e.redirect(`/login?returnTo=${t}`,302)}function I(){return async(e,t)=>{const r=ae(e);return!r||!h.validateToken(r)?kr(e):t()}}const W=new Map;function vr(e){let t=0;for(const r of W.values())r.ip===e&&t++;return t}function ie(e,t,r){try{e.close(t,r)}catch{}}function xn(e,t){e.readyState===fe.OPEN&&e.send(JSON.stringify(t))}function xr(e,t){let r="xterm";(t==="ghostty"||t==="xterm")&&(r=t);const n=process.env.TMUX_WEB_TERMINAL_RENDERER?.trim().toLowerCase();(n==="ghostty"||n==="xterm")&&(r=n);for(const a of e)a==="--ghostty"&&(r="ghostty"),a==="--xterm"&&(r="xterm");return r}const Tr=7;function Ce(e){return typeof e!="number"||!Number.isFinite(e)?Tr:Math.min(365,Math.max(1,Math.round(e)))}const Re=process.argv.slice(2);{const e=Re.filter(t=>t!=="--ghostty"&&t!=="--xterm");if(e.length>0){const[t,r]=e;switch(t){case"add":r||(console.error("usage: tmux-web add <package>"),process.exit(1)),await _t(r),process.exit(0);case"remove":case"rm":r||(console.error("usage: tmux-web remove <package>"),process.exit(1)),await Ct(r),process.exit(0);case"list":case"ls":await Rt(),process.exit(0);case"setup":await Et(e),process.exit(0);case"theme":await Mt(e.slice(1)),process.exit(0);case"help":case"--help":case"-h":xe(),process.exit(0);case"-V":case"--version":case"-v":Ot(),process.exit(0);default:console.error(`unknown argument: ${t}`),xe(),process.exit(1)}}}function x(e,t){e.readyState===fe.OPEN&&e.send(JSON.stringify(t))}const F=new Set,Ee=[];await j.read(),j.data.sessionAccess??=[],j.data.pinnedViews??=[],j.data.watchedPanes??=[],j.data.triggeredTasks??=[],j.data.quickCommands??=[];const V=await ne();let y=await Lt();const p=V.commandbar===!0,_=V.agents===!0,Me=_&&V.agentsBackgroundWatch===!0,Z=xr(Re,V.terminalRenderer),Oe=Ce(V.scheduleHistoryDays),Pe=g.join(process.cwd(),"extensions"),de=await vt(Pe);for(const e of de)e.start&&Ee.push(xt(e.dir,e));const M=new bt({db:j,historyRetentionMs:Oe*864e5,onMissedTask:e=>console.warn(`[scheduler] dropped missed task ${e.id} (was due ${new Date(e.fireAt).toISOString()})`)});await M.restoreFromDb();function ee(e){if(_)try{const t=lr(e);if(!t)return;mr({paneId:t.paneId,sessionName:e,windowIndex:t.windowIndex,paneIndex:t.paneIndex}).catch(()=>{})}catch{}}const o=new Ye;o.use("*",async(e,t)=>{await t(),e.header("X-Content-Type-Options","nosniff"),e.header("X-Frame-Options","DENY"),e.header("Content-Security-Policy",["default-src 'self'","script-src 'self' 'unsafe-inline'","style-src 'self' 'unsafe-inline'","connect-src 'self' ws: wss: http: https:","img-src 'self' data:","font-src 'self'","worker-src 'self' blob:"].join("; ")),e.header("Referrer-Policy","strict-origin-when-cross-origin")}),o.use("*",async(e,t)=>{const r=e.req.method;if(r==="GET"||r==="HEAD"||r==="OPTIONS")return t();const n=e.req.header("sec-fetch-site");return n&&n!=="same-origin"&&n!=="none"?e.text("cross-site request blocked",403):t()}),Tt(o,Pe,de);const br=g.dirname(Xe(import.meta.url)),Sr=[g.join(br,"assets"),g.join(process.cwd(),"dist","assets")];o.get("/assets/:file",async e=>{const t=e.req.param("file");if(!/^[a-zA-Z0-9._-]+$/.test(t))return e.notFound();for(const r of Sr){const n=g.join(r,t);if(!K(n))continue;const a=await Ke(n),s=g.extname(t),d={".css":"text/css; charset=utf-8",".js":"application/javascript; charset=utf-8",".map":"application/json; charset=utf-8"};return e.body(a,200,{"Content-Type":d[s]??"application/octet-stream"})}return e.notFound()});const Ar=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
3
3
|
<rect x="2.5" y="5.5" width="27" height="21" rx="4" fill="#0d1117" stroke="#7dd3fc" stroke-width="2"/>
|
|
4
4
|
<path d="M8 13l4.2 3.1L8 19.2" fill="none" stroke="#7dd3fc" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
5
5
|
<line x1="15.5" y1="19.6" x2="22.5" y2="19.6" stroke="#7dd3fc" stroke-width="2.2" stroke-linecap="round"/>
|
|
6
|
-
</svg>`,Ee=e=>e.body(fr,200,{"Content-Type":"image/svg+xml; charset=utf-8","Cache-Control":"public, max-age=86400"});a.get("/favicon.svg",Ee),a.get("/favicon.ico",Ee),a.get("/login",e=>{const r=e.req.query("setup")==="1"?!0:!l.passwordHash,n=e.req.query("error");return e.html(Je({setupMode:r,error:n?decodeURIComponent(n):void 0,theme:y}))}),a.get("/",A(),e=>{const t=e.req.query("view"),r=t==="recent"?"recent":t==="default"?"default":O.defaultView??"default",n=b(),s=I(),o=h?M(n,s):[];return e.html(Ke(n,{view:r,accessMap:s,commandbarEnabled:h,commandbarSessions:o,agentsEnabled:q,theme:y}))}),a.get("/s/:session",A(),async e=>{const t=decodeURIComponent(e.req.param("session"));await dt(t),te(t);const r=b(),n=I(),s=h?M(r,n):[];return e.html(Qe(t,ae,{commandbarEnabled:h,commandbarSessions:s,quickCommands:h?re():[],agentsEnabled:q,terminal:be,theme:y,renderer:se}))}),a.get("/notes",A(),e=>{const t=h?M(b(),I()):[];return e.html(Xe(k.data.notes,y,h,t,q))}),a.get("/notes/:session",A(),e=>{const t=decodeURIComponent(e.req.param("session")),r=h?M(b(),I()):[];return e.html(Ye(t,y,h,r))}),a.get("/schedule",A(),e=>{const t=h?M(b(),I()):[];return e.html(tt(_.list(),_.listTriggered(),y,Re,h,t,q))}),a.get("/agents",A(),e=>{if(!q)return e.redirect("/settings",303);const t=h?M(b(),I()):[];return e.html(rt(y,h,t))}),a.get("/history",A(),e=>{const t=b(),r=h?M(t,I()):[],n=new Set(t.map(s=>s.name));return e.html(nt(ct(),y,h,r,q,n))}),a.get("/quick-commands",A(),e=>{const t=h?M(b(),I()):[];return e.html(ot(re(),y,h,t,q))}),a.post("/api/history/clear",d(),async e=>(await ut(),e.json({ok:!0}))),a.get("/api/quick-commands",d(),e=>e.json(re())),a.post("/api/quick-commands",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Wt(t);return"error"in r?e.json({error:r.error},400):e.json(r,201)}),a.patch("/api/quick-commands/:id",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Ot(e.req.param("id"),t);return"error"in r?e.json({error:r.error},r.status):e.json(r)}),a.delete("/api/quick-commands/:id",d(),async e=>await Ht(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),a.get("/api/agents",d(),async e=>{if(!q)return e.json({error:"agents page disabled"},403);const t=Me?nr():await tr();return e.json(t)}),a.post("/api/auth/password",async e=>{const t=z(e),r=R.check(t);if(!r.allowed)return u("rate_limited",{ip:t,retryAfterMs:r.retryAfterMs,permanentLock:r.permanentLock}),r.permanentLock?e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403):e.json({error:"Too many attempts",retryAfterMs:r.retryAfterMs},429);let n;try{n=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const s=typeof n.password=="string"?n.password:"";if(!l.passwordHash){if(!l.security.allowRemoteSetup&&!Se(t))return u("setup_rejected_remote",{ip:t}),e.json({error:"First-run setup must be performed from localhost"},403);if(U)return e.json({error:"Password setup in progress"},409);const f=ye(s);if(f)return e.json({error:f},400);U=!0;try{l.passwordHash=await fe(s),pe(l),u("password_set",{ip:t})}finally{U=!1}const{plaintext:T}=p.createAccessToken("setup",l.security.tokenTtlDays);return Ie(e,T),e.json({ok:!0,token:T,setupMode:!0})}if(!await he(s,l.passwordHash)){const f=R.recordFailure(t);return u("auth_failed",{ip:t,method:"password",failures:f.failures,permanentLock:f.permanentLock}),f.permanentLock?(p.revokeAll(),u("permanent_lock",{ip:t,failures:f.failures}),e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403)):f.allowed?e.json({error:"Incorrect password"},401):e.json({error:"Too many attempts",retryAfterMs:f.retryAfterMs},429)}R.recordSuccess(t);const i=`browser-${t}`,{plaintext:w}=p.createAccessToken(i,l.security.tokenTtlDays);return Ie(e,w),u("auth_success",{ip:t,method:"password",tokenName:i}),e.json({ok:!0,token:w})}),a.post("/api/auth/token",d(),async e=>{const t=z(e);let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const n=typeof r.name=="string"?r.name:`api-${t}`,s=typeof r.ttlDays=="number"?r.ttlDays:l.security.tokenTtlDays,{stored:o,plaintext:i}=p.createAccessToken(n,s);return u("token_created",{ip:t,name:o.name,tokenId:o.id}),e.json({id:o.id,name:o.name,token:i,expiresAt:o.expiresAt})}),a.get("/api/auth/tokens",d(),e=>e.json(p.list().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,lastUsedAt:t.lastUsedAt,expiresAt:t.expiresAt})))),a.delete("/api/auth/tokens/:id",d(),e=>p.revoke(e.req.param("id"))?(u("token_revoked",{ip:z(e),tokenId:e.req.param("id")}),e.json({ok:!0})):e.json({error:"not found"},404)),a.post("/api/auth/logout",d(),e=>(ir(e),e.json({ok:!0}))),a.get("/settings",A(),async e=>{const t=await ee(),r=t.terminalRenderer??"xterm";return e.html(Ze({settings:t,renderer:se,rendererOverridden:se!==r,theme:y,plugins:t.plugins??[],saved:e.req.query("saved")==="1",error:e.req.query("error")?decodeURIComponent(e.req.query("error")):void 0}))}),a.post("/settings",d(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=await ee(),n=t.terminalRenderer==="ghostty"?"ghostty":"xterm",s=t.defaultView==="recent"?"recent":"default",o=Ae(typeof t.scheduleHistoryDays=="string"?Number(t.scheduleHistoryDays):void 0);return await Tt({...r,commandbar:t.commandbar!==void 0,agents:t.agents!==void 0,agentsBackgroundWatch:t.agentsBackgroundWatch!==void 0,terminalRenderer:n,defaultView:s,scheduleHistoryDays:o}),e.redirect("/settings?saved=1",303)}),a.post("/settings/plugins",d(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=t.action,n=typeof t.pkg=="string"?t.pkg.trim():"";if(!n)return e.redirect("/settings?error="+encodeURIComponent("missing package name"),303);const s=r==="remove"?await Ct(n):r==="add"?await qt(n):{ok:!1,output:"unknown action"};return s.ok?e.redirect("/settings?saved=1",303):e.redirect("/settings?error="+encodeURIComponent(s.output.slice(0,800)),303)}),a.get("/settings/theme",A(),e=>e.html(et({theme:y,saved:e.req.query("saved")==="1"}))),a.post("/settings/theme",d(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings/theme?error=1",303)}const r=t.template;return typeof r!="string"||!je(r)?e.redirect("/settings/theme",303):(y=await ke(r),e.redirect("/settings/theme?saved=1",303))});const hr={vscode:"VS Code",ghostty:"Ghostty","warm-clay":"Warm Clay","dark-cove":"Dark Cove"};a.get("/api/theme",d(),e=>e.json({active:y.template,templates:At.map(t=>({id:t,name:hr[t]}))})),a.post("/api/theme",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=t.template;return typeof r!="string"||!je(r)?e.json({error:"invalid theme template"},400):(y=await ke(r),e.json({ok:!0,active:y.template}))}),a.get("/api/sessions",d(),e=>h?e.json(M(b(),I())):e.json({error:"commandbar disabled"},404)),a.post("/api/sessions/new",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name is required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(r))return e.json({error:"name contains invalid characters"},400);const n=typeof t.dir=="string"&&t.dir.trim()?t.dir.trim():void 0;if(b().some(o=>o.name===r))return e.json({error:"session already exists"},409);try{return Yt(r,n),e.json({ok:!0})}catch(o){const i=o instanceof V?o.message:"failed to create session";return e.json({error:i},500)}}),a.get("/api/fs/list",d(),e=>{const t=process.env.HOME??"/";let r=e.req.query("path")??t;r.startsWith("~")&&(r=t+r.slice(1)),r.startsWith("/")||(r=v.join(t,r));let n=r,s="",o=r.endsWith("/");if(!o)try{o=Z(r).isDirectory()}catch{}o||(n=v.dirname(r),s=v.basename(r).toLowerCase());try{const i=le(n),w=[];for(const f of i)if(!f.startsWith(".")&&!(s&&!f.toLowerCase().startsWith(s))){try{const T=v.join(n,f);Z(T).isDirectory()&&w.push(T)}catch{}if(w.length>=50)break}return e.json({dirs:w})}catch{return e.json({dirs:[]})}});function ie(e){return{...Ut(b(),I(),_t()),currentSession:e??null}}function Pe(e){const t=typeof e.sessionName=="string"?e.sessionName.trim():"";if(!t)return{error:"sessionName is required"};if(e.windowIndex===void 0)return{sessionName:t};const r=e.windowIndex;return typeof r!="number"||!Number.isInteger(r)||r<0?{error:"windowIndex must be a non-negative integer"}:{sessionName:t,windowIndex:r}}a.get("/api/sidebar/sessions",d(),e=>{const t=e.req.query("currentSession");return e.json(ie(typeof t=="string"&&t?t:void 0))}),a.get("/api/sidebar/session-windows/:session",d(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.json(ve(t))}),a.post("/api/pinned-views",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=Pe(t);return"error"in r?e.json({error:r.error},400):(await Mt(r.sessionName,r.windowIndex),e.json(ie()))}),a.delete("/api/pinned-views",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=Pe(t);return"error"in r?e.json({error:r.error},400):(await Rt(r.sessionName,r.windowIndex),e.json(ie()))}),a.get("/api/notes",d(),e=>{const t=[...k.data.notes].sort((r,n)=>(n.updatedAt??0)-(r.updatedAt??0));return e.json(t)}),a.get("/api/notes/:scope",d(),e=>{const t=decodeURIComponent(e.req.param("scope")),r=k.data.notes.find(n=>n.scope===t);return r?e.json(r):e.json(null,404)}),a.put("/api/notes/:scope",d(),async e=>{const t=decodeURIComponent(e.req.param("scope"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}if(typeof r.content!="string")return e.json({error:"content must be string"},400);const n={scope:t,content:r.content,updatedAt:Date.now()},s=k.data.notes.findIndex(o=>o.scope===t);return s>=0?k.data.notes[s]=n:k.data.notes.push(n),await k.write(),e.json({ok:!0})}),a.post("/api/session/:session/upload",d(),async e=>{const t=decodeURIComponent(e.req.param("session"));if(!b().some(o=>o.name===t))return e.json({error:"session not found"},404);let n;try{n=await e.req.parseBody()}catch{return e.json({error:"invalid multipart body"},400)}const s=n.file;if(!(s instanceof File))return e.json({error:"missing file field"},400);try{const o=await s.arrayBuffer(),i=Buffer.from(o),{path:w}=await zt(i,s.type||void 0,s.name||void 0);return e.json({path:w})}catch(o){return o instanceof Gt?e.json({error:o.message},o.status):(console.error("[upload]",o),e.json({error:"upload failed"},500))}}),a.get("/api/session/:session/windows",d(),e=>{const t=decodeURIComponent(e.req.param("session")),r=new Map(Et(t).map(o=>[o.windowIndex,o.label])),n=new Map(ve(t).map(o=>[o.index,o])),s=Kt(t).map(o=>({...o,label:r.get(o.index)??null,worktree:n.get(o.index)?.worktree??!1}));return e.json(s)}),a.post("/api/session/:session/window-label",d(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);const s=typeof r.label=="string"?r.label:"",o=await Pt(t,n,s);return e.json(o)}),a.post("/api/session/:session/select-window",d(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);try{return Qt(t,n),K(t),e.json({ok:!0})}catch(s){return s instanceof V?e.json({error:s.message},s.status):(console.error("[select-window]",s),e.json({error:"select-window failed"},500))}}),a.post("/api/session/:session/rename-window",d(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);if(typeof r.name!="string"||!r.name.trim())return e.json({error:"name is required"},400);try{return Xt(t,n,r.name),te(t),e.json({ok:!0})}catch(s){return s instanceof V?e.json({error:s.message},s.status):(console.error("[rename-window]",s),e.json({error:"rename-window failed"},500))}}),a.post("/api/session/:session/new-window",d(),e=>{const t=decodeURIComponent(e.req.param("session"));try{return Jt(t),K(t),te(t),e.json({ok:!0})}catch(r){return r instanceof V?e.json({error:r.message},r.status):(console.error("[new-window]",r),e.json({error:"new-window failed"},500))}}),a.get("/api/schedule",d(),e=>e.json(_.list(e.req.query("session")))),a.post("/api/schedule",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=we(t);if(r)return e.json({error:r},400);if(!ht(t))return e.json({error:"invalid body"},400);const n=await _.create(t);return e.json({id:n.id,fireAt:n.fireAt})}),a.delete("/api/schedule/:id",d(),async e=>await _.delete(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),a.patch("/api/schedule/:id",d(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=we(t);if(r)return e.json({error:r},400);if(!yt(t))return e.json({error:"invalid body"},400);const n=await _.reschedule(e.req.param("id"),t.delayMs);return n?e.json({id:n.id,fireAt:n.fireAt}):e.json({error:"not found"},404)});const yr=parseInt(process.env.PORT||"21000",10),wr=Ve({fetch:a.fetch,port:yr},e=>{console.log(`tmux-web running at http://localhost:${e.port}`)});Me&&rr();const de=new Ge({noServer:!0});function Le(e,t,r){e.write(`HTTP/1.1 ${t} ${r}\r
|
|
6
|
+
</svg>`,Le=e=>e.body(Ar,200,{"Content-Type":"image/svg+xml; charset=utf-8","Cache-Control":"public, max-age=86400"});o.get("/favicon.svg",Le),o.get("/favicon.ico",Le),o.get("/login",e=>{const r=e.req.query("setup")==="1"?!0:!l.passwordHash,n=e.req.query("error");return e.html(rt({setupMode:r,error:n?decodeURIComponent(n):void 0,theme:y}))}),o.get("/",I(),e=>{const t=p?C(v(),q()):[],r=je();return e.html(mt({theme:y,commandbarEnabled:p,commandbarSessions:t,agentsEnabled:_,fsRoots:r,terminalCfg:B,renderer:Z,scrollback:B.initialLines+2*B.historyChunk}))}),o.get("/s/:session",I(),async e=>{const t=decodeURIComponent(e.req.param("session"));await gt(t),oe(t);const r=v(),n=q(),a=p?C(r,n):[];return e.html(tt(t,de,{commandbarEnabled:p,commandbarSessions:a,quickCommands:p?se():[],agentsEnabled:_,terminal:B,theme:y,renderer:Z}))}),o.get("/notes",I(),e=>{const t=p?C(v(),q()):[];return e.html(nt(j.data.notes,y,p,t,_))}),o.get("/notes/:session",I(),e=>{const t=decodeURIComponent(e.req.param("session")),r=p?C(v(),q()):[];return e.html(ot(t,y,p,r))}),o.get("/schedule",I(),e=>{const t=p?C(v(),q()):[];return e.html(it(M.list(),M.listTriggered(),y,Oe,p,t,_))}),o.get("/agents",I(),e=>{if(!_)return e.redirect("/settings",303);const t=p?C(v(),q()):[];return e.html(dt(y,p,t))}),o.get("/history",I(),e=>{const t=v(),r=p?C(t,q()):[],n=new Set(t.map(a=>a.name));return e.html(ut(jt(),y,p,r,_,n))}),o.get("/quick-commands",I(),e=>{const t=p?C(v(),q()):[];return e.html(ct(se(),y,p,t,_))}),o.get("/files",I(),e=>{const t=p?C(v(),q()):[],r=je();return e.html(lt(y,p,t,_,r))}),o.post("/api/history/clear",i(),async e=>(await kt(),e.json({ok:!0}))),o.get("/api/quick-commands",i(),e=>e.json(se())),o.post("/api/quick-commands",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Kt(t);return"error"in r?e.json({error:r.error},400):e.json(r,201)}),o.patch("/api/quick-commands/:id",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=await Xt(e.req.param("id"),t);return"error"in r?e.json({error:r.error},r.status):e.json(r)}),o.delete("/api/quick-commands/:id",i(),async e=>await Qt(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),o.get("/api/agents",i(),async e=>{if(!_)return e.json({error:"agents page disabled"},403);const t=Me?hr():await pr();return e.json(t)}),o.post("/api/auth/password",async e=>{const t=J(e),r=E.check(t);if(!r.allowed)return c("rate_limited",{ip:t,retryAfterMs:r.retryAfterMs,permanentLock:r.permanentLock}),r.permanentLock?e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403):e.json({error:"Too many attempts",retryAfterMs:r.retryAfterMs},429);let n;try{n=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const a=typeof n.password=="string"?n.password:"";if(!l.passwordHash){if(!l.security.allowRemoteSetup&&!qe(t))return c("setup_rejected_remote",{ip:t}),e.json({error:"First-run setup must be performed from localhost"},403);if(U)return e.json({error:"Password setup in progress"},409);const f=ge(a);if(f)return e.json({error:f},400);U=!0;try{l.passwordHash=await ye(a),he(l),c("password_set",{ip:t})}finally{U=!1}const{plaintext:b}=h.createAccessToken("setup",l.security.tokenTtlDays);return _e(e,b),e.json({ok:!0,token:b,setupMode:!0})}if(!await we(a,l.passwordHash)){const f=E.recordFailure(t);return c("auth_failed",{ip:t,method:"password",failures:f.failures,permanentLock:f.permanentLock}),f.permanentLock?(h.revokeAll(),c("permanent_lock",{ip:t,failures:f.failures}),e.json({error:"Server locked after too many failed attempts",permanentLock:!0},403)):f.allowed?e.json({error:"Incorrect password"},401):e.json({error:"Too many attempts",retryAfterMs:f.retryAfterMs},429)}E.recordSuccess(t);const d=`browser-${t}`,{plaintext:T}=h.createAccessToken(d,l.security.tokenTtlDays);return _e(e,T),c("auth_success",{ip:t,method:"password",tokenName:d}),e.json({ok:!0,token:T})}),o.post("/api/auth/token",i(),async e=>{const t=J(e);let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const n=typeof r.name=="string"?r.name:`api-${t}`,a=typeof r.ttlDays=="number"?r.ttlDays:l.security.tokenTtlDays,{stored:s,plaintext:d}=h.createAccessToken(n,a);return c("token_created",{ip:t,name:s.name,tokenId:s.id}),e.json({id:s.id,name:s.name,token:d,expiresAt:s.expiresAt})}),o.get("/api/auth/tokens",i(),e=>e.json(h.list().map(t=>({id:t.id,name:t.name,createdAt:t.createdAt,lastUsedAt:t.lastUsedAt,expiresAt:t.expiresAt})))),o.delete("/api/auth/tokens/:id",i(),e=>h.revoke(e.req.param("id"))?(c("token_revoked",{ip:J(e),tokenId:e.req.param("id")}),e.json({ok:!0})):e.json({error:"not found"},404)),o.post("/api/auth/logout",i(),e=>(jr(e),e.json({ok:!0}))),o.get("/settings",I(),async e=>{const t=await ne(),r=t.terminalRenderer??"xterm";return e.html(st({settings:t,renderer:Z,rendererOverridden:Z!==r,theme:y,plugins:t.plugins??[],saved:e.req.query("saved")==="1",error:e.req.query("error")?decodeURIComponent(e.req.query("error")):void 0}))}),o.post("/settings",i(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=await ne(),n=t.terminalRenderer==="ghostty"?"ghostty":"xterm",a=t.defaultView==="recent"?"recent":"default",s=Ce(typeof t.scheduleHistoryDays=="string"?Number(t.scheduleHistoryDays):void 0);return await Pt({...r,commandbar:t.commandbar!==void 0,agents:t.agents!==void 0,agentsBackgroundWatch:t.agentsBackgroundWatch!==void 0,terminalRenderer:n,defaultView:a,scheduleHistoryDays:s}),e.redirect("/settings?saved=1",303)}),o.post("/settings/plugins",i(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings?error="+encodeURIComponent("invalid form body"),303)}const r=t.action,n=typeof t.pkg=="string"?t.pkg.trim():"";if(!n)return e.redirect("/settings?error="+encodeURIComponent("missing package name"),303);const a=r==="remove"?await Ut(n):r==="add"?await Dt(n):{ok:!1,output:"unknown action"};return a.ok?e.redirect("/settings?saved=1",303):e.redirect("/settings?error="+encodeURIComponent(a.output.slice(0,800)),303)}),o.get("/settings/theme",I(),e=>e.html(at({theme:y,saved:e.req.query("saved")==="1"}))),o.post("/settings/theme",i(),async e=>{let t;try{t=await e.req.parseBody()}catch{return e.redirect("/settings/theme?error=1",303)}const r=t.template;return typeof r!="string"||!be(r)?e.redirect("/settings/theme",303):(y=await Te(r),e.redirect("/settings/theme?saved=1",303))});const qr={vscode:"VS Code",ghostty:"Ghostty","warm-clay":"Warm Clay","dark-cove":"Dark Cove"};o.get("/api/theme",i(),e=>e.json({active:y.template,templates:Nt.map(t=>({id:t,name:qr[t]}))})),o.post("/api/theme",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=t.template;return typeof r!="string"||!be(r)?e.json({error:"invalid theme template"},400):(y=await Te(r),e.json({ok:!0,active:y.template}))}),o.get("/api/sessions",i(),e=>p?e.json(C(v(),q())):e.json({error:"commandbar disabled"},404)),o.post("/api/sessions/new",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=typeof t.name=="string"?t.name.trim():"";if(!r)return e.json({error:"name is required"},400);if(!/^[a-zA-Z0-9_\-. ]+$/.test(r))return e.json({error:"name contains invalid characters"},400);const n=typeof t.dir=="string"&&t.dir.trim()?t.dir.trim():void 0;if(v().some(s=>s.name===r))return e.json({error:"session already exists"},409);try{return dr(r,n),e.json({ok:!0})}catch(s){const d=s instanceof X?s.message:"failed to create session";return e.json({error:d},500)}}),o.post("/api/sessions/rename",i(),async e=>{try{const{oldName:t,newName:r}=await e.req.json();return!t||!r?e.json({error:"oldName and newName required"},400):/^[a-zA-Z0-9_\-. ]+$/.test(r)?(ur(t,r),e.json({ok:!0})):e.json({error:"invalid characters in name"},400)}catch{return e.json({error:"rename failed"},500)}}),o.post("/api/sessions/kill",i(),async e=>{try{const{name:t}=await e.req.json();return t?(cr(t),e.json({ok:!0})):e.json({error:"name required"},400)}catch{return e.json({error:"kill failed"},500)}}),o.get("/api/fs/list",i(),e=>{const t=process.env.HOME??"/";let r=e.req.query("path")??t;r.startsWith("~")&&(r=t+r.slice(1)),r.startsWith("/")||(r=g.join(t,r));const n=e.req.query("recursive")==="true";let a=r,s="",d=r.endsWith("/");if(!d)try{d=D(r).isDirectory()}catch{}d||(a=g.dirname(r),s=g.basename(r).toLowerCase());try{const T=pe(a),f=[],b=[];for(const O of T)if(!O.startsWith(".")&&!(s&&!O.toLowerCase().startsWith(s))){try{const S=g.join(a,O);D(S).isDirectory()?(f.push(S),n&&wt(S,f,b,0)):b.push(S)}catch{}if(f.length+b.length>=5e3)break}return e.json({dirs:f,files:b})}catch{return e.json({dirs:[],files:[]})}}),o.get("/api/file",i(),e=>{try{const t=e.req.query("path");if(!t)return e.json({error:"path is required"},400);const r=Q(t);if(!D(r).isFile())return e.json({error:"not a file"},400);const n=D(r).size;if(n>ke)return e.json({error:"file too large",size:n,maxBytes:ke},413);const a=Ve(r,"utf-8");return e.json({path:r,content:a,size:n})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):t.code==="ENOENT"?e.json({error:"not found"},404):e.json({error:"internal error"},500)}}),o.put("/api/file",i(),async e=>{try{const t=await e.req.json();if(!t.path||typeof t.content!="string")return e.json({error:"path and content are required"},400);const r=Q(t.path);return yt(r,t.content),e.json({ok:!0})}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"write failed"},500)}}),o.post("/api/file/delete",i(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=Q(t.path);return D(r).isFile()?(Ge(r),e.json({ok:!0})):e.json({error:"not a file"},400)}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):t.code==="ENOENT"?e.json({error:"not found"},404):e.json({error:"delete failed"},500)}}),o.post("/api/file/touch",i(),async e=>{try{const t=await e.req.json();if(!t.path)return e.json({error:"path is required"},400);const r=Q(t.path);return K(r)?e.json({error:"file already exists"},409):(Be(g.dirname(r),{recursive:!0}),ze(r,"","utf-8"),e.json({ok:!0,path:r}))}catch(t){return t.message==="FS_ROOTS_NOT_CONFIGURED"?e.json({error:"file access not configured"},403):t.message==="PATH_NOT_ALLOWED"?e.json({error:"path not allowed"},403):e.json({error:"touch failed"},500)}});function ue(e){return{...zt(v(),q(),Ht()),currentSession:e??null}}function Ne(e){const t=typeof e.sessionName=="string"?e.sessionName.trim():"";if(!t)return{error:"sessionName is required"};if(e.windowIndex===void 0)return{sessionName:t};const r=e.windowIndex;return typeof r!="number"||!Number.isInteger(r)||r<0?{error:"windowIndex must be a non-negative integer"}:{sessionName:t,windowIndex:r}}o.get("/api/sidebar/sessions",i(),e=>{const t=e.req.query("currentSession");return e.json(ue(typeof t=="string"&&t?t:void 0))}),o.get("/api/sidebar/session-windows/:session",i(),e=>{const t=decodeURIComponent(e.req.param("session"));return e.json(Se(t))}),o.post("/api/pinned-views",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=Ne(t);return"error"in r?e.json({error:r.error},400):(await Wt(r.sessionName,r.windowIndex),e.json(ue()))}),o.delete("/api/pinned-views",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=Ne(t);return"error"in r?e.json({error:r.error},400):(await Ft(r.sessionName,r.windowIndex),e.json(ue()))}),o.get("/api/notes",i(),e=>{const t=[...j.data.notes].sort((r,n)=>(n.updatedAt??0)-(r.updatedAt??0));return e.json(t)}),o.get("/api/notes/:scope",i(),e=>{const t=decodeURIComponent(e.req.param("scope")),r=j.data.notes.find(n=>n.scope===t);return r?e.json(r):e.json(null,404)}),o.put("/api/notes/:scope",i(),async e=>{const t=decodeURIComponent(e.req.param("scope"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}if(typeof r.content!="string")return e.json({error:"content must be string"},400);const n={scope:t,content:r.content,updatedAt:Date.now()},a=j.data.notes.findIndex(s=>s.scope===t);return a>=0?j.data.notes[a]=n:j.data.notes.push(n),await j.write(),e.json({ok:!0})}),o.post("/api/session/:session/upload",i(),async e=>{const t=decodeURIComponent(e.req.param("session"));if(!v().some(s=>s.name===t))return e.json({error:"session not found"},404);let n;try{n=await e.req.parseBody()}catch{return e.json({error:"invalid multipart body"},400)}const a=n.file;if(!(a instanceof File))return e.json({error:"missing file field"},400);try{const s=await a.arrayBuffer(),d=Buffer.from(s),{path:T}=await nr(d,a.type||void 0,a.name||void 0);return e.json({path:T})}catch(s){return s instanceof rr?e.json({error:s.message},s.status):(console.error("[upload]",s),e.json({error:"upload failed"},500))}}),o.get("/api/session/:session/windows",i(),e=>{const t=decodeURIComponent(e.req.param("session")),r=new Map($t(t).map(s=>[s.windowIndex,s.label])),n=new Map(Se(t).map(s=>[s.index,s])),a=or(t).map(s=>({...s,label:r.get(s.index)??null,worktree:n.get(s.index)?.worktree??!1}));return e.json(a)}),o.post("/api/session/:session/window-label",i(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);const a=typeof r.label=="string"?r.label:"",s=await Bt(t,n,a);return e.json(s)}),o.post("/api/session/:session/select-window",i(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);try{return sr(t,n),ee(t),e.json({ok:!0})}catch(a){return a instanceof X?e.json({error:a.message},a.status):(console.error("[select-window]",a),e.json({error:"select-window failed"},500))}}),o.post("/api/session/:session/rename-window",i(),async e=>{const t=decodeURIComponent(e.req.param("session"));let r;try{r=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const{windowIndex:n}=r;if(typeof n!="number"||!Number.isInteger(n)||n<0)return e.json({error:"windowIndex must be a non-negative integer"},400);if(typeof r.name!="string"||!r.name.trim())return e.json({error:"name is required"},400);try{return ir(t,n,r.name),oe(t),e.json({ok:!0})}catch(a){return a instanceof X?e.json({error:a.message},a.status):(console.error("[rename-window]",a),e.json({error:"rename-window failed"},500))}}),o.post("/api/session/:session/new-window",i(),e=>{const t=decodeURIComponent(e.req.param("session"));try{return ar(t),ee(t),oe(t),e.json({ok:!0})}catch(r){return r instanceof X?e.json({error:r.message},r.status):(console.error("[new-window]",r),e.json({error:"new-window failed"},500))}}),o.get("/api/schedule",i(),e=>e.json(M.list(e.req.query("session")))),o.post("/api/schedule",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=ve(t);if(r)return e.json({error:r},400);if(!St(t))return e.json({error:"invalid body"},400);const n=await M.create(t);return e.json({id:n.id,fireAt:n.fireAt})}),o.delete("/api/schedule/:id",i(),async e=>await M.delete(e.req.param("id"))?e.json({ok:!0}):e.json({error:"not found"},404)),o.patch("/api/schedule/:id",i(),async e=>{let t;try{t=await e.req.json()}catch{return e.json({error:"invalid json"},400)}const r=ve(t);if(r)return e.json({error:r},400);if(!At(t))return e.json({error:"invalid body"},400);const n=await M.reschedule(e.req.param("id"),t.delayMs);return n?e.json({id:n.id,fireAt:n.fireAt}):e.json({error:"not found"},404)});const Ir=parseInt(process.env.PORT||"21000",10),_r=Je({fetch:o.fetch,port:Ir},e=>{console.log(`tmux-web running at http://localhost:${e.port}`)});Me&&fr();const ce=new Ze({noServer:!0});function De(e,t,r){e.write(`HTTP/1.1 ${t} ${r}\r
|
|
7
7
|
\r
|
|
8
|
-
`),e.destroy()}
|
|
9
|
-
\x1B[31mFailed to attach to tmux session "${r}": ${
|
|
10
|
-
`}),
|
|
11
|
-
\x1B[2m--- tmux exited (code ${
|
|
12
|
-
`}),
|
|
8
|
+
`),e.destroy()}_r.on("upgrade",(e,t,r)=>{const n=new URL(e.url||"/",`http://${e.headers.host}`),a=n.pathname.match(/^\/ws\/(.+)$/);if(!a){t.destroy();return}const s=gr(e),d=e.headers.origin,T=l.security.allowedOrigins;if(d&&!(d===`http://${e.headers.host}`||d===`https://${e.headers.host}`)&&(T.length===0||!T.includes(d))){c("ws_rejected_origin",{ip:s,origin:d}),De(t,403,"Origin not allowed");return}const f=vr(s);if(f>=l.security.maxConnectionsPerIp){c("ws_rejected_per_ip_cap",{ip:s,liveFromIp:f}),De(t,429,"Too many connections");return}const b=Ie(e)||n.searchParams.get("token")||"",O=b?h.validateToken(b):null;ce.handleUpgrade(e,t,r,S=>{ce.emit("connection",S,e,decodeURIComponent(a[1]),s,O)})}),ce.on("connection",(e,t,r,n,a)=>{const s={ws:e,ip:n,authenticated:!1,authTimeout:null};W.set(e,s),c("ws_connected",{ip:n});let d=null;const{initialLines:T,historyChunk:f,syncIdleMs:b,syncMaxMs:O}=B;let S=!0,N=null,G=null,H=r,P=null;const z=()=>{N&&(clearTimeout(N),N=null),G&&(clearTimeout(G),G=null)},le=()=>{if(S){z(),S=!1;try{H=Yt(r)}catch{H=r}if(ee(r),!er(H))try{const w=Jt(H,T);x(e,{type:"snapshot",data:Ae(w),lines:T})}catch{}}},We=()=>{S&&(N&&clearTimeout(N),N=setTimeout(le,b))};function me(){try{d=et.spawn("tmux",["attach-session","-t",r],{name:"xterm-256color",cols:80,rows:24,cwd:process.env.HOME||"/",env:process.env})}catch(u){x(e,{type:"data",data:`\r
|
|
9
|
+
\x1B[31mFailed to attach to tmux session "${r}": ${u.message}\x1B[0m\r
|
|
10
|
+
`}),ie(e,1011,"pty spawn failed");return}F.add(d);let w=-1,m="";P=Vt(r,({activeIndex:u,windows:k})=>{x(e,{type:"window_changed",activeIndex:u,windows:k});const L=k.map(A=>A.index).join(","),$=u!==w||L!==m;w=u,m=L,$&&(ee(r),yr())}),G=setTimeout(le,O),d.onData(u=>{if(S){We();return}x(e,{type:"data",data:u})}),d.onExit(({exitCode:u})=>{z(),d&&F.delete(d),d=null,x(e,{type:"data",data:`\r
|
|
11
|
+
\x1B[2m--- tmux exited (code ${u}) ---\x1B[0m\r
|
|
12
|
+
`}),ie(e,1e3,"pty exited")})}function te(w,m){s.authenticated=!0,s.authTimeout&&(clearTimeout(s.authTimeout),s.authTimeout=null),c("auth_success",{ip:n,method:w,tokenName:m}),x(e,{type:"auth.ok",setupMode:!l.passwordHash}),me()}function R(w,m={}){x(e,{type:"auth.failed",message:w,...m})}async function Fe(w){let m;try{m=JSON.parse(w)}catch{return!1}if(m.type==="auth.token"&&typeof m.token=="string"){const u=h.validateToken(m.token);if(!u){c("token_auth_failed",{ip:n});const k=E.recordFailure(n);return k.permanentLock&&(h.revokeAll(),c("permanent_lock",{ip:n,failures:k.failures})),R("Invalid or expired token",{permanentLock:k.permanentLock,retryAfterMs:k.retryAfterMs}),!0}return h.touch(u.tokenHash),te("token",u.name),!0}if(m.type==="auth"&&typeof m.password=="string"){const u=E.check(n);if(!u.allowed)return c("rate_limited",{ip:n,retryAfterMs:u.retryAfterMs,permanentLock:u.permanentLock}),R("Too many attempts",{permanentLock:u.permanentLock,retryAfterMs:u.retryAfterMs}),!0;if(!l.passwordHash){if(!l.security.allowRemoteSetup&&!qe(n))return c("setup_rejected_remote",{ip:n}),R("First-run setup must be performed from localhost"),!0;if(U)return R("Password setup in progress"),!0;const A=ge(m.password);if(A)return R(A),!0;U=!0;try{l.passwordHash=await ye(m.password),he(l),c("password_set",{ip:n})}finally{U=!1}const{stored:re,plaintext:He}=h.createAccessToken("setup",l.security.tokenTtlDays);return x(e,{type:"auth.ok",setupMode:!0,token:He}),c("token_created",{ip:n,name:re.name,tokenId:re.id}),c("auth_success",{ip:n,method:"password",tokenName:re.name}),me(),!0}if(!l.passwordHash)return R("Server not configured"),!0;if(!await we(m.password,l.passwordHash)){const A=E.recordFailure(n);return c("auth_failed",{ip:n,method:"password",failures:A.failures,permanentLock:A.permanentLock}),A.permanentLock&&(h.revokeAll(),c("permanent_lock",{ip:n,failures:A.failures})),R("Incorrect password",{permanentLock:A.permanentLock,retryAfterMs:A.retryAfterMs}),!0}E.recordSuccess(n);const L=`ws-${n}`,{plaintext:$}=h.createAccessToken(L,l.security.tokenTtlDays);return te("password",L),x(e,{type:"auth.ok",setupMode:!1,token:$}),!0}return!1}a?(h.touch(a.tokenHash),te("token",a.name)):(x(e,{type:"auth.required",setupMode:!l.passwordHash}),s.authTimeout=setTimeout(()=>{s.authenticated||(c("auth_timeout",{ip:n}),R("Authentication timeout"),ie(e,4e3,"Auth timeout"))},l.security.authTimeoutMs)),e.on("message",async w=>{const m=typeof w=="string"?w:w.toString("utf-8");if(!s.authenticated){if(m.length>1e6){x(e,{type:"auth.failed",message:"Message too large"});return}await Fe(m)||R("Authentication required");return}if(!d)return;try{if(qt(m,d))return}catch{return}let u;try{u=JSON.parse(m)}catch{return}if(u.type==="load_history"&&typeof u.before=="number"){const k=Math.max(0,Math.floor(u.before));try{const{data:L,lines:$}=Zt(H,k,f);x(e,{type:"history",data:Ae(L),before:k,lines:$})}catch{x(e,{type:"history",data:"",before:k,lines:0})}}}),e.on("close",()=>{s.authTimeout&&clearTimeout(s.authTimeout),W.delete(e),c("ws_disconnected",{ip:n}),z(),P&&(P(),P=null),d&&(F.delete(d),d.kill(),d=null)}),e.on("error",()=>{s.authTimeout&&clearTimeout(s.authTimeout),W.delete(e),z(),P&&(P(),P=null),d&&(F.delete(d),d.kill(),d=null)})});function Ue(){M.cleanup(),Gt();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 F)try{e.kill()}catch{}F.clear();for(const e of Ee)try{e.kill("SIGTERM")}catch{}E.dispose(),process.exit(0)}process.on("SIGINT",Ue),process.on("SIGTERM",Ue),setInterval(()=>{h.purgeExpired()},3600*1e3).unref();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"node:path";import{readdirSync as u,statSync as l}from"node:fs";const O=1048576;function a(){const o=process.env.TMUX_WEB_FS_ROOTS;return o?o.split(":").map(r=>(r=r.trim(),r.startsWith("~")&&(r=e.join(process.env.HOME||"/",r.slice(1))),e.resolve(r))):[]}function E(o){const r=a();if(r.length===0)throw new Error("FS_ROOTS_NOT_CONFIGURED");let t=o;if(t.startsWith("~")&&(t=e.join(process.env.HOME||"/",t.slice(1))),e.isAbsolute(t)||(t=e.resolve(t)),t=e.normalize(t),!r.some(s=>t===s||t.startsWith(s+e.sep)))throw new Error("PATH_NOT_ALLOWED");return t}function h(o,r,t,i,s=5e3){if(i>8)return;let c;try{c=u(o)}catch{return}for(const f of c){if(f.startsWith("."))continue;const n=e.join(o,f);try{l(n).isDirectory()?(r.push(n),h(n,r,t,i+1,s)):t.push(n)}catch{}if(r.length+t.length>=s)return}}export{O as MAX_FILE_BYTES,E as resolveFsPath,a as resolveFsRoots,h as walkRecursive};
|
package/dist/lib/icons.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const a={menu:"M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z",close:"M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7a1 1 0 1 0-1.41 1.41L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 1 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z",search:"M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z",refresh:"M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 8 8h-2a6 6 0 1 1-6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z",add:"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z",edit:"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zm13.66-12.83a1 1 0 0 0 0-1.41l-1.34-1.34a1 1 0 0 0-1.41 0l-1.05 1.05 2.75 2.75 1.05-1.05z",delete:"M15 4V3H9v1H4v2h1.5l.81 13.05A2 2 0 0 0 8.3 21h7.4a2 2 0 0 0 1.99-1.95L18.5 6H20V4h-5zm-5.5 2h5l-.75 12h-3.5L9.5 6z",check:"M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z",sessions:"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z",notes:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 2 5 5h-5V4zM6 20V4h5v7h7v9H6z",schedule:"M19 4h-1V2h-2v2H8V2H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 16H5V10h14v10zM9 14H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm-8 4H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2z",agents:"M12 2a5 5 0 1 0 0 10 5 5 0 0 0 0-10zm0 8a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm-7 13v-2a5 5 0 0 1 5-5h4a5 5 0 0 1 5 5v2h2v-2a7 7 0 0 0-7-7H10a7 7 0 0 0-7 7v2h2z",history:"M13 3a9 9 0 0 0-9 9H1l4 4 4-4H6a7 7 0 1 1 7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.96 8.96 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.25 2.55.75-1.23-3.5-2.08V8H12z","quick-commands":"M13 3 4 14h7l-1 7 9-11h-7l1-7z",windows:"M4 6h16v2H4V6zm0 5h10v2H4v-2zm0 5h16v2H4v-2z",terminal:"M4 17.6 6.4 20l8-8-8-8L4 6.4 11.6 12 4 17.6zM20 18h-8v2h8v-2z",microphone:"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5-3c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z",keyboard:"M20 5H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z",settings:"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.488.488 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6a3.6 3.6 0 1 1 0-7.2 3.6 3.6 0 0 1 0 7.2z",pin:"M16 12V4h1V2H7v2h1v8l-4 4v2h5.2v6h1.6v-6H20v-2l-4-4z","pin-outline":"M16 12V4h1V2H7v2h1v8l-4 4v2h5.2v6h1.6v-6H20v-2l-4-4zm-4.8 7.2V11l2-2V4h1.6v5l2 2v5.2H11.2z",folder:"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",worktree:"M6 3a3 3 0 0 0-1 5.83v6.34a3 3 0 1 0 2 0V8.83A3 3 0 0 0 6 3zm0 2a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm0 12a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM18 3a3 3 0 0 0-1 5.83V9a4 4 0 0 1-4 4h-2a3 3 0 1 0 0 2h2a6 6 0 0 0 6-6v-.17A3 3 0 0 0 18 3zm0 2a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM9 13a1 1 0 1 1 0 2 1 1 0 0 1 0-2z",copy:"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z",export:"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z",chevronRight:"M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.41 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.7 6.7a1 1 0 0 0-1.41.01z",lightning:"M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66.19-.33.51-.89.73-1.27L13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15c-.22.46-.58 1.18-.78 1.58L11 21z","git-branch":"M13.5 7a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0zm0 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0zM11 9.5v5.1a4.03 4.03 0 0 0-3-.1V9.4a4.03 4.03 0 0 0 3 .1z",external:"M14 3h7v7h-2V6l-9.29 9.29-1.41-1.41L17.59 4.5H14V3zm4 16H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7z",play:"M8 5v14l11-7L8 5z",stop:"M6 6h12v12H6V6z",warning:"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z",info:"M11 7h2v2h-2V7zm0 4h2v6h-2v-6zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z",spinner:"M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8z",dots:"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"};function
|
|
1
|
+
const a={menu:"M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z",close:"M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7a1 1 0 1 0-1.41 1.41L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 1 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z",search:"M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z",refresh:"M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 8 8h-2a6 6 0 1 1-6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z",add:"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z",edit:"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zm13.66-12.83a1 1 0 0 0 0-1.41l-1.34-1.34a1 1 0 0 0-1.41 0l-1.05 1.05 2.75 2.75 1.05-1.05z",delete:"M15 4V3H9v1H4v2h1.5l.81 13.05A2 2 0 0 0 8.3 21h7.4a2 2 0 0 0 1.99-1.95L18.5 6H20V4h-5zm-5.5 2h5l-.75 12h-3.5L9.5 6z",check:"M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z",sessions:"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z",notes:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 2 5 5h-5V4zM6 20V4h5v7h7v9H6z",schedule:"M19 4h-1V2h-2v2H8V2H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 16H5V10h14v10zM9 14H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm-8 4H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2z",agents:"M12 2a5 5 0 1 0 0 10 5 5 0 0 0 0-10zm0 8a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm-7 13v-2a5 5 0 0 1 5-5h4a5 5 0 0 1 5 5v2h2v-2a7 7 0 0 0-7-7H10a7 7 0 0 0-7 7v2h2z",history:"M13 3a9 9 0 0 0-9 9H1l4 4 4-4H6a7 7 0 1 1 7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.96 8.96 0 0 0 13 21a9 9 0 0 0 0-18zm-1 5v5l4.25 2.55.75-1.23-3.5-2.08V8H12z","quick-commands":"M13 3 4 14h7l-1 7 9-11h-7l1-7z",windows:"M4 6h16v2H4V6zm0 5h10v2H4v-2zm0 5h16v2H4v-2z",terminal:"M4 17.6 6.4 20l8-8-8-8L4 6.4 11.6 12 4 17.6zM20 18h-8v2h8v-2z",microphone:"M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5-3c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z",keyboard:"M20 5H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z",settings:"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.488.488 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6a3.6 3.6 0 1 1 0-7.2 3.6 3.6 0 0 1 0 7.2z",pin:"M16 12V4h1V2H7v2h1v8l-4 4v2h5.2v6h1.6v-6H20v-2l-4-4z","pin-outline":"M16 12V4h1V2H7v2h1v8l-4 4v2h5.2v6h1.6v-6H20v-2l-4-4zm-4.8 7.2V11l2-2V4h1.6v5l2 2v5.2H11.2z",folder:"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",file:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 2 5 5h-5V4zM6 20V4h5v7h7v9H6z",worktree:"M6 3a3 3 0 0 0-1 5.83v6.34a3 3 0 1 0 2 0V8.83A3 3 0 0 0 6 3zm0 2a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm0 12a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM18 3a3 3 0 0 0-1 5.83V9a4 4 0 0 1-4 4h-2a3 3 0 1 0 0 2h2a6 6 0 0 0 6-6v-.17A3 3 0 0 0 18 3zm0 2a1 1 0 1 1 0 2 1 1 0 0 1 0-2zM9 13a1 1 0 1 1 0 2 1 1 0 0 1 0-2z",copy:"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z",export:"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z",chevronRight:"M9.29 6.71a1 1 0 0 0 0 1.41L13.17 12l-3.88 3.88a1 1 0 1 0 1.41 1.41l4.59-4.59a1 1 0 0 0 0-1.41L10.7 6.7a1 1 0 0 0-1.41.01z",lightning:"M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66.19-.33.51-.89.73-1.27L13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15c-.22.46-.58 1.18-.78 1.58L11 21z","git-branch":"M13.5 7a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0zm0 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0zM11 9.5v5.1a4.03 4.03 0 0 0-3-.1V9.4a4.03 4.03 0 0 0 3 .1z",external:"M14 3h7v7h-2V6l-9.29 9.29-1.41-1.41L17.59 4.5H14V3zm4 16H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7z",play:"M8 5v14l11-7L8 5z",stop:"M6 6h12v12H6V6z",warning:"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z",info:"M11 7h2v2h-2V7zm0 4h2v6h-2v-6zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z",spinner:"M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0 0 20 12c0-4.42-3.58-8-8-8z",dots:"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"};function l(h){return a[h]??a.menu}function c(h,v=""){return`<svg viewBox="0 0 24 24" fill="currentColor"${v?" "+v:""}><path d="${l(h)}"/></svg>`}function H(h){return`<path d="${l(h)}"/>`}const m={};function n(h,v){const z=m[h];return z?c(z):v?.trim().startsWith("<svg")?v:`<span style="font-size:18px;line-height:1">${v??""}</span>`}export{n as extIcon,c as icon,l as iconPath,H as iconPathString};
|