aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/plugin_api
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-04-17 14:08:48 +0200
committerGitHub <noreply@github.com>2026-04-17 14:08:48 +0200
commitaa29cbaf6631af0afca6aae427a97b3e478393b3 (patch)
tree0ee1cab0f7bdc66cf151f38341252701338c1853 /zellij-utils/src/plugin_api
parentfix(windows): skip modifier re-encoding for Char keys in cast_crossterm_key (... (diff)
downloadsl0ppij-aa29cbaf6631af0afca6aae427a97b3e478393b3.tar.gz
sl0ppij-aa29cbaf6631af0afca6aae427a97b3e478393b3.tar.xz
sl0ppij-aa29cbaf6631af0afca6aae427a97b3e478393b3.zip
Some idle CPU and disk/io fixes, new CommandChanged plugin Event (#5063)
* initial implementation * fix mouse cpu spam * prevent excess metadata write and prune sessions on startup * consolidate CommandChanged and PaneCommandChanged * tests * rustfmt * add pr
Diffstat (limited to 'zellij-utils/src/plugin_api')
-rw-r--r--zellij-utils/src/plugin_api/event.proto9
-rw-r--r--zellij-utils/src/plugin_api/event.rs41
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto16
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs94
4 files changed, 150 insertions, 10 deletions
diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto
index ce9eb8ff..3c3a8490 100644
--- a/zellij-utils/src/plugin_api/event.proto
+++ b/zellij-utils/src/plugin_api/event.proto
@@ -67,6 +67,7 @@ enum EventType {
HighlightClicked = 42;
PaneRenderReportWithAnsi = 43;
InitialKeybinds = 44;
+ CommandChanged = 45;
}
message EventNameList {
@@ -113,6 +114,7 @@ message Event {
HighlightClickedPayload highlight_clicked_payload = 36;
PaneRenderReportPayload pane_render_report_with_ansi_payload = 37;
InitialKeybindsPayload initial_keybinds_payload = 38;
+ CommandChangedPayload command_changed_payload = 39;
}
}
@@ -122,6 +124,13 @@ message CwdChangedPayload {
repeated uint32 focused_client_ids = 3;
}
+message CommandChangedPayload {
+ PaneId pane_id = 1;
+ repeated string command = 2;
+ bool is_foreground = 3;
+ repeated uint32 focused_client_ids = 4;
+}
+
message FailedToStartWebServerPayload {
string error = 1;
}
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index 93b28365..6f138742 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -6,7 +6,8 @@ pub use super::generated_api::api::{
pane_scrollback_response, ActionCompletePayload as ProtobufActionCompletePayload,
AvailableLayoutInfoPayload as ProtobufAvailableLayoutInfoPayload,
ClientInfo as ProtobufClientInfo, ClientPaneHistory as ProtobufClientPaneHistory,
- ClientTabHistory as ProtobufClientTabHistory, ContextItem as ProtobufContextItem,
+ ClientTabHistory as ProtobufClientTabHistory,
+ CommandChangedPayload as ProtobufCommandChangedPayload, ContextItem as ProtobufContextItem,
CopyDestination as ProtobufCopyDestination, CwdChangedPayload as ProtobufCwdChangedPayload,
Event as ProtobufEvent, EventNameList as ProtobufEventNameList,
EventType as ProtobufEventType, FileMetadata as ProtobufFileMetadata,
@@ -474,6 +475,27 @@ impl TryFrom<ProtobufEvent> for Event {
},
_ => Err("Malformed payload for the CwdChanged Event"),
},
+ Some(ProtobufEventType::CommandChanged) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::CommandChangedPayload(p)) => {
+ let pane_id: PaneId = p
+ .pane_id
+ .ok_or("Missing pane_id in CommandChanged payload")?
+ .try_into()
+ .map_err(|_| "Failed to convert PaneId in CommandChanged payload")?;
+ let focused_client_ids: Vec<ClientId> = p
+ .focused_client_ids
+ .into_iter()
+ .map(|id| id as u16)
+ .collect();
+ Ok(Event::CommandChanged(
+ pane_id,
+ p.command,
+ p.is_foreground,
+ focused_client_ids,
+ ))
+ },
+ _ => Err("Malformed payload for the CommandChanged Event"),
+ },
Some(ProtobufEventType::AvailableLayoutInfo) => match protobuf_event.payload {
Some(ProtobufEventPayload::AvailableLayoutInfoPayload(
available_layout_info_payload,
@@ -1014,6 +1036,21 @@ impl TryFrom<Event> for ProtobufEvent {
payload: Some(event::Payload::CwdChangedPayload(cwd_changed_payload)),
})
},
+ Event::CommandChanged(pane_id, command, is_foreground, focused_client_ids) => {
+ let protobuf_pane_id: ProtobufPaneId = pane_id.try_into()?;
+ let focused_client_ids_u32: Vec<u32> =
+ focused_client_ids.into_iter().map(|id| id as u32).collect();
+ let payload = ProtobufCommandChangedPayload {
+ pane_id: Some(protobuf_pane_id),
+ command,
+ is_foreground,
+ focused_client_ids: focused_client_ids_u32,
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::CommandChanged as i32,
+ payload: Some(event::Payload::CommandChangedPayload(payload)),
+ })
+ },
Event::AvailableLayoutInfo(available_layouts, layouts_with_errors) => {
let mut protobuf_available_layouts = vec![];
let mut protobuf_layouts_with_errors = vec![];
@@ -2015,6 +2052,7 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::UserAction => EventType::UserAction,
ProtobufEventType::ActionComplete => EventType::ActionComplete,
ProtobufEventType::CwdChanged => EventType::CwdChanged,
+ ProtobufEventType::CommandChanged => EventType::CommandChanged,
ProtobufEventType::AvailableLayoutInfo => EventType::AvailableLayoutInfo,
ProtobufEventType::PluginConfigurationChanged => EventType::PluginConfigurationChanged,
ProtobufEventType::HighlightClicked => EventType::HighlightClicked,
@@ -2067,6 +2105,7 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::UserAction => ProtobufEventType::UserAction,
EventType::ActionComplete => ProtobufEventType::ActionComplete,
EventType::CwdChanged => ProtobufEventType::CwdChanged,
+ EventType::CommandChanged => ProtobufEventType::CommandChanged,
EventType::AvailableLayoutInfo => ProtobufEventType::AvailableLayoutInfo,
EventType::PluginConfigurationChanged => ProtobufEventType::PluginConfigurationChanged,
EventType::HighlightClicked => ProtobufEventType::HighlightClicked,
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index 63baa443..0b768838 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -203,6 +203,7 @@ enum CommandName {
ClearPaneHighlights = 208;
OpenPluginPaneFloating = 209;
ListWindowsVolumes = 210;
+ GetSessionList = 211;
}
message PluginCommand {
@@ -354,6 +355,7 @@ message PluginCommand {
SetPaneRegexHighlightsPayload set_pane_regex_highlights_payload = 158;
ClearPaneHighlightsPayload clear_pane_highlights_payload = 159;
OpenPluginPaneFloatingPayload open_plugin_pane_floating_payload = 160;
+ GetSessionListPayload get_session_list_payload = 161;
}
}
@@ -1295,3 +1297,17 @@ message OpenPluginPaneFloatingPayload {
message OpenPluginPaneFloatingResponse {
optional PaneId pane_id = 1;
}
+
+message GetSessionListPayload {}
+
+message SessionListSnapshot {
+ repeated event.SessionManifest live_sessions = 1;
+ repeated event.ResurrectableSession resurrectable_sessions = 2;
+}
+
+message GetSessionListResponse {
+ oneof result {
+ SessionListSnapshot snapshot = 1;
+ string error = 2;
+ }
+}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index a457f720..5bfb2b42 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -1,13 +1,17 @@
pub use super::generated_api::api::{
action::{Action as ProtobufAction, PaneIdAndShouldFloat, SwitchToModePayload},
- event::{EventNameList as ProtobufEventNameList, Header},
+ event::{
+ EventNameList as ProtobufEventNameList, Header,
+ ResurrectableSession as ProtobufResurrectableSession,
+ SessionManifest as ProtobufSessionManifest,
+ },
input_mode::InputMode as ProtobufInputMode,
plugin_command::{
break_panes_to_new_tab_response, break_panes_to_tab_with_id_response,
break_panes_to_tab_with_index_response, delete_layout_response, dump_layout_response,
dump_session_layout_response, edit_layout_response, focus_or_create_tab_response,
get_focused_pane_info_response, get_pane_cwd_response, get_pane_pid_response,
- get_pane_running_command_response, hide_floating_panes_response,
+ get_pane_running_command_response, get_session_list_response, hide_floating_panes_response,
highlight_style::Style as ProtobufHighlightStyleVariant, new_tab_response,
parse_layout_response, plugin_command::Payload, rename_layout_response,
save_layout_response, save_session_response, show_floating_panes_response,
@@ -48,7 +52,9 @@ pub use super::generated_api::api::{
GetPaneScrollbackPayload,
GetSessionEnvironmentVariablesPayload as ProtobufGetSessionEnvironmentVariablesPayload,
GetSessionEnvironmentVariablesResponse as ProtobufGetSessionEnvironmentVariablesResponse,
- GetTabInfoPayload, GetTabInfoResponse as ProtobufGetTabInfoResponse, GoToTabWithIdPayload,
+ GetSessionListPayload as ProtobufGetSessionListPayload,
+ GetSessionListResponse as ProtobufGetSessionListResponse, GetTabInfoPayload,
+ GetTabInfoResponse as ProtobufGetTabInfoResponse, GoToTabWithIdPayload,
GroupAndUngroupPanesPayload, HideFloatingPanesPayload as ProtobufHideFloatingPanesPayload,
HideFloatingPanesResponse as ProtobufHideFloatingPanesResponse, HidePaneWithIdPayload,
HighlightAndUnhighlightPanesPayload, HighlightLayer as ProtobufHighlightLayer,
@@ -108,9 +114,10 @@ pub use super::generated_api::api::{
SaveLayoutPayload, SaveLayoutResponse as ProtobufSaveLayoutResponse, SaveSessionPayload,
SaveSessionResponse as ProtobufSaveSessionResponse, ScrollDownInPaneIdPayload,
ScrollToBottomInPaneIdPayload, ScrollToTopInPaneIdPayload, ScrollUpInPaneIdPayload,
- SetFloatingPanePinnedPayload, SetPaneBorderlessPayload, SetPaneColorPayload,
- SetPaneRegexHighlightsPayload, SetSelfMouseSelectionSupportPayload, SetTimeoutPayload,
- ShowCursorPayload, ShowFloatingPanesPayload as ProtobufShowFloatingPanesPayload,
+ SessionListSnapshot as ProtobufSessionListSnapshot, SetFloatingPanePinnedPayload,
+ SetPaneBorderlessPayload, SetPaneColorPayload, SetPaneRegexHighlightsPayload,
+ SetSelfMouseSelectionSupportPayload, SetTimeoutPayload, ShowCursorPayload,
+ ShowFloatingPanesPayload as ProtobufShowFloatingPanesPayload,
ShowFloatingPanesResponse as ProtobufShowFloatingPanesResponse, ShowPaneWithIdPayload,
StackPanesPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToIdPayload,
SwitchTabToPayload, TogglePaneBorderlessPayload, TogglePaneEmbedOrEjectForPaneIdPayload,
@@ -124,9 +131,10 @@ pub use super::generated_api::api::{
use crate::data::{
ConnectToSession, DeleteLayoutResponse, EditLayoutResponse, FloatingPaneCoordinates,
GetFocusedPaneInfoResponse, GetPaneCwdResponse, GetPanePidResponse,
- GetPaneRunningCommandResponse, HighlightLayer, HighlightStyle, HttpVerb, InputMode,
- KeyWithModifier, MessageToPlugin, NewPluginArgs, PaneId, PermissionType, PluginCommand,
- RegexHighlight, RenameLayoutResponse, SaveLayoutResponse,
+ GetPaneRunningCommandResponse, GetSessionListResponse, HighlightLayer, HighlightStyle,
+ HttpVerb, InputMode, KeyWithModifier, MessageToPlugin, NewPluginArgs, PaneId, PermissionType,
+ PluginCommand, RegexHighlight, RenameLayoutResponse, SaveLayoutResponse, SessionInfo,
+ SessionListSnapshot,
};
use crate::input::actions::Action;
use crate::input::layout::PercentOrFixed;
@@ -329,6 +337,63 @@ impl From<GetPaneRunningCommandResponse> for ProtobufGetPaneRunningCommandRespon
}
}
+impl From<GetSessionListResponse> for ProtobufGetSessionListResponse {
+ fn from(response: GetSessionListResponse) -> Self {
+ match response {
+ GetSessionListResponse::Ok(snapshot) => {
+ let live_sessions = snapshot
+ .live_sessions
+ .into_iter()
+ .filter_map(|si| ProtobufSessionManifest::try_from(si).ok())
+ .collect();
+ let resurrectable_sessions = snapshot
+ .resurrectable_sessions
+ .into_iter()
+ .map(|entry| ProtobufResurrectableSession::from(entry))
+ .collect();
+ ProtobufGetSessionListResponse {
+ result: Some(get_session_list_response::Result::Snapshot(
+ ProtobufSessionListSnapshot {
+ live_sessions,
+ resurrectable_sessions,
+ },
+ )),
+ }
+ },
+ GetSessionListResponse::Err(err) => ProtobufGetSessionListResponse {
+ result: Some(get_session_list_response::Result::Error(err)),
+ },
+ }
+ }
+}
+
+impl TryFrom<ProtobufGetSessionListResponse> for GetSessionListResponse {
+ type Error = &'static str;
+ fn try_from(protobuf_response: ProtobufGetSessionListResponse) -> Result<Self, &'static str> {
+ match protobuf_response.result {
+ Some(get_session_list_response::Result::Snapshot(snapshot)) => {
+ let mut live_sessions: Vec<SessionInfo> = Vec::new();
+ for manifest in snapshot.live_sessions {
+ live_sessions.push(SessionInfo::try_from(manifest)?);
+ }
+ let resurrectable_sessions = snapshot
+ .resurrectable_sessions
+ .into_iter()
+ .map(<(String, std::time::Duration)>::from)
+ .collect();
+ Ok(GetSessionListResponse::Ok(SessionListSnapshot {
+ live_sessions,
+ resurrectable_sessions,
+ }))
+ },
+ Some(get_session_list_response::Result::Error(err)) => {
+ Ok(GetSessionListResponse::Err(err))
+ },
+ None => Err("Empty GetSessionListResponse"),
+ }
+ }
+}
+
impl From<GetPaneCwdResponse> for ProtobufGetPaneCwdResponse {
fn from(response: GetPaneCwdResponse) -> Self {
match response {
@@ -1300,6 +1365,11 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
Some(_) => Err("ListWindowsVolumes should have no payload"),
None => Ok(PluginCommand::ListWindowsVolumes),
},
+ Some(CommandName::GetSessionList) => match protobuf_plugin_command.payload {
+ Some(Payload::GetSessionListPayload(_)) => Ok(PluginCommand::GetSessionList),
+ None => Ok(PluginCommand::GetSessionList),
+ _ => Err("Mismatched payload for GetSessionList"),
+ },
Some(CommandName::DumpSessionLayout) => match protobuf_plugin_command.payload {
Some(Payload::DumpSessionLayoutPayload(payload)) => {
Ok(PluginCommand::DumpSessionLayout {
@@ -3145,6 +3215,12 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::ListWindowsVolumes as i32,
payload: None,
}),
+ PluginCommand::GetSessionList => Ok(ProtobufPluginCommand {
+ name: CommandName::GetSessionList as i32,
+ payload: Some(Payload::GetSessionListPayload(
+ ProtobufGetSessionListPayload {},
+ )),
+ }),
PluginCommand::DumpSessionLayout { tab_index } => Ok(ProtobufPluginCommand {
name: CommandName::DumpSessionLayout as i32,
payload: tab_index.map(|idx| {