Files
Aram DrevekeninandThomas Linford c5ac796880 Feature: web-client/server to share your sessions in the browser (#4242)
* work

* moar work

* notes

* work

* separate to terminal and control channels

* stdin working

* serve html web client initial

* serve static assets loaded with include_dir

* merge

* enable_web_server config parameter

* compile time flag to disable web server capability

* rustfmt

* add license to all xterm.js assets

* mouse working except copy/paste

* helpful comment

* web client improvements

- move script to js file
- add favicon
- add nerd font
- change title

TODO: investigate if font license embedded in otf is sufficient

* get mouse to work properly

* kitty keyboard support initial

* fix wrong type in preload link

* wip axum websocket handlers

- upgrade axum to v0.8.1, enable ws feature
- begin setup of websocket handlers
- tidy up imports

* replace control listener

* handle terminal websocket with axum

* cleanup Cargo.toml

* kitty fixes and bracketed paste

* fix(mouse): pane not found crash

* initial session switching infra

* add `web_client_font` option

* session switching, creation and resurrection working through the session manager

* move session module to zellij-utils and share logic with web-client

* some cleanups

* require restart for enable-web-server

* use session name from router

* write config to disk and watch for config changes

* rename session name to ipc path

* add basic panic handler, make render_to_client exit on channel close

* use while let instead of loop

* handle websocket close

* add mouse motions

* make clipboard work

* add weblink handling and webgl rendering

* add todo

* fix: use session name instead of patch on session switch

* use "default" layout for new sessions

* ui indication for session being shared

* share this session ui

* plugin assets

* Fix process crash on mac with notify watcher.

Use poll watcher instead of recommended as a workaround.

* make url session switching and creation work

* start welcome screen on root url

* scaffold control messages, set font from config

* set dimensions on session start

* bring back session name from url

* send bytes on terminal websocket instead of json

- create web client os input and id before websocket connection

* draft ui

* work

* refactor ui

* remove otf font, remove margins to avoid scrollbar

* version query endpoint for server status

* web session info query endpoint

* refactor: move stuff around

* add web client info to session metadata

* make tests pass

* populate real data in session list

* remove unnecessary endpoint

* add web_client node to config, add font option

* remove web_client_font

* allow disabling the web session through the config - WIP

* formalize sharing/not-sharing configuration

* fix tests

* allow shutting down web server

* display error when web clients are forbidden to attach

* only show sessions that allow web clients if this is a web client

* style(fmt): rustfmt

* fix: query web server from Zellij rather than from each plugin

* remove log spam

* handle some error paths better in the web client

* allow controlling the web server through the cli

* allow configuring the web server's ip/port

* fix tests and format code

* use direct WebServerStatus event instead of piggy-backing on SessionInfo

* plugin revamp initial

* make plugin responsive

* adjust plugin title

* refactor: share plugin

* refactor: share plugin

* add cors middleware

* some fixes for running without a compiled web server capability

* display error when starting the share plugin without web server support

* clarify config

* add pipelines to compile zellij without web support

* display error when unable to start web server

* only query web server when share plugin is running

* refactor(web-client): connection table

* give zellij_server_listener access to the control channel

* fixes and clarifications

* refactor: consolidate generate_unique_session_name

* give proper error when trying to attach to a forbidden session

* change browser URL when switching sessions

* add keyboard shortcut

* enforce https when bound to non-loopback ip

* initial authentication token implementation

* background color from theme

* initial web client theme config

* basic token generation ui

* refactor set config message creation

* also set body background

* allow editing scrollback for plugins too

* set scrollback to 0

* properly parse colors in config

* generate token from plugin

* nice login modals

* initial token management screen

* implement token authentication

* refactor(share): token management screen

* style(fmt): rustfmt

* fix(plugin): some minor bugs

* refactor(share): main screen

* refactor(share): token screen

* refactor(share): main

* refactor(share): ui components

* fix(responsiveness): properly send usage_width to the render function

* fix cli commands and add some verbosity

* add support for settings ansi and selection colors

* add cursor and cursor accent

* basic web client tests

* fix tests

* refactor: web client

* use session tokens for authentication

* improve modals

* move shutdown to ipc

* refactor: ipc logic

* serialize theme config for web client

* update tests

* refactor: move some stuff around to prepare for config hot reload

* config live reloading for the web clients

* change remember-me UI wording

* improve xterm.js link handling

* make sure terminal is focused on mousemove

* remove deprecated sharing indication from compact-bar

* gate deps and functionality behind the web_server_compatibility feature

* feat(build): add --no-web flag in all the places

* fix some other build flows

* add new assets

* update CI for no-web (untested)

* make more dependencies optional

* update axum-extra

* add web client configuration options

* gracefully close connections on server exit

* tests for graceful connection closing

* handle client-side reconnect when server is down

* fix: make sure ipc bus folder exists before starting

* add commands to manage login tokens from the cli

* style(fmt): rustfmt

* some cleanups

* fix(ux): allow alt-right-click on the web client without opening the context menu

* fix: prevent attaching to welcome screen

* fix: reload config issues

* fix long socket path on macos

* normalize config conversion and fix color gap in browser

* revoke session_token cookie if it is not valid

* fix: visual bug with multiple clients in extremely small screen sizes

* fix: only include rusqlite for the web server capability builds

* update e2e snapshots

* refactor(web): client side js

* some cleanups

* moar cleanups

* fix(tests): wait for server instead of using a fixed timeout

* debug CI

* fix(tests): use spawn_blocking for running the test web server

* fix(tests): wait for http rather than tcp port

* fix(tests): properly pass config path - hopefully this is the issue...

* success! bring back the rest of the tests

* attempt to fix the macos CI issue

* docs(changelog): add PR

---------

Co-authored-by: Thomas Linford <[email protected]>
2025-06-23 19:19:37 +02:00

2 lines
6.2 KiB
JavaScript

!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardAddon=e():t.ClipboardAddon=e()}(self,(()=>(()=>{var t={575:function(t,e,r){"undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==r.g&&r.g,t.exports=function(){"use strict";var t,e="3.7.7",r=e,n="function"==typeof Buffer,o="function"==typeof TextDecoder?new TextDecoder:void 0,i="function"==typeof TextEncoder?new TextEncoder:void 0,u=Array.prototype.slice.call("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="),a=(t={},u.forEach((function(e,r){return t[e]=r})),t),c=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,f=String.fromCharCode.bind(String),s="function"==typeof Uint8Array.from?Uint8Array.from.bind(Uint8Array):function(t){return new Uint8Array(Array.prototype.slice.call(t,0))},d=function(t){return t.replace(/=/g,"").replace(/[+\/]/g,(function(t){return"+"==t?"-":"_"}))},l=function(t){return t.replace(/[^A-Za-z0-9\+\/]/g,"")},p=function(t){for(var e,r,n,o,i="",a=t.length%3,c=0;c<t.length;){if((r=t.charCodeAt(c++))>255||(n=t.charCodeAt(c++))>255||(o=t.charCodeAt(c++))>255)throw new TypeError("invalid character found");i+=u[(e=r<<16|n<<8|o)>>18&63]+u[e>>12&63]+u[e>>6&63]+u[63&e]}return a?i.slice(0,a-3)+"===".substring(a):i},h="function"==typeof btoa?function(t){return btoa(t)}:n?function(t){return Buffer.from(t,"binary").toString("base64")}:p,b=n?function(t){return Buffer.from(t).toString("base64")}:function(t){for(var e=[],r=0,n=t.length;r<n;r+=4096)e.push(f.apply(null,t.subarray(r,r+4096)));return h(e.join(""))},y=function(t,e){return void 0===e&&(e=!1),e?d(b(t)):b(t)},x=function(t){if(t.length<2)return(e=t.charCodeAt(0))<128?t:e<2048?f(192|e>>>6)+f(128|63&e):f(224|e>>>12&15)+f(128|e>>>6&63)+f(128|63&e);var e=65536+1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320);return f(240|e>>>18&7)+f(128|e>>>12&63)+f(128|e>>>6&63)+f(128|63&e)},A=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,g=function(t){return t.replace(A,x)},v=n?function(t){return Buffer.from(t,"utf8").toString("base64")}:i?function(t){return b(i.encode(t))}:function(t){return h(g(t))},B=function(t,e){return void 0===e&&(e=!1),e?d(v(t)):v(t)},C=function(t){return B(t,!0)},m=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,w=function(t){switch(t.length){case 4:var e=((7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3))-65536;return f(55296+(e>>>10))+f(56320+(1023&e));case 3:return f((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return f((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},T=function(t){return t.replace(m,w)},_=function(t){if(t=t.replace(/\s+/g,""),!c.test(t))throw new TypeError("malformed base64.");t+="==".slice(2-(3&t.length));for(var e,r,n,o="",i=0;i<t.length;)e=a[t.charAt(i++)]<<18|a[t.charAt(i++)]<<12|(r=a[t.charAt(i++)])<<6|(n=a[t.charAt(i++)]),o+=64===r?f(e>>16&255):64===n?f(e>>16&255,e>>8&255):f(e>>16&255,e>>8&255,255&e);return o},F="function"==typeof atob?function(t){return atob(l(t))}:n?function(t){return Buffer.from(t,"base64").toString("binary")}:_,U=n?function(t){return s(Buffer.from(t,"base64"))}:function(t){return s(F(t).split("").map((function(t){return t.charCodeAt(0)})))},P=function(t){return U(S(t))},j=n?function(t){return Buffer.from(t,"base64").toString("utf8")}:o?function(t){return o.decode(U(t))}:function(t){return T(F(t))},S=function(t){return l(t.replace(/[-_]/g,(function(t){return"-"==t?"+":"/"})))},E=function(t){return j(S(t))},R=function(t){return{value:t,enumerable:!1,writable:!0,configurable:!0}},O=function(){var t=function(t,e){return Object.defineProperty(String.prototype,t,R(e))};t("fromBase64",(function(){return E(this)})),t("toBase64",(function(t){return B(this,t)})),t("toBase64URI",(function(){return B(this,!0)})),t("toBase64URL",(function(){return B(this,!0)})),t("toUint8Array",(function(){return P(this)}))},D=function(){var t=function(t,e){return Object.defineProperty(Uint8Array.prototype,t,R(e))};t("toBase64",(function(t){return y(this,t)})),t("toBase64URI",(function(){return y(this,!0)})),t("toBase64URL",(function(){return y(this,!0)}))},z={version:e,VERSION:r,atob:F,atobPolyfill:_,btoa:h,btoaPolyfill:p,fromBase64:E,toBase64:B,encode:B,encodeURI:C,encodeURL:C,utob:g,btou:T,decode:E,isValid:function(t){if("string"!=typeof t)return!1;var e=t.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(e)||!/[^\s0-9a-zA-Z\-_]/.test(e)},fromUint8Array:y,toUint8Array:P,extendString:O,extendUint8Array:D,extendBuiltins:function(){O(),D()},Base64:{}};return Object.keys(z).forEach((function(t){return z.Base64[t]=z[t]})),z}()}},e={};function r(n){var o=e[n];if(void 0!==o)return o.exports;var i=e[n]={exports:{}};return t[n].call(i.exports,i,i.exports,r),i.exports}r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}();var n={};return(()=>{"use strict";var t=n;Object.defineProperty(t,"__esModule",{value:!0}),t.Base64=t.BrowserClipboardProvider=t.ClipboardAddon=void 0;const e=r(575);t.ClipboardAddon=class{constructor(t=new i,e=new o){this._base64=t,this._provider=e}activate(t){this._terminal=t,this._disposable=t.parser.registerOscHandler(52,(t=>this._setOrReportClipboard(t)))}dispose(){return this._disposable?.dispose()}_readText(t,e){const r=this._base64.encodeText(e);this._terminal?.input(`]52;${t};${r}`,!1)}_setOrReportClipboard(t){const e=t.split(";");if(e.length<2)return!0;const r=e[0],n=e[1];if("?"===n){const t=this._provider.readText(r);return t instanceof Promise?t.then((t=>(this._readText(r,t),!0))):(this._readText(r,t),!0)}let o="";try{o=this._base64.decodeText(n)}catch{}const i=this._provider.writeText(r,o);return!(i instanceof Promise)||i.then((()=>!0))}};class o{async readText(t){return"c"!==t?Promise.resolve(""):navigator.clipboard.readText()}async writeText(t,e){return"c"!==t?Promise.resolve():navigator.clipboard.writeText(e)}}t.BrowserClipboardProvider=o;class i{encodeText(t){return e.Base64.encode(t)}decodeText(t){const r=e.Base64.decode(t);return e.Base64.isValid(t)&&e.Base64.encode(r)===t?r:""}}t.Base64=i})(),n})()));
//# sourceMappingURL=addon-clipboard.js.map