tmux-weblink 1.1.1 → 1.1.3
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/shell-client.js +6 -5
- package/dist/browser/shell-client.js +6 -5
- package/dist/lib/mobile-toolbar.js +49 -162
- package/dist/lib/pages/shell.js +41 -11
- package/dist/lib/pages/terminal.js +29 -30
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{a as
|
|
2
|
-
<span>${
|
|
3
|
-
<button class="session-edit-btn" data-session="${
|
|
4
|
-
`,a.addEventListener("click",
|
|
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"),u=document.getElementById("terminal-container"),I=document.getElementById("file-editor"),$=document.getElementById("fe-path"),o=document.getElementById("fe-status"),L=document.getElementById("fe-content"),y=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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function H(e){if(!(r===e&&i)){I.style.display="none",E.style.display="none",u.style.display="",u.classList.add("terminal-pending"),z(),i&&(i.destroy(),i=null),r=e;try{i=await R(u,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);u.textContent="Failed to open terminal for "+e+": "+n,u.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,u.style.display="none",i&&(i.destroy(),i=null,r=null),z(),E.style.display="none",I.style.display="flex",$.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",L.value="";return}let n=await t.json();L.value=n.content,x=n.content,S=n.content,o.textContent=n.size+" bytes",y.style.display=""}catch{o.textContent="Failed to load",L.value=""}}L.addEventListener("input",()=>{x=L.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: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{y.disabled=!1,y.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(
|
|
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,u.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"};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
|
|
2
|
-
<span>${
|
|
3
|
-
<button class="session-edit-btn" data-session="${
|
|
4
|
-
`,
|
|
1
|
+
import{initTerminal as V}from"./terminal-core.js";const b=window.__TMUX_WEB_SHELL__;let s=null,r=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===r?" 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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function H(e){if(!(r===e&&s)){I.style.display="none",E.style.display="none",y.style.display="",y.classList.add("terminal-pending"),O(),s&&(s.destroy(),s=null),r=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(r){X(r);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,r=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(
|
|
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(),r===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(),r===g&&(s&&(s.destroy(),s=null),r=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"};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 d=document.getElementById("mk-input"),R=document.getElementById("mk-send");function _(){if(!d||!s)return;const e=d.value;e&&(s.sendInput(e+"\r"),d.value="",d.rows=1,s.focus())}d&&(d.addEventListener("input",()=>{d.rows=1;const e=d.value.split(`
|
|
14
|
+
`).length;d.rows=Math.min(e,4)}),d.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),_())})),R&&R.addEventListener("click",_),window.__openSession=H,window.__refreshSidebar=w,w();
|
|
@@ -19,11 +19,33 @@ import{icon as e}from"./icons.js";function n(){return`
|
|
|
19
19
|
#mobile-toolbar button:hover { color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
|
|
20
20
|
#mobile-toolbar button:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); outline: none; }
|
|
21
21
|
#mobile-toolbar button svg { width: 22px; height: 22px; fill: currentColor; }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
|
|
23
|
+
#mobile-toolbar .tb-input {
|
|
24
|
+
flex: 1; display: flex; align-items: stretch; gap: 6px; min-width: 0;
|
|
25
|
+
}
|
|
26
|
+
#mobile-toolbar .tb-input textarea {
|
|
27
|
+
flex: 1; resize: none; box-sizing: border-box;
|
|
28
|
+
min-height: 38px; max-height: 96px;
|
|
29
|
+
background: var(--terminal-bg, rgba(0,0,0,0.28));
|
|
30
|
+
color: var(--page-fg);
|
|
31
|
+
border: 1px solid var(--panel-border);
|
|
32
|
+
border-radius: 8px; padding: 6px 10px;
|
|
33
|
+
font-family: var(--font-mono); font-size: var(--text-base); line-height: 1.4;
|
|
34
|
+
outline: none;
|
|
25
35
|
}
|
|
26
|
-
|
|
36
|
+
#mobile-toolbar .tb-input textarea:focus {
|
|
37
|
+
border-color: var(--panel-accent);
|
|
38
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--panel-accent) 12%, transparent);
|
|
39
|
+
}
|
|
40
|
+
#mobile-toolbar .tb-input textarea::placeholder { color: var(--panel-muted); opacity: 0.6; }
|
|
41
|
+
#mobile-toolbar .tb-input button {
|
|
42
|
+
flex: 0 0 auto; min-width: 44px;
|
|
43
|
+
border: 1px solid var(--panel-success); color: var(--panel-success);
|
|
44
|
+
}
|
|
45
|
+
#mobile-toolbar .tb-input button:hover {
|
|
46
|
+
background: color-mix(in srgb, var(--panel-success) 12%, transparent);
|
|
47
|
+
}
|
|
48
|
+
#mobile-toolbar #tb-kb-btn { flex: 0 0 auto; min-width: 44px; }
|
|
27
49
|
|
|
28
50
|
#type-backdrop {
|
|
29
51
|
position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 1001;
|
|
@@ -128,62 +150,13 @@ import{icon as e}from"./icons.js";function n(){return`
|
|
|
128
150
|
border-color: var(--panel-success); color: var(--panel-success);
|
|
129
151
|
}
|
|
130
152
|
|
|
131
|
-
/* \u2500\u2500 Floating input bar (narrow-screen voice-input helper) \u2500\u2500 */
|
|
132
|
-
#float-bar { display: none; }
|
|
133
|
-
@media (max-width: 767px) {
|
|
134
|
-
#float-bar {
|
|
135
|
-
display: flex; align-items: flex-end; gap: 6px;
|
|
136
|
-
position: fixed; bottom: 0; left: 0; right: 0;
|
|
137
|
-
z-index: 1000;
|
|
138
|
-
padding: 8px 10px;
|
|
139
|
-
padding-bottom: calc(8px + env(safe-area-inset-bottom));
|
|
140
|
-
background: var(--panel-bg);
|
|
141
|
-
border-top: 1px solid var(--panel-border);
|
|
142
|
-
}
|
|
143
|
-
#float-bar textarea {
|
|
144
|
-
flex: 1; resize: none; box-sizing: border-box;
|
|
145
|
-
min-height: 38px; max-height: 120px;
|
|
146
|
-
background: var(--terminal-bg, rgba(0,0,0,0.28));
|
|
147
|
-
color: var(--page-fg);
|
|
148
|
-
border: 1px solid var(--panel-border);
|
|
149
|
-
border-radius: 8px; padding: 8px 10px;
|
|
150
|
-
font-family: var(--font-mono); font-size: var(--text-base); line-height: 1.4;
|
|
151
|
-
outline: none;
|
|
152
|
-
}
|
|
153
|
-
#float-bar textarea:focus {
|
|
154
|
-
border-color: var(--panel-accent);
|
|
155
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--panel-accent) 12%, transparent);
|
|
156
|
-
}
|
|
157
|
-
#float-bar textarea::placeholder { color: var(--panel-muted); opacity: 0.6; }
|
|
158
|
-
#float-bar .float-bar-actions {
|
|
159
|
-
display: flex; flex-direction: column; gap: 4px; flex-shrink: 0;
|
|
160
|
-
}
|
|
161
|
-
#float-bar .float-bar-actions button {
|
|
162
|
-
display: flex; align-items: center; justify-content: center;
|
|
163
|
-
min-width: 38px; min-height: 38px;
|
|
164
|
-
background: none; border: 1px solid var(--panel-border);
|
|
165
|
-
border-radius: 8px; color: var(--panel-muted); cursor: pointer;
|
|
166
|
-
font-size: var(--text-sm); font-family: var(--font-mono);
|
|
167
|
-
transition: color 0.15s, background 0.15s, border-color 0.15s;
|
|
168
|
-
}
|
|
169
|
-
#float-bar .float-bar-actions button:hover,
|
|
170
|
-
#float-bar .float-bar-actions button:focus-visible {
|
|
171
|
-
color: var(--panel-accent); border-color: var(--panel-accent);
|
|
172
|
-
background: color-mix(in srgb, var(--panel-accent) 8%, transparent);
|
|
173
|
-
}
|
|
174
|
-
#float-bar .float-bar-actions #float-send {
|
|
175
|
-
color: var(--panel-success); border-color: var(--panel-success);
|
|
176
|
-
}
|
|
177
|
-
#float-bar .float-bar-actions #float-send:hover {
|
|
178
|
-
background: color-mix(in srgb, var(--panel-success) 12%, transparent);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
153
|
`}function a(){return`
|
|
182
154
|
<div id="mobile-toolbar">
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
155
|
+
<div class="tb-input">
|
|
156
|
+
<textarea id="tb-input" placeholder="Type or voice input\u2026" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" rows="1"></textarea>
|
|
157
|
+
<button id="tb-send" type="button" title="Send (Enter)">⏎</button>
|
|
158
|
+
</div>
|
|
159
|
+
<button id="type-toggle" type="button" title="Advanced keys" aria-label="Advanced keys">
|
|
187
160
|
${e("keyboard")}
|
|
188
161
|
</button>
|
|
189
162
|
</div>
|
|
@@ -199,20 +172,13 @@ import{icon as e}from"./icons.js";function n(){return`
|
|
|
199
172
|
<label><input type="radio" name="type-modifier" value="Tab" /><span class="type-modal-modifier-label">Tab</span></label>
|
|
200
173
|
<label><input type="radio" name="type-modifier" value="Ctrl" /><span class="type-modal-modifier-label">Ctrl</span></label>
|
|
201
174
|
</div>
|
|
202
|
-
<textarea id="type-input" placeholder="Type a command
|
|
175
|
+
<textarea id="type-input" placeholder="Type a command\u2026" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"></textarea>
|
|
203
176
|
<div id="type-status"></div>
|
|
204
177
|
<div class="type-modal-footer">
|
|
205
178
|
<button id="type-send" type="button">Send</button>
|
|
206
179
|
<button id="type-send-enter" type="button">Send ⏎</button>
|
|
207
180
|
</div>
|
|
208
|
-
</div>`}function r(){return`
|
|
209
|
-
<div id="float-bar">
|
|
210
|
-
<textarea id="float-input" placeholder="Type or voice input\u2026" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" rows="1"></textarea>
|
|
211
|
-
<div class="float-bar-actions">
|
|
212
|
-
<button id="float-send" type="button" title="Send (Enter)">⏎</button>
|
|
213
|
-
</div>
|
|
214
|
-
</div>`}function i(t){return`(function() {
|
|
215
|
-
const micBtn = document.getElementById('mic-toggle');
|
|
181
|
+
</div>`}function r(t){return`(function() {
|
|
216
182
|
const typeBtn = document.getElementById('type-toggle');
|
|
217
183
|
const modal = document.getElementById('type-modal');
|
|
218
184
|
const backdrop = document.getElementById('type-backdrop');
|
|
@@ -241,7 +207,6 @@ import{icon as e}from"./icons.js";function n(){return`
|
|
|
241
207
|
function closeModal() {
|
|
242
208
|
modal.classList.remove('open');
|
|
243
209
|
backdrop.classList.remove('open');
|
|
244
|
-
stopRecognition();
|
|
245
210
|
}
|
|
246
211
|
|
|
247
212
|
function getActiveModifier() {
|
|
@@ -299,113 +264,35 @@ import{icon as e}from"./icons.js";function n(){return`
|
|
|
299
264
|
sendBtn.addEventListener('click', () => send(false));
|
|
300
265
|
sendEnterBtn.addEventListener('click', () => send(true));
|
|
301
266
|
|
|
302
|
-
// \u2500\u2500
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
let listening = false;
|
|
306
|
-
|
|
307
|
-
if (!SR) {
|
|
308
|
-
// Unsupported browser \u2014 hide the mic entirely.
|
|
309
|
-
micBtn.style.display = 'none';
|
|
310
|
-
} else {
|
|
311
|
-
micBtn.addEventListener('click', () => {
|
|
312
|
-
if (listening) { stopRecognition(); return; }
|
|
313
|
-
startRecognition();
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function startRecognition() {
|
|
318
|
-
if (!SR) return;
|
|
319
|
-
recognition = new SR();
|
|
320
|
-
recognition.lang = navigator.language || 'en-US';
|
|
321
|
-
recognition.interimResults = true;
|
|
322
|
-
recognition.continuous = false;
|
|
323
|
-
const baseText = input.value ? input.value + ' ' : '';
|
|
324
|
-
|
|
325
|
-
recognition.onstart = () => {
|
|
326
|
-
listening = true;
|
|
327
|
-
micBtn.classList.add('listening');
|
|
328
|
-
openModal();
|
|
329
|
-
setStatus('Listening\u2026');
|
|
330
|
-
};
|
|
331
|
-
recognition.onresult = (event) => {
|
|
332
|
-
let transcript = '';
|
|
333
|
-
for (let i = 0; i < event.results.length; i++) {
|
|
334
|
-
transcript += event.results[i][0].transcript;
|
|
335
|
-
}
|
|
336
|
-
input.value = baseText + transcript;
|
|
337
|
-
};
|
|
338
|
-
recognition.onerror = (event) => {
|
|
339
|
-
const code = event && event.error;
|
|
340
|
-
if (code === 'not-allowed' || code === 'service-not-allowed') {
|
|
341
|
-
setStatus('Microphone permission denied', true);
|
|
342
|
-
} else if (code === 'no-speech') {
|
|
343
|
-
setStatus('No speech detected', true);
|
|
344
|
-
} else {
|
|
345
|
-
setStatus('Voice input error', true);
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
recognition.onend = () => {
|
|
349
|
-
listening = false;
|
|
350
|
-
micBtn.classList.remove('listening');
|
|
351
|
-
if (!status.textContent || status.textContent === 'Listening\u2026') {
|
|
352
|
-
setStatus(input.value ? 'Review and send' : '');
|
|
353
|
-
}
|
|
354
|
-
recognition = null;
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
try {
|
|
358
|
-
recognition.start();
|
|
359
|
-
} catch (e) {
|
|
360
|
-
setStatus('Voice input unavailable', true);
|
|
361
|
-
listening = false;
|
|
362
|
-
micBtn.classList.remove('listening');
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function stopRecognition() {
|
|
367
|
-
if (recognition) {
|
|
368
|
-
try { recognition.stop(); } catch (e) { /* ignore */ }
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// \u2500\u2500 Floating input bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
373
|
-
const floatInput = document.getElementById('float-input');
|
|
374
|
-
const floatSend = document.getElementById('float-send');
|
|
267
|
+
// \u2500\u2500 Inline toolbar input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
268
|
+
const tbInput = document.getElementById('tb-input');
|
|
269
|
+
const tbSend = document.getElementById('tb-send');
|
|
375
270
|
|
|
376
|
-
if (
|
|
377
|
-
function
|
|
378
|
-
const text =
|
|
271
|
+
if (tbInput && tbSend) {
|
|
272
|
+
function sendTb() {
|
|
273
|
+
const text = tbInput.value;
|
|
379
274
|
if (text && window.tmuxWeb && window.tmuxWeb.sendInput) {
|
|
380
275
|
window.tmuxWeb.sendInput(text + '\r');
|
|
381
276
|
}
|
|
382
|
-
|
|
383
|
-
|
|
277
|
+
tbInput.value = '';
|
|
278
|
+
tbInput.rows = 1;
|
|
384
279
|
if (window.tmuxWeb && window.tmuxWeb.focusTerminal) window.tmuxWeb.focusTerminal();
|
|
385
280
|
}
|
|
386
281
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const lines = floatInput.value.split('
|
|
282
|
+
tbInput.addEventListener('input', () => {
|
|
283
|
+
tbInput.rows = 1;
|
|
284
|
+
const lines = tbInput.value.split('
|
|
391
285
|
').length;
|
|
392
|
-
|
|
286
|
+
tbInput.rows = Math.min(lines, 4);
|
|
393
287
|
});
|
|
394
288
|
|
|
395
|
-
|
|
396
|
-
floatInput.addEventListener('keydown', (e) => {
|
|
289
|
+
tbInput.addEventListener('keydown', (e) => {
|
|
397
290
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
398
291
|
e.preventDefault();
|
|
399
|
-
|
|
292
|
+
sendTb();
|
|
400
293
|
}
|
|
401
294
|
});
|
|
402
295
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
// Focus the float input when bar becomes visible (narrow screen)
|
|
406
|
-
const narrowMq = window.matchMedia('(max-width: 767px)');
|
|
407
|
-
narrowMq.addEventListener('change', (e) => {
|
|
408
|
-
if (e.matches) setTimeout(() => floatInput.focus(), 100);
|
|
409
|
-
});
|
|
296
|
+
tbSend.addEventListener('click', sendTb);
|
|
410
297
|
}
|
|
411
|
-
})();`}export{
|
|
298
|
+
})();`}export{n as mobileToolbarCSS,a as mobileToolbarHTML,r as mobileToolbarScript};
|
package/dist/lib/pages/shell.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cssVarsStyle as
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}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);
|
|
@@ -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?
|
|
330
|
+
${t?g():""}
|
|
305
331
|
${y()}
|
|
306
|
-
${
|
|
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:
|
|
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
|
-
${
|
|
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">${
|
|
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">${
|
|
356
|
-
<button class="mode-btn" id="mode-files" title="Files">${
|
|
357
|
-
<button class="mode-btn" id="mode-settings" title="Settings">${
|
|
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)">⏎</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>
|
|
@@ -444,7 +474,7 @@ import{cssVarsStyle as g}from"../theme.js";import{commandbarCSS as x,commandbarH
|
|
|
444
474
|
})();
|
|
445
475
|
</script>
|
|
446
476
|
|
|
447
|
-
${
|
|
477
|
+
${v()}
|
|
448
478
|
${t?m():""}
|
|
449
479
|
|
|
450
480
|
<script>
|
|
@@ -458,7 +488,7 @@ window.__onSessionCreated = async function(name) {
|
|
|
458
488
|
await window.__refreshSidebar();
|
|
459
489
|
window.__openSession(name);
|
|
460
490
|
};
|
|
461
|
-
${
|
|
491
|
+
${h("__onSessionCreated")}
|
|
462
492
|
</script>
|
|
463
493
|
<script>
|
|
464
494
|
// Sync app-layout height to visual viewport so mobile keyboard doesn't overlap
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as c}from"../html.js";import{notesDrawerCSS as v,notesDrawerHTML as y,notesDrawerScript as $}from"../notes-drawer.js";import{schedulerDrawerCSS as k,schedulerDrawerHTML as S,schedulerDrawerScript as L}from"../scheduler-drawer.js";import{windowsDrawerCSS as D,windowsDrawerHTML as E,windowsDrawerScript as F}from"../windows-drawer.js";import{sessionsDrawerCSS as M,sessionsDrawerHTML as I,sessionsDrawerScript as T}from"../sessions-drawer.js";import{mobileToolbarCSS as
|
|
1
|
+
import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as c}from"../html.js";import{notesDrawerCSS as v,notesDrawerHTML as y,notesDrawerScript as $}from"../notes-drawer.js";import{schedulerDrawerCSS as k,schedulerDrawerHTML as S,schedulerDrawerScript as L}from"../scheduler-drawer.js";import{windowsDrawerCSS as D,windowsDrawerHTML as E,windowsDrawerScript as F}from"../windows-drawer.js";import{sessionsDrawerCSS as M,sessionsDrawerHTML as I,sessionsDrawerScript as T}from"../sessions-drawer.js";import{mobileToolbarCSS as C,mobileToolbarHTML as B,mobileToolbarScript as O}from"../mobile-toolbar.js";import{icon as m,extIcon as W}from"../icons.js";import{commandbarButtonHTML as z,commandbarCSS as H,commandbarHTML as P,commandbarScript as R}from"../commandbar.js";import{drawerResizeCSS as A,drawerResizeHandleHTML as h,drawerResizeScript as x}from"../drawer-resize.js";import{themeSwitcherButtonHTML as j,themeSwitcherScript as J}from"../shared-layout.js";function _(){return`
|
|
2
2
|
.ext-backdrop {
|
|
3
3
|
position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 999;
|
|
4
4
|
opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
|
|
@@ -18,7 +18,7 @@ import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as
|
|
|
18
18
|
transform: translateX(100%); transition: transform 0.25s ease;
|
|
19
19
|
}
|
|
20
20
|
.ext-panel.open { transform: translateX(0); }
|
|
21
|
-
${
|
|
21
|
+
${A()}
|
|
22
22
|
.ext-drawer .drawer-header,
|
|
23
23
|
.ext-panel .drawer-header {
|
|
24
24
|
display: flex; justify-content: space-between; align-items: center;
|
|
@@ -35,25 +35,25 @@ import{cssVarsStyle as w}from"../theme.js";import{escapeHtml as i,escapeAttr as
|
|
|
35
35
|
.ext-panel .drawer-header button:hover { color: var(--panel-accent); }
|
|
36
36
|
.ext-drawer iframe { flex: 1; border: none; width: 100%; height: 0; }
|
|
37
37
|
.ext-panel iframe { flex: 1; border: none; width: 100%; height: 100%; }
|
|
38
|
-
`}function
|
|
39
|
-
<div id="ext-${
|
|
40
|
-
<div id="ext-${
|
|
38
|
+
`}function U(e){const n=e.id;return`
|
|
39
|
+
<div id="ext-${n}-backdrop" class="ext-backdrop"></div>
|
|
40
|
+
<div id="ext-${n}-drawer" class="ext-drawer resizable-drawer">
|
|
41
41
|
${h()}
|
|
42
42
|
<div class="drawer-header">
|
|
43
43
|
<span>${i(e.icon)} ${i(e.name)}</span>
|
|
44
|
-
<button id="ext-${
|
|
44
|
+
<button id="ext-${n}-close">×</button>
|
|
45
45
|
</div>
|
|
46
|
-
<iframe id="ext-${
|
|
47
|
-
</div>`}function
|
|
48
|
-
<div id="ext-${
|
|
49
|
-
<div id="ext-${
|
|
46
|
+
<iframe id="ext-${n}-frame" src="/ext/${n}/ui/index.html"></iframe>
|
|
47
|
+
</div>`}function X(e){const n=e.id,t=e.panel;if(!t)return"";const r=t.title??e.name;return`
|
|
48
|
+
<div id="ext-${n}-panel-backdrop" class="ext-backdrop"></div>
|
|
49
|
+
<div id="ext-${n}-panel" class="ext-panel resizable-drawer">
|
|
50
50
|
${h()}
|
|
51
51
|
<div class="drawer-header">
|
|
52
52
|
<span>${i(e.icon)} ${i(r)}</span>
|
|
53
|
-
<button id="ext-${
|
|
53
|
+
<button id="ext-${n}-panel-close">×</button>
|
|
54
54
|
</div>
|
|
55
|
-
<iframe id="ext-${
|
|
56
|
-
</div>`}function
|
|
55
|
+
<iframe id="ext-${n}-panel-frame" src="/ext/${n}/ui/${c(t.entry)}"></iframe>
|
|
56
|
+
</div>`}function V(e,n){const t=e.id,r=JSON.stringify(e.config),o=e.panel?.entry??null,d=e.panel?.defaultWidth??960;return`
|
|
57
57
|
${x(`ext-${t}-drawer`,`tmux-web:drawer-width:ext:${t}`,360)}
|
|
58
58
|
${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
|
|
59
59
|
(function() {
|
|
@@ -113,7 +113,7 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
|
|
|
113
113
|
|
|
114
114
|
function sendMessages(targetFrame) {
|
|
115
115
|
if (!targetFrame || !targetFrame.contentWindow) return;
|
|
116
|
-
targetFrame.contentWindow.postMessage({ type: 'ext:context', context: { session: ${JSON.stringify(
|
|
116
|
+
targetFrame.contentWindow.postMessage({ type: 'ext:context', context: { session: ${JSON.stringify(n)}, host: location.origin } }, '*');
|
|
117
117
|
targetFrame.contentWindow.postMessage({ type: 'ext:config', config: cfg }, '*');
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -150,7 +150,7 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
|
|
|
150
150
|
sendMessages(panelFrame);
|
|
151
151
|
}
|
|
152
152
|
});
|
|
153
|
-
}());`}function
|
|
153
|
+
}());`}function re(e,n=[],t){const r=n.filter(a=>a.slot==="sidebar"),{commandbarEnabled:o=!1,commandbarSessions:d=[],quickCommands:g=[],agentsEnabled:b=!1,theme:s,renderer:f="xterm"}=t,l=t.terminal??{initialLines:1e3,historyChunk:500,syncIdleMs:200,syncMaxMs:3e3},u=l.initialLines+2*l.historyChunk,p=[{label:"Switch window",meta:`Windows in ${e}`,subView:"windows"},{label:"Quick Commands",meta:"Paste configured command",subView:"quickCommands"},{label:"Send Command",meta:"Send input to active window",clickTargetId:"type-toggle"},{label:"Open sessions sidebar",meta:"Recent and pinned sessions",clickTargetId:"sessions-toggle"},{label:"Open notes",meta:`Notes for ${e}`,clickTargetId:"notes-toggle"},{label:"Open scheduler",meta:`Schedule command in ${e}`,clickTargetId:"sched-toggle"}];return b&&p.push({label:"View All Agents",meta:"Running agents",href:"/agents"}),`<!DOCTYPE html>
|
|
154
154
|
<html lang="en">
|
|
155
155
|
<head>
|
|
156
156
|
<meta charset="UTF-8" />
|
|
@@ -266,13 +266,13 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
|
|
|
266
266
|
@media (max-width: 380px) {
|
|
267
267
|
header .status { display: none; }
|
|
268
268
|
}
|
|
269
|
-
${o?
|
|
269
|
+
${o?H():""}
|
|
270
270
|
${v()}
|
|
271
271
|
${k()}
|
|
272
272
|
${D()}
|
|
273
273
|
${M()}
|
|
274
|
-
${
|
|
275
|
-
${
|
|
274
|
+
${C()}
|
|
275
|
+
${_()}
|
|
276
276
|
@media (prefers-reduced-motion: reduce) {
|
|
277
277
|
*, *::before, *::after {
|
|
278
278
|
animation-duration: 0.01ms !important;
|
|
@@ -287,33 +287,32 @@ ${o?x(`ext-${t}-panel`,`tmux-web:panel-width:ext:${t}`,d):""}
|
|
|
287
287
|
<header>
|
|
288
288
|
<div class="brand"><a href="/" aria-label="Go to home">tmux<span>-weblink</span></a></div>
|
|
289
289
|
<span class="session">${i(e)}</span>
|
|
290
|
-
${o?
|
|
290
|
+
${o?z("Sessions"):""}
|
|
291
291
|
<button class="notes-btn" id="notes-toggle" title="Session notes" aria-label="Session notes">
|
|
292
292
|
${m("notes")}
|
|
293
293
|
</button>
|
|
294
294
|
<button class="sched-btn" id="sched-toggle" title="Schedule command" aria-label="Schedule command">
|
|
295
295
|
${m("schedule")}
|
|
296
296
|
</button>
|
|
297
|
-
${r.map(
|
|
297
|
+
${r.map(a=>`<button class="ext-btn" id="ext-${a.id}-toggle" title="${c(a.name)}" aria-label="${c(a.name)}">${W(a.id,a.icon)}</button>`).join(`
|
|
298
298
|
`)}
|
|
299
|
-
${
|
|
299
|
+
${j(s.template)}
|
|
300
300
|
<div class="status">
|
|
301
301
|
<div class="dot" id="status-dot"></div>
|
|
302
302
|
<span id="status-text">connecting</span>
|
|
303
303
|
</div>
|
|
304
304
|
</header>
|
|
305
|
-
<script>${
|
|
305
|
+
<script>${J()}</script>
|
|
306
306
|
<div id="terminal-container" class="terminal-pending"></div>
|
|
307
|
-
${
|
|
308
|
-
${
|
|
309
|
-
${o?R():""}
|
|
307
|
+
${B()}
|
|
308
|
+
${o?P():""}
|
|
310
309
|
${y(`Notes - ${e}`)}
|
|
311
310
|
${S(`Scheduler - ${e}`)}
|
|
312
311
|
${E(`Windows - ${e}`)}
|
|
313
312
|
${I()}
|
|
314
|
-
${r.map(
|
|
313
|
+
${r.map(a=>U(a)).join(`
|
|
315
314
|
`)}
|
|
316
|
-
${r.map(
|
|
315
|
+
${r.map(a=>X(a)).join(`
|
|
317
316
|
`)}
|
|
318
317
|
|
|
319
318
|
<script type="module">
|
|
@@ -349,7 +348,7 @@ ${T(e)}
|
|
|
349
348
|
${O(e)}
|
|
350
349
|
|
|
351
350
|
// ========== COMMANDBAR ==========
|
|
352
|
-
${o?
|
|
351
|
+
${o?R(d,p,{sessionName:e},g):""}
|
|
353
352
|
|
|
354
353
|
// ========== NOTES ==========
|
|
355
354
|
// (notes and scheduler scripts already included above; extensions below)
|
|
@@ -358,8 +357,8 @@ ${o?A(d,p,{sessionName:e},g):""}
|
|
|
358
357
|
${r.length>0?`<script>
|
|
359
358
|
// Extension bootstrap: plain script (not module) so it runs before the module
|
|
360
359
|
// awaits terminal-client import, avoiding a race where iframes load during that await.
|
|
361
|
-
${r.map(
|
|
360
|
+
${r.map(a=>V(a,e)).join(`
|
|
362
361
|
`)}
|
|
363
362
|
</script>`:""}
|
|
364
363
|
</body>
|
|
365
|
-
</html>`}export{
|
|
364
|
+
</html>`}export{re as renderTerminal};
|