aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/plugin_api/plugin_command.rs
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-06-23 19:19:37 +0200
committerGitHub <noreply@github.com>2025-06-23 19:19:37 +0200
commitc5ac7968809ddb1033b625562eae22019cccc2f4 (patch)
tree932cf07d898dfbcc6ec6a9d4cf5353c06a47accc /zellij-utils/src/plugin_api/plugin_command.rs
parentfeat(ui): add optional tooltip with key hints to the compact bar (#4225) (diff)
downloadsl0ppij-c5ac7968809ddb1033b625562eae22019cccc2f4.tar.gz
sl0ppij-c5ac7968809ddb1033b625562eae22019cccc2f4.tar.xz
sl0ppij-c5ac7968809ddb1033b625562eae22019cccc2f4.zip
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 <linford.t@gmail.com>
Diffstat (limited to 'zellij-utils/src/plugin_api/plugin_command.rs')
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs164
1 files changed, 153 insertions, 11 deletions
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index 30a6a03d..7fb583b7 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -6,15 +6,17 @@ pub use super::generated_api::api::{
plugin_command::Payload, BreakPanesToNewTabPayload, BreakPanesToTabWithIndexPayload,
ChangeFloatingPanesCoordinatesPayload, ChangeHostFolderPayload,
ClearScreenForPaneIdPayload, CliPipeOutputPayload, CloseMultiplePanesPayload,
- CloseTabWithIndexPayload, CommandName, ContextItem, EditScrollbackForPaneWithIdPayload,
- EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload,
+ CloseTabWithIndexPayload, CommandName, ContextItem,
+ CreateTokenResponse as ProtobufCreateTokenResponse, CreateTokenResponse,
+ EditScrollbackForPaneWithIdPayload, EmbedMultiplePanesPayload, EnvVariable, ExecCmdPayload,
FixedOrPercent as ProtobufFixedOrPercent,
FixedOrPercentValue as ProtobufFixedOrPercentValue, FloatMultiplePanesPayload,
- FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, GroupAndUngroupPanesPayload,
- HidePaneWithIdPayload, HighlightAndUnhighlightPanesPayload, HttpVerb as ProtobufHttpVerb,
- IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload, LoadNewPluginPayload,
- MessageToPluginPayload, MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload,
- MovePayload, NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload,
+ FloatingPaneCoordinates as ProtobufFloatingPaneCoordinates, GenerateWebLoginTokenPayload,
+ GroupAndUngroupPanesPayload, HidePaneWithIdPayload, HighlightAndUnhighlightPanesPayload,
+ HttpVerb as ProtobufHttpVerb, IdAndNewName, KeyToRebind, KeyToUnbind, KillSessionsPayload,
+ ListTokensResponse, LoadNewPluginPayload, MessageToPluginPayload,
+ MovePaneWithPaneIdInDirectionPayload, MovePaneWithPaneIdPayload, MovePayload,
+ NewPluginArgs as ProtobufNewPluginArgs, NewTabsWithLayoutInfoPayload,
OpenCommandPaneFloatingNearPluginPayload, OpenCommandPaneInPlaceOfPluginPayload,
OpenCommandPaneNearPluginPayload, OpenCommandPanePayload,
OpenFileFloatingNearPluginPayload, OpenFileInPlaceOfPluginPayload,
@@ -23,10 +25,12 @@ pub use super::generated_api::api::{
PageScrollDownInPaneIdPayload, PageScrollUpInPaneIdPayload, PaneId as ProtobufPaneId,
PaneIdAndFloatingPaneCoordinates, PaneType as ProtobufPaneType,
PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RebindKeysPayload,
- ReconfigurePayload, ReloadPluginPayload, RequestPluginPermissionPayload,
- RerunCommandPanePayload, ResizePaneIdWithDirectionPayload, ResizePayload,
- RunCommandPayload, ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload,
- ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload,
+ ReconfigurePayload, ReloadPluginPayload, RenameWebLoginTokenPayload,
+ RenameWebTokenResponse, RequestPluginPermissionPayload, RerunCommandPanePayload,
+ ResizePaneIdWithDirectionPayload, ResizePayload, RevokeAllWebTokensResponse,
+ RevokeTokenResponse, RevokeWebLoginTokenPayload, RunCommandPayload,
+ ScrollDownInPaneIdPayload, ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload,
+ ScrollUpInPaneIdPayload, SetFloatingPanePinnedPayload, SetSelfMouseSelectionSupportPayload,
SetTimeoutPayload, ShowPaneWithIdPayload, StackPanesPayload, SubscribePayload,
SwitchSessionPayload, SwitchTabToPayload, TogglePaneEmbedOrEjectForPaneIdPayload,
TogglePaneIdFullscreenPayload, UnsubscribePayload, WebRequestPayload,
@@ -1545,6 +1549,27 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for OpenFileInPlaceOfPlugin"),
},
+ Some(CommandName::StartWebServer) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("StartWebServer should not have a payload")
+ } else {
+ Ok(PluginCommand::StartWebServer)
+ }
+ },
+ Some(CommandName::StopWebServer) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("StopWebServer should not have a payload")
+ } else {
+ Ok(PluginCommand::StopWebServer)
+ }
+ },
+ Some(CommandName::QueryWebServerStatus) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("QueryWebServerStatus should not have a payload")
+ } else {
+ Ok(PluginCommand::QueryWebServerStatus)
+ }
+ },
Some(CommandName::GroupAndUngroupPanes) => match protobuf_plugin_command.payload {
Some(Payload::GroupAndUngroupPanesPayload(group_and_ungroup_panes_payload)) => {
Ok(PluginCommand::GroupAndUngroupPanes(
@@ -1617,6 +1642,67 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for EmbedMultiplePanes"),
},
+ Some(CommandName::ShareCurrentSession) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("ShareCurrentSession should not have a payload")
+ } else {
+ Ok(PluginCommand::ShareCurrentSession)
+ }
+ },
+ Some(CommandName::StopSharingCurrentSession) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("StopSharingCurrentSession should not have a payload")
+ } else {
+ Ok(PluginCommand::StopSharingCurrentSession)
+ }
+ },
+ Some(CommandName::SetSelfMouseSelectionSupport) => {
+ match protobuf_plugin_command.payload {
+ Some(Payload::SetSelfMouseSelectionSupportPayload(
+ set_self_mouse_selection_support_payload,
+ )) => Ok(PluginCommand::SetSelfMouseSelectionSupport(
+ set_self_mouse_selection_support_payload.support_mouse_selection,
+ )),
+ _ => Err("SetSelfMouseSelectionSupport requires a payload"),
+ }
+ },
+ Some(CommandName::GenerateWebLoginToken) => match protobuf_plugin_command.payload {
+ Some(Payload::GenerateWebLoginTokenPayload(generate_web_login_token_payload)) => {
+ Ok(PluginCommand::GenerateWebLoginToken(
+ generate_web_login_token_payload.token_label,
+ ))
+ },
+ _ => Err("GenerateWebLoginToken requires a payload"),
+ },
+ Some(CommandName::RevokeWebLoginToken) => match protobuf_plugin_command.payload {
+ Some(Payload::RevokeWebLoginTokenPayload(revoke_web_login_token_payload)) => Ok(
+ PluginCommand::RevokeWebLoginToken(revoke_web_login_token_payload.token_label),
+ ),
+ _ => Err("RevokeWebLoginToken requires a payload"),
+ },
+ Some(CommandName::ListWebLoginTokens) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("ListWebLoginTokens should not have a payload")
+ } else {
+ Ok(PluginCommand::ListWebLoginTokens)
+ }
+ },
+ Some(CommandName::RevokeAllWebLoginTokens) => {
+ if protobuf_plugin_command.payload.is_some() {
+ Err("RevokeAllWebLoginTokens should not have a payload")
+ } else {
+ Ok(PluginCommand::RevokeAllWebLoginTokens)
+ }
+ },
+ Some(CommandName::RenameWebLoginToken) => match protobuf_plugin_command.payload {
+ Some(Payload::RenameWebLoginTokenPayload(rename_web_login_token_payload)) => {
+ Ok(PluginCommand::RenameWebLoginToken(
+ rename_web_login_token_payload.old_name,
+ rename_web_login_token_payload.new_name,
+ ))
+ },
+ _ => Err("RenameWebLoginToken requires a payload"),
+ },
Some(CommandName::InterceptKeyPresses) => match protobuf_plugin_command.payload {
Some(_) => Err("InterceptKeyPresses should have no payload, found a payload"),
None => Ok(PluginCommand::InterceptKeyPresses),
@@ -2656,6 +2742,18 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
)),
})
},
+ PluginCommand::StartWebServer => Ok(ProtobufPluginCommand {
+ name: CommandName::StartWebServer as i32,
+ payload: None,
+ }),
+ PluginCommand::StopWebServer => Ok(ProtobufPluginCommand {
+ name: CommandName::StopWebServer as i32,
+ payload: None,
+ }),
+ PluginCommand::QueryWebServerStatus => Ok(ProtobufPluginCommand {
+ name: CommandName::QueryWebServerStatus as i32,
+ payload: None,
+ }),
PluginCommand::HighlightAndUnhighlightPanes(
panes_to_highlight,
panes_to_unhighlight,
@@ -2698,6 +2796,50 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
},
)),
}),
+ PluginCommand::ShareCurrentSession => Ok(ProtobufPluginCommand {
+ name: CommandName::ShareCurrentSession as i32,
+ payload: None,
+ }),
+ PluginCommand::StopSharingCurrentSession => Ok(ProtobufPluginCommand {
+ name: CommandName::StopSharingCurrentSession as i32,
+ payload: None,
+ }),
+ PluginCommand::SetSelfMouseSelectionSupport(support_mouse_selection) => {
+ Ok(ProtobufPluginCommand {
+ name: CommandName::SetSelfMouseSelectionSupport as i32,
+ payload: Some(Payload::SetSelfMouseSelectionSupportPayload(
+ SetSelfMouseSelectionSupportPayload {
+ support_mouse_selection,
+ },
+ )),
+ })
+ },
+ PluginCommand::GenerateWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
+ name: CommandName::GenerateWebLoginToken as i32,
+ payload: Some(Payload::GenerateWebLoginTokenPayload(
+ GenerateWebLoginTokenPayload { token_label },
+ )),
+ }),
+ PluginCommand::RevokeWebLoginToken(token_label) => Ok(ProtobufPluginCommand {
+ name: CommandName::RevokeWebLoginToken as i32,
+ payload: Some(Payload::RevokeWebLoginTokenPayload(
+ RevokeWebLoginTokenPayload { token_label },
+ )),
+ }),
+ PluginCommand::ListWebLoginTokens => Ok(ProtobufPluginCommand {
+ name: CommandName::ListWebLoginTokens as i32,
+ payload: None,
+ }),
+ PluginCommand::RevokeAllWebLoginTokens => Ok(ProtobufPluginCommand {
+ name: CommandName::RevokeAllWebLoginTokens as i32,
+ payload: None,
+ }),
+ PluginCommand::RenameWebLoginToken(old_name, new_name) => Ok(ProtobufPluginCommand {
+ name: CommandName::RenameWebLoginToken as i32,
+ payload: Some(Payload::RenameWebLoginTokenPayload(
+ RenameWebLoginTokenPayload { old_name, new_name },
+ )),
+ }),
PluginCommand::InterceptKeyPresses => Ok(ProtobufPluginCommand {
name: CommandName::InterceptKeyPresses as i32,
payload: None,