tmux-weblink 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import{a as F}from"./chunk-O3DBTEWH.js";var h=window.__TMUX_WEB_SHELL__,l=null,c=null,D="sessions",m="",B="",x="",H="",i=document.getElementById("sidebar-content"),g=document.getElementById("main-placeholder"),f=document.getElementById("terminal-container"),k=document.getElementById("file-editor"),P=document.getElementById("fe-path"),o=document.getElementById("fe-status"),E=document.getElementById("fe-content"),y=document.getElementById("fe-save"),R=document.getElementById("fe-delete"),V=document.getElementById("fe-back"),S=document.getElementById("fe-new-name"),$=document.getElementById("fe-new-btn"),T=document.getElementById("settings-backdrop"),N=document.getElementById("settings-popover"),j=document.querySelector(".sidebar");function J(){return window.matchMedia("(max-width: 640px)").matches}function O(){J()&&j.classList.add("collapsed")}function z(){j.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{z(),_("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{z(),_("files")});function _(e){D=e,document.querySelectorAll(".mode-btn").forEach(n=>n.classList.remove("active"));let t=document.getElementById(e==="sessions"?"mode-sessions":"mode-files");t&&t.classList.add("active"),e==="sessions"?L():U()}async function L(){i.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{let t=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(let n of t){let a=document.createElement("div");a.className="session-item"+(n.name===c?" active":""),a.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${n.attached?"M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H5V6h14v12z":"M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z"}"></svg>
2
- <span>${b(n.name)}</span>
3
- <button class="session-edit-btn" data-session="${b(n.name)}" title="Edit session"><svg viewBox="0 0 24 24" fill="currentColor" width="14" height="14"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 000-1.41l-2.34-2.34a1 1 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg></button>
4
- `,a.addEventListener("click",p=>{p.target.closest(".session-edit-btn")||M(n.name)}),i.appendChild(a);let s=a.querySelector(".session-edit-btn");s&&s.addEventListener("click",p=>{p.stopPropagation(),Z(n.name,s)})}}catch{i.innerHTML+='<div class="file-tree-empty">Failed to load sessions</div>'}let e=document.getElementById("ns-btn");e&&e.addEventListener("click",()=>document.getElementById("new-session-modal")?.classList.add("open"))}function b(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function M(e){if(!(c===e&&l)){k.style.display="none",g.style.display="none",f.style.display="",f.classList.add("terminal-pending"),O(),l&&(l.destroy(),l=null),c=e;try{l=await F(f,e,{terminal:h.terminal,scrollback:h.scrollback,theme:h.theme,renderer:h.renderer})}catch(t){console.error("[shell] terminal init failed:",t);let n=t&&typeof t=="object"&&"message"in t?t.message:String(t);f.textContent="Failed to open terminal for "+e+": "+n,f.classList.remove("terminal-pending")}}}var I=[];function U(){if(I=h.fsRoots||[],I.length===0){if(c){q(c);return}i.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}i.innerHTML="";for(let e of I){let t=document.createElement("div");t.className="file-tree-item",t.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${b(e)}`,t.addEventListener("click",()=>u(e)),i.appendChild(t)}}async function q(e){i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();t.path?u(t.path):i.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{i.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function u(e){m=e,i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();i.innerHTML="";let n=A(e);if(n&&!I.some(s=>e===s)){let s=document.createElement("div");s.className="file-tree-item",s.innerHTML="..",s.addEventListener("click",()=>u(n)),i.appendChild(s)}if(t.dirs)for(let s of t.dirs){let p=s.split("/").pop()||s.split("\\").pop(),d=document.createElement("div");d.className="file-tree-item",d.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${b(p)}`,d.addEventListener("click",()=>u(s)),i.appendChild(d)}else i.innerHTML="";let a=t.files||[];for(let s of a){let p=s.split("/").pop()||s.split("\\").pop(),d=document.createElement("div");d.className="file-tree-item file",d.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${b(p)}`,d.addEventListener("click",()=>W(s)),i.appendChild(d)}}catch{i.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function A(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function W(e){B=e,f.style.display="none",l&&(l.destroy(),l=null,c=null),O(),g.style.display="none",k.style.display="flex",P.textContent=e,o.textContent="Loading...",y.style.display="none";try{let t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){let a=await t.json();o.textContent=a.error||"Failed to load",E.value="";return}let n=await t.json();E.value=n.content,x=n.content,H=n.content,o.textContent=n.size+" bytes",y.style.display=""}catch{o.textContent="Failed to load",E.value=""}}E.addEventListener("input",()=>{x=E.value}),y.addEventListener("click",async()=>{y.textContent="Saving...",y.disabled=!0;try{let e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B,content:x})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Save failed";return}H=x,o.textContent="Saved"}catch{o.textContent="Save failed"}finally{y.disabled=!1,y.textContent="Save"}}),R.addEventListener("click",async()=>{if(confirm("Delete "+B+"?"))try{let e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B})});if(!e.ok){let t=await e.json();o.textContent=t.error||"Delete failed";return}k.style.display="none",g.style.display="flex",m&&u(m)}catch{o.textContent="Delete failed"}}),V.addEventListener("click",()=>{k.style.display="none",g.style.display="flex",m&&u(m)}),$.addEventListener("click",async()=>{let e=S.value.trim();if(!e)return;let t=m+"/"+e;try{let n=await fetch("/api/file/touch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:t})});if(!n.ok){let a=await n.json();o.textContent=a.error||"Create failed";return}S.value="",m&&u(m)}catch{o.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=N.classList.toggle("open");T.classList.toggle("open",e)}),T.addEventListener("click",()=>{N.classList.remove("open"),T.classList.remove("open")});var v="",w=document.createElement("div");w.className="session-popover-backdrop",document.body.appendChild(w);var r=document.createElement("div");r.id="session-popover",r.className="session-popover",r.style.display="none",r.innerHTML=`<div class="sp-field">
1
+ import{a as R}from"./chunk-O3DBTEWH.js";var g=window.__TMUX_WEB_SHELL__,i=null,r=null,V="sessions",p="",C="",x="",S="",l=document.getElementById("sidebar-content"),E=document.getElementById("main-placeholder"),y=document.getElementById("terminal-container"),I=document.getElementById("file-editor"),$=document.getElementById("fe-path"),o=document.getElementById("fe-status"),L=document.getElementById("fe-content"),u=document.getElementById("fe-save"),J=document.getElementById("fe-delete"),U=document.getElementById("fe-back"),N=document.getElementById("fe-new-name"),q=document.getElementById("fe-new-btn"),M=document.getElementById("settings-backdrop"),j=document.getElementById("settings-popover"),O=document.querySelector(".sidebar");function A(){return window.matchMedia("(max-width: 640px)").matches}function z(){A()&&O.classList.add("collapsed")}function _(){O.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{_(),F("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{_(),F("files")});function F(e){V=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():W()}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===r?" 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>${w(n.name)}</span>
3
+ <button class="session-edit-btn" data-session="${w(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",f=>{f.target.closest(".session-edit-btn")||H(n.name)}),l.appendChild(a);let s=a.querySelector(".session-edit-btn");s&&s.addEventListener("click",f=>{f.stopPropagation(),Q(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 w(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function H(e){if(!(r===e&&i)){I.style.display="none",E.style.display="none",y.style.display="",y.classList.add("terminal-pending"),z(),i&&(i.destroy(),i=null),r=e;try{i=await R(y,e,{terminal:g.terminal,scrollback:g.scrollback,theme:g.theme,renderer:g.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 T=[];function W(){if(T=g.fsRoots||[],T.length===0){if(r){K(r);return}l.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}l.innerHTML="";for(let e of T){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> ${w(e)}`,t.addEventListener("click",()=>v(e)),l.appendChild(t)}}async function K(e){l.innerHTML='<div class="file-tree-empty">Loading...</div>';try{let t=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();t.path?v(t.path):l.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{l.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function v(e){p=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=X(e);if(n&&!T.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 f=s.split("/").pop()||s.split("\\").pop(),c=document.createElement("div");c.className="file-tree-item",c.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(f)}`,c.addEventListener("click",()=>v(s)),l.appendChild(c)}else l.innerHTML="";let a=t.files||[];for(let s of a){let f=s.split("/").pop()||s.split("\\").pop(),c=document.createElement("div");c.className="file-tree-item file",c.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${w(f)}`,c.addEventListener("click",()=>Z(s)),l.appendChild(c)}}catch{l.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function X(e){let t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function Z(e){C=e,y.style.display="none",i&&(i.destroy(),i=null,r=null),z(),E.style.display="none",I.style.display="flex",$.textContent=e,o.textContent="Loading...",u.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",L.value="";return}let n=await t.json();L.value=n.content,x=n.content,S=n.content,o.textContent=n.size+" bytes",u.style.display=""}catch{o.textContent="Failed to load",L.value=""}}L.addEventListener("input",()=>{x=L.value}),u.addEventListener("click",async()=>{u.textContent="Saving...",u.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}S=x,o.textContent="Saved"}catch{o.textContent="Save failed"}finally{u.disabled=!1,u.textContent="Save"}}),J.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}I.style.display="none",E.style.display="flex",p&&v(p)}catch{o.textContent="Delete failed"}}),U.addEventListener("click",()=>{I.style.display="none",E.style.display="flex",p&&v(p)}),q.addEventListener("click",async()=>{let e=N.value.trim();if(!e)return;let t=p+"/"+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}N.value="",p&&v(p)}catch{o.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{let e=j.classList.toggle("open");M.classList.toggle("open",e)}),M.addEventListener("click",()=>{j.classList.remove("open"),M.classList.remove("open")});var h="",k=document.createElement("div");k.className="session-popover-backdrop",document.body.appendChild(k);var m=document.createElement("div");m.id="session-popover",m.className="session-popover",m.style.display="none",m.innerHTML=`<div class="sp-field">
5
5
  <label>Rename session</label>
6
6
  <input type="text" id="sp-name" />
7
7
  </div>
@@ -10,4 +10,5 @@ import{a as F}from"./chunk-O3DBTEWH.js";var h=window.__TMUX_WEB_SHELL__,l=null,c
10
10
  <button class="btn" id="sp-cancel">Cancel</button>
11
11
  </div>
12
12
  <hr class="sp-divider" />
13
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(r);function X(e){let t=e.getBoundingClientRect();r.style.top=t.bottom+4+"px",r.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Z(e,t){v=e;let n=document.getElementById("sp-name");n.value=e,w.classList.add("open"),r.style.display="block",X(t),setTimeout(()=>n.focus(),50)}function C(){w.classList.remove("open"),r.style.display="none"}w.addEventListener("click",C),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===v){C();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:v,newName:e})})).ok)return;C(),L(),c===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;C(),c===v&&(l&&(l.destroy(),l=null),c=null,f.style.display="none",g.style.display="flex"),L()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",C);var 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=>{let t=e.target.closest("button[data-key]");if(!t||!l)return;let n=G[t.dataset.key];n&&(l.sendInput(n),l.focus())}),window.__openSession=M,window.__refreshSidebar=L,L();
13
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(m);function G(e){let t=e.getBoundingClientRect();m.style.top=t.bottom+4+"px",m.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Q(e,t){h=e;let n=document.getElementById("sp-name");n.value=e,k.classList.add("open"),m.style.display="block",G(t),setTimeout(()=>n.focus(),50)}function B(){k.classList.remove("open"),m.style.display="none"}k.addEventListener("click",B),document.getElementById("sp-save").addEventListener("click",async()=>{let e=document.getElementById("sp-name").value.trim();if(!e||e===h){B();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:h,newName:e})})).ok)return;B(),b(),r===h&&H(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+h+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:h})})).ok)return;B(),r===h&&(i&&(i.destroy(),i=null),r=null,y.style.display="none",E.style.display="flex"),b()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",B);var Y={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",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{let t=e.target.closest("button[data-key]");if(!t||!i)return;let n=Y[t.dataset.key];n&&(i.sendInput(n),i.focus())});var d=document.getElementById("mk-input"),D=document.getElementById("mk-send");function P(){if(!d||!i)return;let e=d.value;e&&(i.sendInput(e+"\r"),d.value="",d.rows=1,i.focus())}d&&(d.addEventListener("input",()=>{d.rows=1;let e=d.value.split(`
14
+ `).length;d.rows=Math.min(e,4)}),d.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),P())})),D&&D.addEventListener("click",P),window.__openSession=H,window.__refreshSidebar=b,b();
@@ -1,7 +1,7 @@
1
- import{initTerminal as z}from"./terminal-core.js";const g=window.__TMUX_WEB_SHELL__;let a=null,r=null,R="sessions",f="",k="",B="",H="";const s=document.getElementById("sidebar-content"),b=document.getElementById("main-placeholder"),u=document.getElementById("terminal-container"),S=document.getElementById("file-editor"),_=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"),F=document.getElementById("settings-popover"),j=document.querySelector(".sidebar");function J(){return window.matchMedia("(max-width: 640px)").matches}function O(){J()&&j.classList.add("collapsed")}function D(){j.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{D(),P("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{D(),P("files")});function P(e){R=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(){s.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{const p=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(const m of p){const i=document.createElement("div");i.className="session-item"+(m.name===r?" active":""),i.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${m.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(m.name)}</span>
3
- <button class="session-edit-btn" data-session="${w(m.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
- `,i.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||M(m.name)}),s.appendChild(i);const o=i.querySelector(".session-edit-btn");o&&o.addEventListener("click",l=>{l.stopPropagation(),G(m.name,o)})}}catch{s.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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function M(e){if(!(r===e&&a)){S.style.display="none",b.style.display="none",u.style.display="",u.classList.add("terminal-pending"),O(),a&&(a.destroy(),a=null),r=e;try{a=await z(u,e,{terminal:g.terminal,scrollback:g.scrollback,theme:g.theme,renderer:g.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=g.fsRoots||[],I.length===0){if(r){A(r);return}s.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}s.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",()=>v(e)),s.appendChild(t)}}async function A(e){s.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();n.path?v(n.path):s.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{s.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function v(e){f=e,s.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();s.innerHTML="";const p=W(e);if(p&&!I.some(o=>e===o)){const o=document.createElement("div");o.className="file-tree-item",o.innerHTML="..",o.addEventListener("click",()=>v(p)),s.appendChild(o)}if(n.dirs)for(const i of n.dirs){const o=i.split("/").pop()||i.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${w(o)}`,l.addEventListener("click",()=>v(i)),s.appendChild(l)}else s.innerHTML="";const m=n.files||[];for(const i of m){const o=i.split("/").pop()||i.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item file",l.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${w(o)}`,l.addEventListener("click",()=>X(i)),s.appendChild(l)}}catch{s.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 X(e){k=e,u.style.display="none",a&&(a.destroy(),a=null,r=null),O(),b.style.display="none",S.style.display="flex",_.textContent=e,c.textContent="Loading...",y.style.display="none";try{const t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){const p=await t.json();c.textContent=p.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",f&&v(f)}catch{c.textContent="Delete failed"}}),$.addEventListener("click",()=>{S.style.display="none",b.style.display="flex",f&&v(f)}),q.addEventListener("click",async()=>{const e=N.value.trim();if(!e)return;const t=f+"/"+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 p=await n.json();c.textContent=p.error||"Create failed";return}N.value="",f&&v(f)}catch{c.textContent="Create failed"}}),document.getElementById("mode-settings").addEventListener("click",()=>{const e=F.classList.toggle("open");T.classList.toggle("open",e)}),T.addEventListener("click",()=>{F.classList.remove("open"),T.classList.remove("open")});let h="";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">
1
+ import{initTerminal as V}from"./terminal-core.js";const b=window.__TMUX_WEB_SHELL__;let s=null,d=null,$="sessions",u="",B="",S="",N="";const i=document.getElementById("sidebar-content"),E=document.getElementById("main-placeholder"),y=document.getElementById("terminal-container"),I=document.getElementById("file-editor"),q=document.getElementById("fe-path"),c=document.getElementById("fe-status"),L=document.getElementById("fe-content"),v=document.getElementById("fe-save"),J=document.getElementById("fe-delete"),U=document.getElementById("fe-back"),F=document.getElementById("fe-new-name"),A=document.getElementById("fe-new-btn"),T=document.getElementById("settings-backdrop"),j=document.getElementById("settings-popover"),D=document.querySelector(".sidebar");function W(){return window.matchMedia("(max-width: 640px)").matches}function O(){W()&&D.classList.add("collapsed")}function P(){D.classList.remove("collapsed")}document.getElementById("mode-sessions").addEventListener("click",()=>{P(),z("sessions")}),document.getElementById("mode-files").addEventListener("click",()=>{P(),z("files")});function z(e){$=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"?w():K()}async function w(){i.innerHTML='<div class="sidebar-section-label">Sessions</div><button class="new-session-sidebar-btn" id="ns-btn">+ New Session</button>';try{const m=(await(await fetch("/api/sidebar/sessions")).json()).recent||[];for(const f of m){const o=document.createElement("div");o.className="session-item"+(f.name===d?" active":""),o.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor"><path d="${f.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>${k(f.name)}</span>
3
+ <button class="session-edit-btn" data-session="${k(f.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
+ `,o.addEventListener("click",l=>{l.target.closest(".session-edit-btn")||H(f.name)}),i.appendChild(o);const a=o.querySelector(".session-edit-btn");a&&a.addEventListener("click",l=>{l.stopPropagation(),Y(f.name,a)})}}catch{i.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 k(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function H(e){if(!(d===e&&s)){I.style.display="none",E.style.display="none",y.style.display="",y.classList.add("terminal-pending"),O(),s&&(s.destroy(),s=null),d=e;try{s=await V(y,e,{terminal:b.terminal,scrollback:b.scrollback,theme:b.theme,renderer:b.renderer})}catch(t){console.error("[shell] terminal init failed:",t);const 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")}}}let M=[];function K(){if(M=b.fsRoots||[],M.length===0){if(d){X(d);return}i.innerHTML='<div class="file-tree-info">Open a session first, then switch to Files to browse its directory.</div>';return}i.innerHTML="";for(const e of M){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> ${k(e)}`,t.addEventListener("click",()=>h(e)),i.appendChild(t)}}async function X(e){i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/session-path?session="+encodeURIComponent(e))).json();n.path?h(n.path):i.innerHTML='<div class="file-tree-info">Could not determine session directory.</div>'}catch{i.innerHTML='<div class="file-tree-error">Failed to get session directory</div>'}}async function h(e){u=e,i.innerHTML='<div class="file-tree-empty">Loading...</div>';try{const n=await(await fetch("/api/fs/list?path="+encodeURIComponent(e))).json();i.innerHTML="";const m=Z(e);if(m&&!M.some(a=>e===a)){const a=document.createElement("div");a.className="file-tree-item",a.innerHTML="..",a.addEventListener("click",()=>h(m)),i.appendChild(a)}if(n.dirs)for(const o of n.dirs){const a=o.split("/").pop()||o.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item",l.innerHTML=`<svg viewBox="0 0 24 24" fill="currentColor" class="file-icon"><path d="M10 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg> ${k(a)}`,l.addEventListener("click",()=>h(o)),i.appendChild(l)}else i.innerHTML="";const f=n.files||[];for(const o of f){const a=o.split("/").pop()||o.split("\\").pop(),l=document.createElement("div");l.className="file-tree-item file",l.innerHTML=`<span class="file-icon">\u{1F4C4}</span> ${k(a)}`,l.addEventListener("click",()=>G(o)),i.appendChild(l)}}catch{i.innerHTML='<div class="file-tree-error">Failed to load directory</div>'}}function Z(e){const t=e.lastIndexOf("/");return t<=0?null:e.substring(0,t)}async function G(e){B=e,y.style.display="none",s&&(s.destroy(),s=null,d=null),O(),E.style.display="none",I.style.display="flex",q.textContent=e,c.textContent="Loading...",v.style.display="none";try{const t=await fetch("/api/file?path="+encodeURIComponent(e));if(!t.ok){const m=await t.json();c.textContent=m.error||"Failed to load",L.value="";return}const n=await t.json();L.value=n.content,S=n.content,N=n.content,c.textContent=n.size+" bytes",v.style.display=""}catch{c.textContent="Failed to load",L.value=""}}L.addEventListener("input",()=>{S=L.value}),v.addEventListener("click",async()=>{v.textContent="Saving...",v.disabled=!0;try{const e=await fetch("/api/file",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B,content:S})});if(!e.ok){const t=await e.json();c.textContent=t.error||"Save failed";return}N=S,c.textContent="Saved"}catch{c.textContent="Save failed"}finally{v.disabled=!1,v.textContent="Save"}}),J.addEventListener("click",async()=>{if(confirm("Delete "+B+"?"))try{const e=await fetch("/api/file/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:B})});if(!e.ok){const t=await e.json();c.textContent=t.error||"Delete failed";return}I.style.display="none",E.style.display="flex",u&&h(u)}catch{c.textContent="Delete failed"}}),U.addEventListener("click",()=>{I.style.display="none",E.style.display="flex",u&&h(u)}),A.addEventListener("click",async()=>{const e=F.value.trim();if(!e)return;const t=u+"/"+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 m=await n.json();c.textContent=m.error||"Create failed";return}F.value="",u&&h(u)}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 g="";const C=document.createElement("div");C.className="session-popover-backdrop",document.body.appendChild(C);const p=document.createElement("div");p.id="session-popover",p.className="session-popover",p.style.display="none",p.innerHTML=`<div class="sp-field">
5
5
  <label>Rename session</label>
6
6
  <input type="text" id="sp-name" />
7
7
  </div>
@@ -10,4 +10,5 @@ import{initTerminal as z}from"./terminal-core.js";const g=window.__TMUX_WEB_SHEL
10
10
  <button class="btn" id="sp-cancel">Cancel</button>
11
11
  </div>
12
12
  <hr class="sp-divider" />
13
- <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(d);function Z(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 G(e,t){h=e;const n=document.getElementById("sp-name");n.value=e,C.classList.add("open"),d.style.display="block",Z(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===h){x();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:h,newName:e})})).ok)return;x(),L(),r===h&&M(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+h+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:h})})).ok)return;x(),r===h&&(a&&(a.destroy(),a=null),r=null,u.style.display="none",b.style.display="flex"),L()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",x);const K={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||!a)return;const n=K[t.dataset.key];n&&(a.sendInput(n),a.focus())}),window.__openSession=M,window.__refreshSidebar=L,L();
13
+ <button class="btn danger" id="sp-delete">Delete session</button>`,document.body.appendChild(p);function Q(e){const t=e.getBoundingClientRect();p.style.top=t.bottom+4+"px",p.style.left=Math.max(8,Math.min(t.left,window.innerWidth-240))+"px"}function Y(e,t){g=e;const n=document.getElementById("sp-name");n.value=e,C.classList.add("open"),p.style.display="block",Q(t),setTimeout(()=>n.focus(),50)}function x(){C.classList.remove("open"),p.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===g){x();return}try{if(!(await fetch("/api/sessions/rename",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({oldName:g,newName:e})})).ok)return;x(),w(),d===g&&H(e)}catch{}}),document.getElementById("sp-delete").addEventListener("click",async()=>{if(confirm('Delete session "'+g+'"?'))try{if(!(await fetch("/api/sessions/kill",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:g})})).ok)return;x(),d===g&&(s&&(s.destroy(),s=null),d=null,y.style.display="none",E.style.display="flex"),w()}catch{}}),document.getElementById("sp-cancel").addEventListener("click",x);const ee={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",yes:"yes\r"};document.getElementById("mobile-keys").addEventListener("click",e=>{const t=e.target.closest("button[data-key]");if(!t||!s)return;const n=ee[t.dataset.key];n&&(s.sendInput(n),s.focus())});const r=document.getElementById("mk-input"),R=document.getElementById("mk-send");function _(){if(!r||!s)return;const e=r.value;e&&(s.sendInput(e+"\r"),r.value="",r.rows=1,s.focus())}r&&(r.addEventListener("input",()=>{r.rows=1;const e=r.value.split(`
14
+ `).length;r.rows=Math.min(e,4)}),r.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),_())})),R&&R.addEventListener("click",_),window.__openSession=H,window.__refreshSidebar=w,w();
@@ -1,4 +1,4 @@
1
- import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarHTML as m,commandbarScript as f}from"../commandbar.js";import{newSessionModalCSS as v,newSessionModalHTML as h,newSessionModalScript as u,reducedMotion as y}from"../shared-layout.js";import{icon as o}from"../icons.js";function C(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function a(n="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${n}; outline: none;`}function _(n){const{theme:e,commandbarEnabled:t,commandbarSessions:s,agentsEnabled:r,fsRoots:d,terminalCfg:l,renderer:i,scrollback:p}=n,c=`
1
+ import{cssVarsStyle as x}from"../theme.js";import{commandbarCSS as g,commandbarHTML as m,commandbarScript as f}from"../commandbar.js";import{newSessionModalCSS as u,newSessionModalHTML as v,newSessionModalScript as h,reducedMotion as y}from"../shared-layout.js";import{icon as n}from"../icons.js";function C(o){return o.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function a(o="var(--panel-accent)"){return`box-shadow: 0 0 0 2px ${o}; outline: none;`}function _(o){const{theme:e,commandbarEnabled:t,commandbarSessions:s,agentsEnabled:r,fsRoots:l,terminalCfg:d,renderer:i,scrollback:p}=o,c=`
2
2
  *, *::before, *::after { box-sizing: border-box; }
3
3
  html, body {
4
4
  background: var(--page-bg); color: var(--page-fg);
@@ -26,7 +26,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
26
26
  /* \u2500\u2500 App layout \u2500\u2500 */
27
27
  .app-layout {
28
28
  display: flex;
29
- height: 100vh;
29
+ height: 100dvh;
30
30
  padding-top: 48px;
31
31
  transition: height 0.05s;
32
32
  }
@@ -284,6 +284,32 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
284
284
  touch-action: manipulation; user-select: none;
285
285
  }
286
286
  .mobile-keys button:active { opacity: 0.55; }
287
+ .mobile-keys .mk-input {
288
+ display: flex; gap: 4px; flex: 1 1 100%;
289
+ }
290
+ .mobile-keys .mk-input textarea {
291
+ flex: 1; resize: none; box-sizing: border-box;
292
+ min-height: 38px; max-height: 80px;
293
+ background: var(--terminal-bg, rgba(0,0,0,0.28));
294
+ color: var(--page-fg);
295
+ border: 1px solid var(--panel-border);
296
+ border-radius: 8px; padding: 6px 10px;
297
+ font-family: var(--font-mono); font-size: var(--text-base); line-height: 1.4;
298
+ outline: none;
299
+ }
300
+ .mobile-keys .mk-input textarea:focus {
301
+ border-color: var(--panel-accent);
302
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--panel-accent) 12%, transparent);
303
+ }
304
+ .mobile-keys .mk-input textarea::placeholder { color: var(--panel-muted); opacity: 0.6; }
305
+ .mobile-keys .mk-input button {
306
+ min-width: 44px;
307
+ border: 1px solid var(--panel-success); color: var(--panel-success);
308
+ background: var(--page-bg);
309
+ }
310
+ .mobile-keys .mk-input button:hover {
311
+ background: color-mix(in srgb, var(--panel-success) 12%, transparent);
312
+ }
287
313
  @media (max-width: 640px) {
288
314
  .mobile-keys { display: flex; }
289
315
  }
@@ -301,16 +327,16 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
301
327
  .file-roots-list li svg { width: 20px; height: 20px; flex-shrink: 0; fill: var(--panel-accent); }
302
328
 
303
329
  /* \u2500\u2500 Commandbar \u2500\u2500 */
304
- ${t?x():""}
330
+ ${t?g():""}
305
331
  ${y()}
306
- ${v()}
332
+ ${u()}
307
333
  @media (max-width: 640px) {
308
334
  .app-layout { flex-direction: column; }
309
335
  .sidebar { flex: 0 0 auto; border-right: none; border-bottom: 1px solid var(--panel-border); transition: max-height 0.2s; }
310
336
  .sidebar.collapsed .sidebar-content { display: none; }
311
337
  .sidebar.collapsed { max-height: 48px; overflow: hidden; }
312
338
  }
313
- `,k=JSON.stringify(e.terminal).replace(/</g,"\\u003c"),b=JSON.stringify({terminal:l,scrollback:p,theme:e.terminal,renderer:i,fsRoots:d,agentsEnabled:r,commandbarEnabled:t}).replace(/</g,"\\u003c");return`<!DOCTYPE html>
339
+ `,k=JSON.stringify(e.terminal).replace(/</g,"\\u003c"),b=JSON.stringify({terminal:d,scrollback:p,theme:e.terminal,renderer:i,fsRoots:l,agentsEnabled:r,commandbarEnabled:t}).replace(/</g,"\\u003c");return`<!DOCTYPE html>
314
340
  <html lang="en">
315
341
  <head>
316
342
  <meta charset="UTF-8" />
@@ -318,7 +344,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
318
344
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
319
345
  <title>tmux-weblink</title>
320
346
  <style>
321
- ${g(e.shell)}
347
+ ${x(e.shell)}
322
348
  ${c}
323
349
  </style>
324
350
  </head>
@@ -344,7 +370,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
344
370
  <header class="fixed-header">
345
371
  <div class="brand"><a href="/">tmux<span>-weblink</span></a></div>
346
372
  <div class="header-actions">
347
- ${t?`<button class="header-btn" id="cmdbar-btn" title="Search" aria-label="Search">${o("search")}</button>`:""}
373
+ ${t?`<button class="header-btn" id="cmdbar-btn" title="Search" aria-label="Search">${n("search")}</button>`:""}
348
374
  </div>
349
375
  </header>
350
376
 
@@ -352,9 +378,9 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
352
378
  <aside class="sidebar">
353
379
  <div class="sidebar-content" id="sidebar-content"></div>
354
380
  <div class="sidebar-footer">
355
- <button class="mode-btn active" id="mode-sessions" title="Sessions">${o("sessions",'width="20" height="20"')}</button>
356
- <button class="mode-btn" id="mode-files" title="Files">${o("folder",'width="20" height="20"')}</button>
357
- <button class="mode-btn" id="mode-settings" title="Settings">${o("settings",'width="20" height="20"')}</button>
381
+ <button class="mode-btn active" id="mode-sessions" title="Sessions">${n("sessions",'width="20" height="20"')}</button>
382
+ <button class="mode-btn" id="mode-files" title="Files">${n("folder",'width="20" height="20"')}</button>
383
+ <button class="mode-btn" id="mode-settings" title="Settings">${n("settings",'width="20" height="20"')}</button>
358
384
  </div>
359
385
  </aside>
360
386
 
@@ -380,6 +406,10 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
380
406
  </div>
381
407
  </div>
382
408
  <div class="mobile-keys" id="mobile-keys">
409
+ <div class="mk-input">
410
+ <textarea id="mk-input" placeholder="Type or voice input\u2026" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" rows="1"></textarea>
411
+ <button id="mk-send" type="button" title="Send (Enter)">&#9166;</button>
412
+ </div>
383
413
  <button data-key="esc">ESC</button>
384
414
  <button data-key="tab">Tab</button>
385
415
  <button data-key="s-tab">S-Tab</button>
@@ -392,6 +422,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
392
422
  <button data-key="ctrl-d">Ctrl-D</button>
393
423
  <button data-key="enter">\u21B5 Enter</button>
394
424
  <button data-key="exit">Exit</button>
425
+ <button data-key="yes">Yes</button>
395
426
  </div>
396
427
  </main>
397
428
  </div>
@@ -444,7 +475,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
444
475
  })();
445
476
  </script>
446
477
 
447
- ${h()}
478
+ ${v()}
448
479
  ${t?m():""}
449
480
 
450
481
  <script>
@@ -458,7 +489,7 @@ window.__onSessionCreated = async function(name) {
458
489
  await window.__refreshSidebar();
459
490
  window.__openSession(name);
460
491
  };
461
- ${u("__onSessionCreated")}
492
+ ${h("__onSessionCreated")}
462
493
  </script>
463
494
  <script>
464
495
  // Sync app-layout height to visual viewport so mobile keyboard doesn't overlap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-weblink",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Access your tmux sessions from the browser. A continuation of tmux-web and persalink.",
5
5
  "type": "module",
6
6
  "bin": {