tmux-weblink 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/dist/assets/addon-fit-D5N335P2.js +16 -0
- package/dist/assets/terminal-client.js +4 -0
- package/dist/assets/xterm-NI46XXQY.js +51 -0
- package/dist/assets/xterm.css +285 -0
- package/dist/browser/terminal-client.js +4 -0
- package/dist/frontend.js +1 -0
- package/dist/index.js +12 -0
- package/dist/lib/agent-detect.js +3 -0
- package/dist/lib/agents-watch.js +2 -0
- package/dist/lib/atomicWrite.js +1 -0
- package/dist/lib/auditLog.js +1 -0
- package/dist/lib/auth.js +2 -0
- package/dist/lib/cli.js +32 -0
- package/dist/lib/commandbar.js +555 -0
- package/dist/lib/db.js +1 -0
- package/dist/lib/drawer-resize.js +166 -0
- package/dist/lib/drawer-script.js +8 -0
- package/dist/lib/ext-loader.js +1 -0
- package/dist/lib/html.js +1 -0
- package/dist/lib/icons.js +1 -0
- package/dist/lib/image-upload.js +1 -0
- package/dist/lib/load-env.js +6 -0
- package/dist/lib/mobile-toolbar.js +313 -0
- package/dist/lib/notes-db.js +18 -0
- package/dist/lib/notes-drawer.js +210 -0
- package/dist/lib/notes-utils.js +15 -0
- package/dist/lib/pages/agents-index.js +105 -0
- package/dist/lib/pages/history-index.js +85 -0
- package/dist/lib/pages/landing.js +78 -0
- package/dist/lib/pages/login.js +191 -0
- package/dist/lib/pages/notes-index.js +65 -0
- package/dist/lib/pages/notes-page.js +158 -0
- package/dist/lib/pages/quick-commands.js +361 -0
- package/dist/lib/pages/schedule-index.js +392 -0
- package/dist/lib/pages/settings.js +206 -0
- package/dist/lib/pages/terminal.js +364 -0
- package/dist/lib/pinned-views.js +1 -0
- package/dist/lib/plugins.js +3 -0
- package/dist/lib/quick-commands.js +1 -0
- package/dist/lib/rateLimiter.js +2 -0
- package/dist/lib/schedule-delay.js +9 -0
- package/dist/lib/scheduler-drawer.js +379 -0
- package/dist/lib/scheduler.js +1 -0
- package/dist/lib/security-config.js +2 -0
- package/dist/lib/session-access.js +1 -0
- package/dist/lib/session-windows.js +1 -0
- package/dist/lib/sessions-drawer.js +634 -0
- package/dist/lib/sessions-sidebar.js +1 -0
- package/dist/lib/settings.js +2 -0
- package/dist/lib/setup-features.js +1 -0
- package/dist/lib/setup-prompts.js +3 -0
- package/dist/lib/shared-layout.js +412 -0
- package/dist/lib/state-paths.js +1 -0
- package/dist/lib/terminal-config.js +1 -0
- package/dist/lib/theme-store.js +2 -0
- package/dist/lib/theme.js +28 -0
- package/dist/lib/themes/dark-cove.js +1 -0
- package/dist/lib/themes/ghostty.js +1 -0
- package/dist/lib/themes/index.js +1 -0
- package/dist/lib/themes/types.js +0 -0
- package/dist/lib/themes/vscode.js +1 -0
- package/dist/lib/themes/warm-clay.js +1 -0
- package/dist/lib/tmux-capture.js +5 -0
- package/dist/lib/tmux-control.js +2 -0
- package/dist/lib/tmux-panes.js +2 -0
- package/dist/lib/tmux-windows.js +4 -0
- package/dist/lib/watched-panes.js +1 -0
- package/dist/lib/window-history.js +1 -0
- package/dist/lib/window-labels.js +1 -0
- package/dist/lib/windows-drawer.js +364 -0
- package/dist/lib/ws-message.js +1 -0
- package/dist/sessions.js +2 -0
- package/package.json +62 -0
- package/scripts/fix-pty-perms.mjs +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# tmux-weblink
|
|
2
|
+
|
|
3
|
+
Access your tmux sessions from the browser. A lightweight web server that lists running tmux sessions and lets you attach through a full terminal in your browser — with built-in notes, a scheduler, and sidebar extensions.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g tmux-weblink
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx tmux-weblink
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Interactive setup (command bar, agents page)
|
|
21
|
+
tmux-weblink setup
|
|
22
|
+
|
|
23
|
+
# Start on default port 3000
|
|
24
|
+
tmux-weblink
|
|
25
|
+
|
|
26
|
+
# Custom port
|
|
27
|
+
PORT=8080 tmux-weblink
|
|
28
|
+
|
|
29
|
+
# Use the default xterm.js renderer
|
|
30
|
+
tmux-weblink
|
|
31
|
+
|
|
32
|
+
# Optional: use ghostty-web for terminal rendering
|
|
33
|
+
tmux-weblink --ghostty
|
|
34
|
+
|
|
35
|
+
# Equivalent environment override
|
|
36
|
+
TMUX_WEB_TERMINAL_RENDERER=ghostty tmux-weblink
|
|
37
|
+
|
|
38
|
+
# Optional: tail-first buffer loading (see docs/architecture.md)
|
|
39
|
+
TMUX_WEB_INITIAL_LINES=1000 TMUX_WEB_HISTORY_CHUNK=500 tmux-weblink
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Then open `http://localhost:3000` in your browser. You'll see a list of active tmux sessions — click one to attach.
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
- [Documentation hub](docs/index.md)
|
|
47
|
+
- [Notes](docs/notes.md) — per-session Markdown scratchpad
|
|
48
|
+
- [Scheduler](docs/scheduler.md) — delayed `send-keys` tasks
|
|
49
|
+
- **Windows drawer** — switch tmux windows from the terminal header (mobile-friendly tab picker)
|
|
50
|
+
- [Extensions](docs/extensions.md) — install, configure, and build sidebar plugins
|
|
51
|
+
- [Architecture](docs/architecture.md) — how the server, terminal, and extensions connect
|
|
52
|
+
|
|
53
|
+
## Prerequisites
|
|
54
|
+
|
|
55
|
+
- **Node.js** >= 22
|
|
56
|
+
- **tmux** installed and available in your PATH
|
|
57
|
+
- Writable `~/.tmux-web/` and `~/.config/tmux-web/` (see [docs](docs/index.md) for dev-mode paths)
|
|
58
|
+
|
|
59
|
+
## Credits
|
|
60
|
+
|
|
61
|
+
This project is built on top of ideas and code from:
|
|
62
|
+
|
|
63
|
+
- [tmux-web](https://github.com/ashutoshpw/tmux-web) by [@ashutoshpw](https://github.com/ashutoshpw)
|
|
64
|
+
- [persalink](https://github.com/brobata/persalink) by [@brobata](https://github.com/brobata)
|
|
65
|
+
|
|
66
|
+
It is a continuation and re-packaging of those experiments, focused on making a single-person, browser-first tmux companion that is easy to install and run.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var p=2,c=1,d=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let i=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(i._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let i=this._terminal.options.scrollback===0?0:this._terminal.options.overviewRuler?.width||14,s=window.getComputedStyle(this._terminal.element.parentElement),l=parseInt(s.getPropertyValue("height")),o=Math.max(0,parseInt(s.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r={top:parseInt(t.getPropertyValue("padding-top")),bottom:parseInt(t.getPropertyValue("padding-bottom")),right:parseInt(t.getPropertyValue("padding-right")),left:parseInt(t.getPropertyValue("padding-left"))},a=r.top+r.bottom,n=r.right+r.left,h=l-a,m=o-n-i;return{cols:Math.max(p,Math.floor(m/e.css.cell.width)),rows:Math.max(c,Math.floor(h/e.css.cell.height))}}};/*! Bundled license information:
|
|
2
|
+
|
|
3
|
+
@xterm/addon-fit/lib/addon-fit.mjs:
|
|
4
|
+
(**
|
|
5
|
+
* Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
9
|
+
* @license MIT
|
|
10
|
+
*
|
|
11
|
+
* Originally forked from (with the author's permission):
|
|
12
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
13
|
+
* http://bellard.org/jslinux/
|
|
14
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
15
|
+
*)
|
|
16
|
+
*/export{d as FitAddon};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var O="https://esm.sh/ghostty-web@0.4.0",y;function U(){return y||(document.querySelector("link[data-tmux-web-xterm-css]")?y=Promise.resolve():(y=new Promise(t=>{let e=document.createElement("link");e.rel="stylesheet",e.href="/assets/xterm.css",e.setAttribute("data-tmux-web-xterm-css",""),e.addEventListener("load",()=>t(),{once:!0}),e.addEventListener("error",()=>t(),{once:!0}),document.head.appendChild(e)}),y))}var J=class j{terminal;fitAddon;container;constructor(e,n,r){this.container=e,this.terminal=n,this.fitAddon=r,this.terminal.loadAddon(this.fitAddon),this.terminal.open(e)}static async create(e,n,r){let[{Terminal:o},{FitAddon:h}]=await Promise.all([import("./xterm-NI46XXQY.js"),import("./addon-fit-D5N335P2.js"),U()]),g=new o({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new j(e,g,new h)}get cols(){return this.terminal.cols}get rows(){return this.terminal.rows}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom()}scrollToLine(e){this.terminal.scrollToLine(Math.max(0,e))}isNearScrollbackTop(){return this.terminal.buffer.active.viewportY<=1}viewportY(){return this.terminal.buffer.active.viewportY}baseY(){return this.terminal.buffer.active.baseY}fit(){let e=V(this.container);return e.width<=0||e.height<=0?!1:(this.fitAddon.fit(),!0)}focus(){this.terminal.focus()}pasteText(e){this.terminal.paste(e)}input(e){this.terminal.input(e,!1)}onData(e){this.terminal.onData(e),this.terminal.onBinary(e)}onResize(e){this.terminal.onResize(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||this.container.contains(e))}},G=class q{terminal;container;colsValue=80;rowsValue=24;charW=0;charH=0;resizeCallbacks=[];constructor(e,n){this.container=e,this.terminal=n,this.terminal.open(e)}static async create(e,n,r){let o=await import(O);await o.init();let h=new o.Terminal({fontSize:14,fontFamily:"'JetBrains Mono', 'SF Mono', 'Menlo', monospace",cursorBlink:!0,cursorStyle:"bar",scrollback:n,convertEol:!1,theme:r});return new q(e,h)}get cols(){return this.colsValue}get rows(){return this.rowsValue}write(e){return new Promise(n=>this.terminal.write(e,n))}reset(){this.terminal.reset()}scrollToBottom(){this.terminal.scrollToBottom?.()}scrollToLine(e){this.terminal.scrollToLine?.(Math.max(0,e))}isNearScrollbackTop(){let e=this.terminal.buffer?.active;return e?e.viewportY>=e.baseY:!1}viewportY(){return this.terminal.buffer?.active?.viewportY??0}baseY(){return this.terminal.buffer?.active?.baseY??0}fit(){let e=V(this.container);if(e.width<=0||e.height<=0)return!1;this.updateCellMetrics(!this.charW||!this.charH);let n=Math.floor(e.width/this.charW),r=Math.floor(e.height/this.charH);if(n<10||r<5)return!1;if(n!==this.colsValue||r!==this.rowsValue){this.colsValue=n,this.rowsValue=r,this.terminal.resize(n,r);for(let o of this.resizeCallbacks)o({cols:n,rows:r})}return!0}focus(){(this.terminal.textarea||this.container.querySelector("textarea")||this.container)?.focus?.()}pasteText(e){let n=this.terminal.getMode?.(2004)??!1;this.input(n?"\x1B[200~"+e+"\x1B[201~":e)}input(e){this.terminal.input?.(e,!1)}onData(e){this.terminal.onData(n=>e(n))}onResize(e){this.resizeCallbacks.push(e)}onScroll(e){this.terminal.onScroll(e)}attachCustomKeyEventHandler(e){this.terminal.attachCustomKeyEventHandler(e)}isFocused(){let e=document.activeElement;return!!e&&(e===this.container||e===this.terminal.textarea||this.container.contains(e))}updateCellMetrics(e=!1){let n=this.container.querySelector("canvas");if(n instanceof HTMLElement&&n.offsetWidth>0&&n.offsetHeight>0&&this.colsValue>0&&this.rowsValue>0){let r=n.offsetWidth/this.colsValue,o=n.offsetHeight/this.rowsValue;(e||!this.charW||!this.charH)&&(this.charW=r,this.charH=o)}(!this.charW||!this.charH)&&(this.charW=9,this.charH=18)}},A=window.__TMUX_WEB_TERMINAL__;if(!A)throw new Error("missing tmux-web terminal config");var u=A,W=document.getElementById("terminal-container");if(!W)throw new Error("missing terminal container");var i=W;function Q(){let t=document.createElement("div");t.textContent="ghostty-web unavailable \u2014 using xterm.js",t.style.cssText='position:fixed;bottom:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#fbbf24;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(251,191,36,0.4);border-radius:6px;',document.body.appendChild(t),setTimeout(()=>t.remove(),6e3)}async function Z(){if(u.renderer==="ghostty")try{return await G.create(i,u.scrollback,u.theme)}catch(t){console.error("[tmux-web] ghostty-web failed to load; falling back to xterm.js:",t),Q()}return J.create(i,u.scrollback,u.theme)}var a=await Z(),c,b=0,Y,s=null,C=0,d="connecting",x=0,E=!1,T=[],m="";function $(){return T.join("")+m}async function ee(t,e=0){let n=t?a.viewportY():0,r=t?a.baseY():0,o=$();if(a.reset(),!o){t||a.scrollToBottom();return}await a.write(o),t?a.scrollToLine(r+n+e):a.scrollToBottom()}function te(t){let e;try{e=JSON.parse(t)}catch{d==="live"&&(m+=t,a.write(t));return}if(e.type==="auth.required"){let n=localStorage.getItem("tmux-web-token");n?w({type:"auth.token",token:n}):location.href="/login?returnTo="+encodeURIComponent(location.pathname+location.search);return}if(e.type==="auth.ok"){e.token&&localStorage.setItem("tmux-web-token",e.token),S(!0),f=1e3,p(),w({type:"resize",cols:a.cols,rows:a.rows}),l();return}if(e.type==="auth.failed"){if(S(!1),e.permanentLock||e.retryAfterMs&&e.retryAfterMs>6e4){localStorage.removeItem("tmux-web-token"),location.href="/login?error="+encodeURIComponent(e.message);return}let n=document.createElement("div");n.textContent="Auth failed: "+e.message,n.style.cssText='position:fixed;top:12px;right:12px;z-index:2000;background:rgba(0,0,0,0.85);color:#f87171;font:12px/1.4 "JetBrains Mono",monospace;padding:8px 12px;border:1px solid rgba(248,113,113,0.4);border-radius:6px;',document.body.appendChild(n),setTimeout(()=>n.remove(),4e3);return}if(e.type==="snapshot"&&typeof e.data=="string"){T=[e.data],m="",x=typeof e.lines=="number"?e.lines:u.terminal.initialLines,d="live",a.reset(),a.write(e.data).then(()=>a.scrollToBottom());return}if(e.type==="window_changed"&&typeof e.activeIndex=="number"){let n=new URL(location.href);n.searchParams.get("window")!==String(e.activeIndex)&&(n.searchParams.set("window",String(e.activeIndex)),history.replaceState(history.state,"",n)),window.dispatchEvent(new CustomEvent("tmux:windows",{detail:e.windows}));return}if(e.type==="history"&&typeof e.data=="string"){E=!1,e.lines>0&&e.data&&(T.unshift(e.data),x+=e.lines,ee(!0,e.lines));return}if(e.type==="data"&&typeof e.data=="string"){if(d==="connecting"){d="live",m=e.data,a.write(e.data);return}d==="live"&&(m+=e.data,a.write(e.data))}}function V(t){let e=t.getBoundingClientRect(),n=window.visualViewport;return n?{width:Math.min(e.width,n.width),height:Math.max(0,Math.min(e.height,n.height-Math.max(0,e.top)))}:e}function p(){return a.fit()}function B(){i.classList.remove("terminal-pending")}function l(){b&&cancelAnimationFrame(b),Y&&clearTimeout(Y),p(),b=requestAnimationFrame(()=>{b=0,p()&&B()}),Y=setTimeout(()=>{p()&&B()},120)}function H(){l(),setTimeout(l,50),setTimeout(l,150),setTimeout(l,300)}var F=document.getElementById("status-dot"),k=document.getElementById("status-text");function S(t){F&&(F.className="dot"+(t?" connected":"")),k&&(k.textContent=t?"connected":"reconnecting")}var ne=location.protocol==="https:"?"wss:":"ws:",re=ne+"//"+location.host+"/ws/"+encodeURIComponent(u.sessionName),ae="/api/session/"+encodeURIComponent(u.sessionName)+"/upload",f=1e3,ie=/^((?!chrome|android|crios|fxios|edgios).)*safari/i.test(navigator.userAgent);function w(t){c?.readyState===WebSocket.OPEN&&c.send(JSON.stringify(t))}function R(){let t=re,e=localStorage.getItem("tmux-web-token");e&&(t+=(t.includes("?")?"&":"?")+"token="+encodeURIComponent(e)),c=new WebSocket(t),c.onopen=()=>{d="connecting",x=0,E=!1,T=[],m="",a.reset(),f=1e3},c.onmessage=n=>{typeof n.data=="string"&&te(n.data)},c.onclose=()=>{S(!1),d="connecting",setTimeout(()=>{f=Math.min(f*2,1e4),R()},f)},c.onerror=()=>c?.close()}function L(t){w({type:"input",data:t})}function oe(t){L(t)}function X(t){return t.split(`\r
|
|
2
|
+
`).join(`
|
|
3
|
+
`).split(`
|
|
4
|
+
`).join("\r")}function K(t){return typeof t=="string"&&t.startsWith("image/")}function N(t){return t?.files?.length?t.files[0]??null:null}function se(t){if(!t?.items)return null;for(let e=0;e<t.items.length;e++){let n=t.items[e];if(n?.kind==="file"&&K(n.type))return n.getAsFile()}return null}function M(t){t&&k?k.textContent=t:S(c?.readyState===WebSocket.OPEN)}async function le(t){let e=new FormData,n=t instanceof File&&t.name?t.name:"upload";e.append("file",t,n);let r=localStorage.getItem("tmux-web-token"),o={};r&&(o.Authorization="Bearer "+r);let h=await fetch(ae,{method:"POST",body:e,headers:o});if(!h.ok){let I="upload failed";try{let z=await h.json();z.error&&(I=z.error)}catch{}throw new Error(I)}let g=await h.json();if(!g.path)throw new Error("no path in response");return g.path}async function P(t){try{M("uploading...");let e=await le(t);oe(e),M(null)}catch(e){console.warn("image upload:",e),M("upload failed"),setTimeout(()=>M(null),2e3)}}a.onData(t=>L(t)),a.onResize(({cols:t,rows:e})=>w({type:"resize",cols:t,rows:e})),window.tmuxWeb={sendInput:L,focusTerminal:_},a.onScroll(()=>{d!=="live"||E||!a.isNearScrollbackTop()||(E=!0,w({type:"load_history",before:x}))}),a.attachCustomKeyEventHandler(t=>t.type!=="keydown"?!0:(t.ctrlKey||t.metaKey)&&t.code==="KeyV"?(t.preventDefault(),(async()=>{try{if(navigator.clipboard?.read){let e=await navigator.clipboard.read();for(let n of e)for(let r of n.types)if(K(r)){let o=await n.getType(r);await P(o);return}}}catch{}try{let e=await navigator.clipboard?.readText();e&&a.pasteText(X(e))}catch{}})(),!1):!0);var D=0;async function ce(t){let e=se(t.clipboardData);if(e){t.preventDefault(),t.stopPropagation();let o=Date.now();if(o-D<50)return;D=o,await P(e);return}let n=t.clipboardData?.getData("text/plain");if(!n)return;t.preventDefault(),t.stopPropagation();let r=Date.now();r-D<50||(D=r,a.pasteText(X(n)))}i.addEventListener("paste",ce);var v=0;i.addEventListener("dragenter",t=>{t.preventDefault(),v++,N(t.dataTransfer)&&i.classList.add("terminal-drag-over")}),i.addEventListener("dragover",t=>{t.preventDefault(),t.dataTransfer&&(t.dataTransfer.dropEffect="copy")}),i.addEventListener("dragleave",t=>{t.preventDefault(),v--,v<=0&&(v=0,i.classList.remove("terminal-drag-over"))}),i.addEventListener("drop",async t=>{t.preventDefault(),v=0,i.classList.remove("terminal-drag-over");let e=N(t.dataTransfer);e&&await P(e)}),document.addEventListener("keydown",t=>{!ie||t.key!=="Escape"||t.defaultPrevented||t.repeat||t.metaKey||t.ctrlKey||t.altKey||a.isFocused()&&(t.preventDefault(),t.stopPropagation(),L("\x1B"))},!0);function ue(t,e,n){(i.querySelector(".xterm-screen")??i).dispatchEvent(new WheelEvent("wheel",{deltaY:t,deltaMode:WheelEvent.DOM_DELTA_PIXEL,clientX:e,clientY:n,bubbles:!0,cancelable:!0}))}function _(){a.focus(),H()}i.addEventListener("touchstart",t=>{if(t.touches.length!==1){s=null;return}let e=t.touches[0];e&&(s={startX:e.clientX,startY:e.clientY,lastX:e.clientX,lastY:e.clientY,scrolling:!1},t.stopPropagation())},{passive:!0,capture:!0}),i.addEventListener("touchmove",t=>{if(!s||t.touches.length!==1)return;let e=t.touches[0];if(!e)return;let n=e.clientY-s.startY,r=e.clientX-s.startX;if(!s.scrolling){if(Math.abs(n)<8||Math.abs(n)<Math.abs(r))return;s.scrolling=!0,C=Date.now()+500}t.preventDefault(),t.stopPropagation(),ue(-(e.clientY-s.lastY),e.clientX,e.clientY),s.lastX=e.clientX,s.lastY=e.clientY},{passive:!1,capture:!0}),i.addEventListener("touchend",t=>{if(!s)return;let e=s.scrolling;s=null,t.stopPropagation(),e?(C=Date.now()+500,t.preventDefault()):_()},{passive:!1,capture:!0}),i.addEventListener("touchcancel",t=>{s=null,t.stopPropagation()},{passive:!0,capture:!0}),i.addEventListener("pointerup",t=>{t.pointerType==="touch"&&Date.now()<C&&(t.preventDefault(),t.stopPropagation())},!0),window.addEventListener("resize",l),window.visualViewport?.addEventListener("resize",l),window.visualViewport?.addEventListener("scroll",l),new ResizeObserver(l).observe(i),document.fonts?.ready.then(()=>{l(),setTimeout(l,50),setTimeout(l,200)}),i.addEventListener("focusin",H,!0),requestAnimationFrame(()=>{p()&&B(),R(),l()});
|