aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-server
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2025-12-11 14:25:42 +0100
committerGitHub <noreply@github.com>2025-12-11 14:25:42 +0100
commit693fa082def5ef0fe030cd7fdc3b1df051b35dc6 (patch)
treeb3f651462bbc1cd2fa5b2d79207343413ef6b83c /zellij-server
parentfix(zellij-server): unblock building on 32bit targets (#4484) (diff)
downloadsl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.gz
sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.tar.xz
sl0ppij-693fa082def5ef0fe030cd7fdc3b1df051b35dc6.zip
Command Sequences, Blocking CLI Commands, New Plugin APIs (#4546)
* fix deadlocks * initial logging to plugin implementation * only log actions, include keys as Action::Write * send actions to plugins as a new Event::UserAction * add Action::Write metadata to plugin API * run_action plugin command and permission * block-until-exit/success/failure in the cli * fix tests * add NewBlockingPane to the plugin API * run actions in a different thread so as not to block plugin execution * add custom context to run_action flow * fixes for floating pane z_index sorting and unknown protobuf events not crashing plugins * always render the frame of pinned panes * macros module * add macros to config * KDL macro parsing * thread macros to the plugin thread * get/update/delete macros from plugins * add macros updated event * add ability to rename macros * fix deadlock * fix z_indices with pinned unselectable panes bug * allow plugins to opt-in to show_cursor * change show_cursor(bool) to show_cursor(Option<coordinates>) * test * upgrade strum to allow proper enum iteration and case insensitive fromstr implementation for Actions * better cwd tracking for exited command panes * add initial panes to NewTab Action (no-op for now) * add initial panes to cli newtab action * make initial panes work * add close_on_exit and start_suspended to the cli * properly convert newtab action (potentially breaking for plugins) * allow first pane in tab to block * make distinction between opening panes with the last active client and the current pane in the cli * improve some apis * fix fixed size closing pane bug * fix plugin cursor coordinates * fix issues with launch-or-focus-plugin having to do with in-place and suppressed panes * fix: set affected pane in replace pane * allow setting a pane in place when it is suppressed and being focused (tests failing) * fix tests * some fixes for unsuppression of panes * some fixes for completion_tx in new tab action * allow replace/suppressed rather than replace/close only * fixes for inserting suppressed panes without conflicts * pane focus history and explicit unsuppress plugin api * fix: prefer explicitly provided pane id in placement in actions that provide it * fix floating pane coordinate change and z_index * fix plugin cache dir to persist between sessions * fix some stack focusing issues when adding panes without a client id * make sure pinned panes are always above non-pinned floating panes * allow plugins to copy to clipboard * initial text styling additions implementations - buggy * fix dimming issues * remove extras, stay with just dim and unbold * unbold and dim as indices * allow ui components to render success/error colors * allow plugins to send sigint/sigkill to processes, as well as receive their pid * ui component fixes and send originating plugin context with run_action * originating plugin for new tab too * make plugn show pane command also expand stacked panes * add sequence plugin * remove macros * remove snapshots * add shortcut for sequence plugin * fix plugin tests * plugin tests for new apis * update snapshots for sequence shortcut * cleanups and formatting * Event::CwdChanged * moar cleanups * remove some warnings * remove mroe warnings * remove dead code * remove more dead code * fix minor bugs in sequence plugin * update sequence help and format codebase * fix conditional blocking cli commands * fix initial-command new-tab cli flag * add client_ids to CwdChanged Event * last fixes * add pr
Diffstat (limited to 'zellij-server')
-rw-r--r--zellij-server/src/background_jobs.rs2
-rw-r--r--zellij-server/src/lib.rs2
-rw-r--r--zellij-server/src/os_input_output.rs6
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs59
-rw-r--r--zellij-server/src/panes/plugin_pane.rs29
-rw-r--r--zellij-server/src/panes/terminal_pane.rs22
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs8
-rw-r--r--zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs8
-rw-r--r--zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs2
-rw-r--r--zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__table_ui_component.snap14
-rw-r--r--zellij-server/src/plugins/mod.rs70
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs667
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focus_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focused_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_plugin_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_terminal_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_plugin_command.snap10
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_without_permission.snap5
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__detach_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_next_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_or_create_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_plugin_pane_plugin_command.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_previous_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_terminal_pane_plugin_command.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_next_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_previous_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_cursor_plugin_command.snap11
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_or_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_direction_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tab_plugin_command.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command-2.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command.snap4
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__next_swap_layout_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__previous_swap_layout_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__quit_zellij_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_plugin_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_terminal_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_with_direction_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_plugin_command.snap17
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_without_permission.snap5
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigint_to_pane_id_plugin_command.snap9
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigkill_to_pane_id_plugin_command.snap9
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_cursor_plugin_command.snap16
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap1
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_self_plugin_command.snap3
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__start_or_reload_plugin.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_active_tab_sync_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_focus_fullscreen_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_frames_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_pane_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_tab_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_plugin_command.snap2
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs109
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs320
-rw-r--r--zellij-server/src/pty.rs318
-rw-r--r--zellij-server/src/route.rs435
-rw-r--r--zellij-server/src/screen.rs273
-rw-r--r--zellij-server/src/tab/layout_applier.rs19
-rw-r--r--zellij-server/src/tab/mod.rs262
-rw-r--r--zellij-server/src/tab/unit/tab_integration_tests.rs18
-rw-r--r--zellij-server/src/tab/unit/tab_tests.rs6
-rw-r--r--zellij-server/src/ui/components/nested_list.rs1
-rw-r--r--zellij-server/src/ui/components/ribbon.rs1
-rw-r--r--zellij-server/src/ui/components/table.rs24
-rw-r--r--zellij-server/src/ui/components/text.rs71
-rw-r--r--zellij-server/src/ui/mod.rs1
-rw-r--r--zellij-server/src/ui/overlay/mod.rs97
-rw-r--r--zellij-server/src/ui/overlay/prompt.rs65
-rw-r--r--zellij-server/src/ui/pane_contents_and_ui.rs3
-rw-r--r--zellij-server/src/unit/screen_tests.rs42
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap2
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap4
-rw-r--r--zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_query_tab_names_action.snap2
110 files changed, 2589 insertions, 603 deletions
diff --git a/zellij-server/src/background_jobs.rs b/zellij-server/src/background_jobs.rs
index 9bc8a938..62907048 100644
--- a/zellij-server/src/background_jobs.rs
+++ b/zellij-server/src/background_jobs.rs
@@ -24,6 +24,7 @@ use std::time::{Duration, Instant};
use crate::panes::PaneId;
use crate::plugins::{PluginId, PluginInstruction};
+use crate::pty::PtyInstruction;
use crate::screen::ScreenInstruction;
use crate::thread_bus::Bus;
use crate::ClientId;
@@ -228,6 +229,7 @@ pub(crate) fn background_jobs_main(
session_infos_on_machine,
resurrectable_sessions,
));
+ let _ = senders.send_to_pty(PtyInstruction::UpdateAndReportCwds);
if last_serialization_time
.lock()
.unwrap()
diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs
index 1861fb68..dd093919 100644
--- a/zellij-server/src/lib.rs
+++ b/zellij-server/src/lib.rs
@@ -784,6 +784,8 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
floating_panes_layout,
tab_name,
swap_layouts,
+ None, // initial_panes
+ false, // block_on_first_terminal
should_focus_tab,
(client_id, is_web_client),
None,
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index adcca974..46010493 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -500,6 +500,8 @@ pub trait ServerOsApi: Send + Sync {
fn kill(&self, pid: Pid) -> Result<()>;
/// Terminate the process with process ID `pid`. (SIGKILL)
fn force_kill(&self, pid: Pid) -> Result<()>;
+ /// Send SIGINT to the process with process ID `pid`
+ fn send_sigint(&self, pid: Pid) -> Result<()>;
/// Returns a [`Box`] pointer to this [`ServerOsApi`] struct.
fn box_clone(&self) -> Box<dyn ServerOsApi>;
fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) -> Result<()>;
@@ -697,6 +699,10 @@ impl ServerOsApi for ServerOsInputOutput {
let _ = kill(pid, Some(Signal::SIGKILL));
Ok(())
}
+ fn send_sigint(&self, pid: Pid) -> Result<()> {
+ let _ = kill(pid, Some(Signal::SIGINT));
+ Ok(())
+ }
fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) -> Result<()> {
let err_context = || format!("failed to send message to client {client_id}");
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index 6ae71b43..d347dcd0 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -18,6 +18,7 @@ use crate::{
ClientId,
};
use std::cell::RefCell;
+use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::rc::Rc;
use std::time::Instant;
@@ -129,6 +130,7 @@ impl FloatingPanes {
.insert(pane_id, pane.position_and_size());
self.panes.insert(pane_id, pane);
self.z_indices.push(pane_id);
+ self.make_sure_pinned_panes_are_on_top();
}
pub fn replace_active_pane(
&mut self,
@@ -169,6 +171,7 @@ impl FloatingPanes {
.with_context(err_context)?;
self.z_indices.remove(z_index);
self.z_indices.insert(z_index, with_pane_id);
+ self.make_sure_pinned_panes_are_on_top();
Ok(removed_pane)
});
@@ -415,21 +418,10 @@ impl FloatingPanes {
vec![]
};
floating_panes.sort_by(|(a_id, _a_pane), (b_id, _b_pane)| {
- self.z_indices
- .iter()
- .position(|id| id == *a_id)
- .with_context(err_context)
- .fatal()
- .cmp(
- &self
- .z_indices
- .iter()
- .position(|id| id == *b_id)
- .with_context(err_context)
- .fatal(),
- )
+ let a_pos = self.z_indices.iter().position(|id| id == *a_id);
+ let b_pos = self.z_indices.iter().position(|id| id == *b_id);
+ a_pos.cmp(&b_pos)
});
-
for (z_index, (kind, pane)) in floating_panes.iter_mut().enumerate() {
let mut active_panes = active_panes.clone();
let multiple_users_exist_in_session =
@@ -806,6 +798,16 @@ impl FloatingPanes {
pane_geom.adjust_coordinates(new_coordinates, viewport);
pane.set_geom(pane_geom);
pane.set_should_render(true);
+
+ // we do this in case this moves the pane under another pane so that the pane user's
+ // are focused on will always be on top
+ let is_focused = self.active_panes.pane_id_is_focused(&pane_id);
+ if is_focused {
+ self.z_indices.retain(|p_id| *p_id != pane_id);
+ self.z_indices.push(pane_id);
+ self.make_sure_pinned_panes_are_on_top();
+ }
+
self.desired_pane_positions.insert(pane_id, pane_geom);
}
let _ = self.set_pane_frames();
@@ -840,7 +842,7 @@ impl FloatingPanes {
self.focus_pane(next_active_pane_id, client_id);
},
None => {
- self.defocus_pane(pane_id, client_id);
+ self.defocus_pane(client_id);
},
}
}
@@ -855,6 +857,7 @@ impl FloatingPanes {
}
self.z_indices.retain(|p_id| *p_id != pane_id);
self.z_indices.push(pane_id);
+ self.make_sure_pinned_panes_are_on_top();
self.set_pane_active_at(pane_id);
self.set_force_render();
}
@@ -888,8 +891,7 @@ impl FloatingPanes {
},
}
}
- pub fn defocus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
- self.z_indices.retain(|p_id| *p_id != pane_id);
+ pub fn defocus_pane(&mut self, client_id: ClientId) {
self.active_panes.remove(&client_id, &mut self.panes);
self.set_force_render();
}
@@ -1244,4 +1246,27 @@ impl FloatingPanes {
);
floating_pane_grid.next_selectable_pane_id_to_the_right(&pane_id)
}
+ fn make_sure_pinned_panes_are_on_top(&mut self) {
+ let pinned_status: std::collections::HashMap<_, _> = self
+ .z_indices
+ .iter()
+ .map(|id| (id.clone(), self.pane_id_is_pinned(id)))
+ .collect();
+
+ self.z_indices.sort_by(|a_id, b_id| {
+ let a_is_pinned = pinned_status.get(a_id).copied().unwrap_or(false);
+ let b_is_pinned = pinned_status.get(b_id).copied().unwrap_or(false);
+ match (a_is_pinned, b_is_pinned) {
+ (true, false) => Ordering::Greater,
+ (false, true) => Ordering::Less,
+ _ => Ordering::Equal,
+ }
+ });
+ }
+ fn pane_id_is_pinned(&self, pane_id: &PaneId) -> bool {
+ self.panes
+ .get(pane_id)
+ .map(|p| p.position_and_size().is_pinned)
+ .unwrap_or(false)
+ }
}
diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs
index 0e74a9de..ffad3d84 100644
--- a/zellij-server/src/panes/plugin_pane.rs
+++ b/zellij-server/src/panes/plugin_pane.rs
@@ -90,6 +90,7 @@ pub(crate) struct PluginPane {
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
vte_parsers: HashMap<ClientId, vte::Parser>,
grids: HashMap<ClientId, Grid>,
+ cursor_visibility: HashMap<ClientId, Option<(usize, usize)>>,
prev_pane_name: String,
frame: HashMap<ClientId, PaneFrame>,
borderless: bool,
@@ -149,6 +150,7 @@ impl PluginPane {
sixel_image_store,
vte_parsers: HashMap::new(),
grids: HashMap::new(),
+ cursor_visibility: HashMap::new(),
style,
pane_frame_color_override: None,
invoked_with,
@@ -248,8 +250,25 @@ impl Pane for PluginPane {
self.should_render.insert(client_id, true);
}
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
- None
+ fn cursor_coordinates(&self, client_id: Option<ClientId>) -> Option<(usize, usize)> {
+ let own_content_columns = self.get_content_columns();
+ let own_content_rows = self.get_content_rows();
+ let Offset { top, left, .. } = self.content_offset;
+ if let Some(coordinates) =
+ client_id.and_then(|client_id| self.cursor_visibility.get(&client_id))
+ {
+ coordinates
+ .map(|(x, y)| (x + left, y + top))
+ .and_then(|(x, y)| {
+ if x >= own_content_columns || y >= own_content_rows {
+ None
+ } else {
+ Some((x, y)) // these are 0 indexed
+ }
+ })
+ } else {
+ None
+ }
}
fn adjust_input_to_terminal(
&mut self,
@@ -360,6 +379,10 @@ impl Pane for PluginPane {
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
+ fn show_cursor(&mut self, client_id: ClientId, cursor_position: Option<(usize, usize)>) {
+ self.cursor_visibility.insert(client_id, cursor_position);
+ self.should_render.insert(client_id, true);
+ }
fn request_permissions_from_user(&mut self, permissions: Option<PluginPermission>) {
self.requesting_permissions = permissions;
self.handle_plugin_bytes_for_all_clients(Default::default()); // to trigger the render of
@@ -440,7 +463,7 @@ impl Pane for PluginPane {
let res = match self.frame.get(&client_id) {
// TODO: use and_then or something?
Some(last_frame) => {
- if &frame != last_frame {
+ if &frame != last_frame || is_pinned {
if !self.borderless {
let frame_output = frame.render().with_context(err_context)?;
self.frame.insert(client_id, frame);
diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs
index 1c5667a5..97bf5dcc 100644
--- a/zellij-server/src/panes/terminal_pane.rs
+++ b/zellij-server/src/panes/terminal_pane.rs
@@ -197,7 +197,7 @@ impl Pane for TerminalPane {
self.vte_parser.advance(&mut self.grid, byte);
}
}
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
+ fn cursor_coordinates(&self, _client_id: Option<ClientId>) -> Option<(usize, usize)> {
// (x, y)
if self.get_content_rows() < 1 || self.get_content_columns() < 1 {
// do not render cursor if there's no room for it
@@ -740,6 +740,14 @@ impl Pane for TerminalPane {
if let Some(notification_end) = self.notification_end.as_mut() {
if let Some(exit_status) = exit_status {
notification_end.set_exit_status(exit_status);
+
+ // Check if unblock condition is met
+ if let Some(condition) = notification_end.unblock_condition() {
+ if condition.is_met(exit_status) {
+ // Condition met - drop the NotificationEnd now to unblock
+ drop(self.notification_end.take());
+ }
+ }
}
}
if is_first_run {
@@ -899,6 +907,13 @@ impl Pane for TerminalPane {
fn update_exit_status(&mut self, exit_status: i32) {
if let Some(notification_end) = self.notification_end.as_mut() {
notification_end.set_exit_status(exit_status);
+ // Check if unblock condition is met
+ if let Some(condition) = notification_end.unblock_condition() {
+ if condition.is_met(exit_status) {
+ // Condition met - drop the NotificationEnd now to unblock
+ drop(self.notification_end.take());
+ }
+ }
}
}
}
@@ -922,7 +937,7 @@ impl TerminalPane {
arrow_fonts: bool,
styled_underlines: bool,
explicitly_disable_keyboard_protocol: bool,
- notification_end: Option<NotificationEnd>,
+ mut notification_end: Option<NotificationEnd>,
) -> TerminalPane {
let initial_pane_title =
initial_pane_title.unwrap_or_else(|| format!("Pane #{}", pane_index));
@@ -940,6 +955,9 @@ impl TerminalPane {
styled_underlines,
explicitly_disable_keyboard_protocol,
);
+ if let Some(notification_end) = notification_end.as_mut() {
+ notification_end.set_affected_pane_id(PaneId::Terminal(pid));
+ }
TerminalPane {
frame: HashMap::new(),
content_offset: Offset::default(),
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index fb73a90d..4df68db4 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -313,6 +313,7 @@ impl TiledPanes {
Ok(new_pane_geom) => {
pane.set_geom(new_pane_geom);
self.panes.insert(pane_id, pane); // TODO: is set_geom the right one?
+ self.expand_pane_in_stack(pane_id);
},
Err(e) => {
log::error!("Failed to add pane to stack: {:?}", e);
@@ -349,7 +350,8 @@ impl TiledPanes {
Ok(new_pane_geom) => {
pane.set_geom(new_pane_geom);
self.panes.insert(pane_id, pane);
- self.set_force_render(); // TODO: why do we need this?
+
+ self.expand_pane_in_stack(pane_id);
return;
},
Err(_e) => {
@@ -447,7 +449,9 @@ impl TiledPanes {
.map(|p| p.is_stacked())
.unwrap_or(false);
if !pane_id_is_stacked {
- let _ = pane_grid.make_pane_stacked(&root_pane_id);
+ if let Err(e) = pane_grid.make_pane_stacked(&root_pane_id) {
+ log::error!("Failed to make pane stacked: {:?}", e);
+ }
}
match pane_grid.make_room_in_stack_of_pane_id_for_pane(&root_pane_id) {
Ok(new_pane_geom) => {
diff --git a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs
index ddb225a4..0c989a9e 100644
--- a/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs
+++ b/zellij-server/src/panes/tiled_panes/tiled_pane_grid.rs
@@ -1344,6 +1344,14 @@ impl<'a> TiledPaneGrid<'a> {
let _ = pane_resizer.layout(direction, side_length);
return true;
}
+ } else {
+ // best effort resize - we just remove the pane and relayout everything
+ // this might happen if we are closing a fixed pane
+ self.panes.borrow_mut().remove(&id);
+ let mut pane_resizer = PaneResizer::new(self.panes.clone());
+ let _ = pane_resizer.layout(SplitDirection::Horizontal, self.display_area.cols);
+ let _ = pane_resizer.layout(SplitDirection::Vertical, self.display_area.rows);
+ return true;
}
false
}
diff --git a/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs
index 6b4edf40..e4adfbe4 100644
--- a/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs
+++ b/zellij-server/src/panes/tiled_panes/unit/stacked_panes_tests.rs
@@ -1085,7 +1085,7 @@ impl Pane for MockPane {
fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {
unimplemented!()
}
- fn cursor_coordinates(&self) -> Option<(usize, usize)> {
+ fn cursor_coordinates(&self, _client_id: Option<ClientId>) -> Option<(usize, usize)> {
unimplemented!()
}
diff --git a/zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__table_ui_component.snap b/zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__table_ui_component.snap
index 7ab7a56c..53059240 100644
--- a/zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__table_ui_component.snap
+++ b/zellij-server/src/panes/unit/snapshots/zellij_server__panes__grid__grid_tests__table_ui_component.snap
@@ -2,13 +2,13 @@
source: zellij-server/src/panes/./unit/grid_tests.rs
expression: "format!(\"{:?}\", grid)"
---
-00 (C): title1 title2 title3 title4 title5
-01 (C): 1lksdjflksdjf lkjdsflksjdf lkjsdlkfjsfd lkjsdlkjfd lkjdslkjsdf
-02 (C): 2lksdjflksdjf lkjdsflksjdf lkj😳😳sdlkfjsfd lkjsdlkjfd lkjdslkjsdf
-03 (C): 3lksdjflksdjf lkjdsflksjdf lkjsd😳lkfjsfd lkjsdlkjfd lkjdslkjsdf
-04 (C): 4lksdjflks222djf lk😳222jdsflksjdf lk222jsdlkfjsfd lkwwwjsdlkjfd lkjdslkjsdf
-05 (C): 5lksdjflksdjf lkjdsflksjdf lkjsdlkfjsfd lksdjflksjfdlkjsdlkjfd lkjdrrrrslkjsdf
-06 (C): 6lksdjflk😳sdjf l😳kjdsflksjdf lkjsdlkfjsfd lkjsdlkjfd lwwkwkjdslkjsdf
+00 (C): title1 title2 title3 title4 title5
+01 (C): 1lksdjflksdjf lkjdsflksjdf lkjsdlkfjsfd lkjsdlkjfd lkjdslkjsdf
+02 (C): 2lksdjflksdjf lkjdsflksjdf lkj😳😳sdlkfjsfd lkjsdlkjfd lkjdslkjsdf
+03 (C): 3lksdjflksdjf lkjdsflksjdf lkjsd😳lkfjsfd lkjsdlkjfd lkjdslkjsdf
+04 (C): 4lksdjflks222djf lk😳222jdsflksjdf lk222jsdlkfjsfd lkwwwjsdlkjfd lkjdslkjsdf
+05 (C): 5lksdjflksdjf lkjdsflksjdf lkjsdlkfjsfd lksdjflksjfdlkjsdlkjfd lkjdrrrrslkjsdf
+06 (C): 6lksdjflk😳sdjf l😳kjdsflksjdf lkjsdlkfjsfd lkjsdlkjfd lwwkwkjdslkjsdf
07 (C):
08 (C):
09 (C):
diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs
index cede7901..24a1e811 100644
--- a/zellij-server/src/plugins/mod.rs
+++ b/zellij-server/src/plugins/mod.rs
@@ -30,12 +30,13 @@ use wasm_bridge::WasmBridge;
use async_std::{channel, future::timeout, task};
use zellij_utils::{
data::{
- ClientInfo, Event, EventType, FloatingPaneCoordinates, InputMode, MessageToPlugin,
- PermissionStatus, PermissionType, PipeMessage, PipeSource, PluginCapabilities,
- WebServerStatus,
+ ClientInfo, CommandOrPlugin, Event, EventType, FloatingPaneCoordinates, InputMode,
+ MessageToPlugin, PermissionStatus, PermissionType, PipeMessage, PipeSource,
+ PluginCapabilities, WebServerStatus,
},
errors::{prelude::*, ContextType, PluginContext},
input::{
+ actions::Action,
command::TerminalAction,
keybinds::Keybinds,
layout::{FloatingPaneLayout, Layout, Run, RunPlugin, RunPluginOrAlias, TiledPaneLayout},
@@ -86,10 +87,12 @@ pub enum PluginInstruction {
Option<TerminalAction>,
Option<TiledPaneLayout>,
Vec<FloatingPaneLayout>,
- usize, // tab_index
- bool, // should change focus to new tab
- (ClientId, bool), // bool -> is_web_client
- Option<NotificationEnd>, // completion signal
+ usize, // tab_index
+ Option<Vec<CommandOrPlugin>>, // initial_panes
+ bool, // block_on_first_terminal
+ bool, // should change focus to new tab
+ (ClientId, bool), // bool -> is_web_client
+ Option<NotificationEnd>, // completion signal
),
ApplyCachedEvents {
plugin_ids: Vec<PluginId>,
@@ -175,6 +178,12 @@ pub enum PluginInstruction {
WebServerStarted(String), // String -> the base url of the web server
FailedToStartWebServer(String),
PaneRenderReport(PaneRenderReport),
+ UserInput {
+ client_id: ClientId,
+ action: Action,
+ terminal_id: Option<u32>,
+ cli_client_id: Option<ClientId>,
+ },
Exit,
}
@@ -225,6 +234,7 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::WebServerStarted(..) => PluginContext::WebServerStarted,
PluginInstruction::FailedToStartWebServer(..) => PluginContext::FailedToStartWebServer,
PluginInstruction::PaneRenderReport(..) => PluginContext::PaneRenderReport,
+ PluginInstruction::UserInput { .. } => PluginContext::UserInput,
}
}
}
@@ -453,6 +463,8 @@ pub(crate) fn plugin_thread_main(
mut tab_layout,
mut floating_panes_layout,
tab_index,
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
(client_id, is_web_client),
completion_tx,
@@ -469,6 +481,26 @@ pub(crate) fn plugin_thread_main(
let mut plugin_ids: HashMap<RunPluginOrAlias, Vec<PluginId>> = HashMap::new();
tab_layout = tab_layout.or_else(|| Some(layout.new_tab().0));
+
+ // Match initial_panes plugins to empty slots in the layout
+ if let Some(ref initial_panes_vec) = initial_panes {
+ if let Some(ref mut tiled_layout) = tab_layout {
+ for initial_pane in initial_panes_vec.iter() {
+ if let CommandOrPlugin::Plugin(run_plugin_or_alias) = initial_pane {
+ if !tiled_layout.replace_next_empty_slot_with_run(Run::Plugin(
+ run_plugin_or_alias.clone(),
+ )) {
+ log::warn!(
+ "More initial_panes provided than empty slots available"
+ );
+ break;
+ }
+ }
+ // Skip CommandOrPlugin::Command entries (handled by pty thread)
+ }
+ }
+ }
+
tab_layout.as_mut().map(|t| {
t.populate_plugin_aliases_in_layout(&plugin_aliases);
if let Some(cwd) = cwd.as_ref() {
@@ -481,7 +513,7 @@ pub(crate) fn plugin_thread_main(
.as_mut()
.map(|f| f.populate_run_plugin_if_needed(&plugin_aliases));
});
- let mut extracted_run_instructions = tab_layout
+ let extracted_run_instructions = tab_layout
.clone()
.unwrap_or_else(|| layout.new_tab().0)
.extract_run_instructions();
@@ -496,8 +528,10 @@ pub(crate) fn plugin_thread_main(
.filter(|f| !f.already_running)
.map(|f| f.run.clone())
.collect();
- extracted_run_instructions.append(&mut extracted_floating_plugins);
- for run_instruction in extracted_run_instructions {
+ let mut all_run_instructions = extracted_run_instructions;
+ all_run_instructions.append(&mut extracted_floating_plugins);
+
+ for run_instruction in all_run_instructions {
if let Some(Run::Plugin(run_plugin_or_alias)) = run_instruction {
let run_plugin = run_plugin_or_alias.get_run_plugin();
let cwd = run_plugin_or_alias
@@ -525,6 +559,8 @@ pub(crate) fn plugin_thread_main(
floating_panes_layout,
tab_index,
plugin_ids,
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
(client_id, is_web_client),
completion_tx,
@@ -989,6 +1025,20 @@ pub(crate) fn plugin_thread_main(
.handle_pane_render_report(pane_render_report, shutdown_send.clone())
.non_fatal();
},
+ PluginInstruction::UserInput {
+ client_id,
+ action,
+ terminal_id,
+ cli_client_id,
+ } => {
+ // Fire Event::UserAction to all subscribed plugins with InterceptInput permission
+ let updates = vec![(
+ None,
+ None,
+ Event::UserAction(action, client_id, terminal_id, cli_client_id),
+ )];
+ wasm_bridge.update_plugins(updates, shutdown_send.clone())?;
+ },
PluginInstruction::Exit => {
break;
},
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index 5f90ebbe..c0368fbc 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -9137,3 +9137,670 @@ pub fn before_close_plugin_event() {
.unwrap();
assert_snapshot!(format!("{:#?}", sent_instruction));
}
+
+#[test]
+#[ignore]
+pub fn show_cursor_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ShowPluginCursor,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_super_modifier()),
+ )]));
+
+ screen_thread.join().unwrap();
+ teardown();
+
+ let show_cursor_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ShowPluginCursor(plugin_id, client_id, cursor_position) = i {
+ Some((*plugin_id, *client_id, *cursor_position))
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", show_cursor_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn hide_cursor_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ShowPluginCursor,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_super_modifier()),
+ )]));
+
+ screen_thread.join().unwrap();
+ teardown();
+
+ let hide_cursor_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ShowPluginCursor(plugin_id, client_id, cursor_position) = i {
+ Some((*plugin_id, *client_id, *cursor_position))
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", hide_cursor_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn copy_to_clipboard_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::CopyTextToClipboard,
+ screen_receiver,
+ 1,
+ &PermissionType::WriteToClipboard,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_super_modifier()),
+ )]));
+
+ screen_thread.join().unwrap();
+ teardown();
+
+ let copy_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::CopyTextToClipboard(text, plugin_id) = i {
+ Some((text.clone(), *plugin_id))
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", copy_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn run_action_plugin_command() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::MoveFocusLeft,
+ screen_receiver,
+ 1,
+ &PermissionType::RunActionsAsUser,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_super_modifier()),
+ )]));
+
+ screen_thread.join().unwrap();
+ teardown();
+
+ let move_focus_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::MoveFocusLeft(client_id, notification_end) = i {
+ Some((*client_id, notification_end.clone()))
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", move_focus_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn send_sigint_to_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) =
+ create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
+
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let client_id = 1;
+
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
+ PtyInstruction::SendSigintToPaneId,
+ pty_receiver,
+ 1
+ );
+
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_super_modifier()),
+ )]));
+
+ pty_thread.join().unwrap();
+ teardown();
+
+ let sigint_instruction = received_pty_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let PtyInstruction::SendSigintToPaneId(pane_id) = i {
+ Some(*pane_id)
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", sigint_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn send_sigkill_to_pane_id_plugin_command() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) =
+ create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
+
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let client_id = 1;
+
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
+ PtyInstruction::SendSigkillToPaneId,
+ pty_receiver,
+ 1
+ );
+
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_super_modifier()),
+ )]));
+
+ pty_thread.join().unwrap();
+ teardown();
+
+ let sigkill_instruction = received_pty_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let PtyInstruction::SendSigkillToPaneId(pane_id) = i {
+ Some(*pane_id)
+ } else {
+ None
+ }
+ });
+
+ assert_snapshot!(format!("{:#?}", sigkill_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn copy_to_clipboard_without_permission() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let client_id = 1;
+
+ // This test denies the permission and expects no CopyTextToClipboard instruction
+ let screen_thread = deny_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::CopyTextToClipboard,
+ screen_receiver,
+ 1,
+ &PermissionType::WriteToClipboard,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_super_modifier()),
+ )]));
+
+ // Give it time to potentially (incorrectly) send the instruction
+ std::thread::sleep(std::time::Duration::from_millis(1000));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Exit);
+ screen_thread.join().unwrap();
+ teardown();
+
+ let copy_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::CopyTextToClipboard(text, plugin_id) = i {
+ Some((text.clone(), *plugin_id))
+ } else {
+ None
+ }
+ });
+
+ // Should be None because permission was denied
+ assert_snapshot!(format!("{:#?}", copy_instruction));
+}
+
+#[test]
+#[ignore]
+pub fn run_action_without_permission() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let client_id = 1;
+
+ let screen_thread = deny_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::MoveFocusLeft,
+ screen_receiver,
+ 1,
+ &PermissionType::RunActionsAsUser,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ ..Default::default()
+ });
+ let tab_index = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ false,
+ plugin_title,
+ run_plugin,
+ Some(tab_index),
+ None,
+ client_id,
+ size,
+ None,
+ None,
+ false,
+ None,
+ None,
+ None,
+ ));
+
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_super_modifier()),
+ )]));
+
+ std::thread::sleep(std::time::Duration::from_millis(1000));
+
+ let _ = plugin_thread_sender.send(PluginInstruction::Exit);
+ screen_thread.join().unwrap();
+ teardown();
+
+ let move_focus_instruction = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::MoveFocusLeft(client_id, notification_end) = i {
+ Some((*client_id, notification_end.clone()))
+ } else {
+ None
+ }
+ });
+
+ // Should be None because permission was denied
+ assert_snapshot!(format!("{:#?}", move_focus_instruction));
+}
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_plugin_command.snap
index 975e20a3..88023c4b 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__clear_screen_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focus_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focus_plugin_command.snap
index 133b23ff..38179b85 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focus_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focus_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focused_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focused_tab_plugin_command.snap
index cdaa2362..d0312218 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focused_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_focused_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_plugin_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_plugin_pane_plugin_command.snap
index cbf3c8c2..b795da46 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_plugin_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_plugin_pane_plugin_command.snap
@@ -12,6 +12,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
None,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_terminal_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_terminal_pane_plugin_command.snap
index 1041e20f..97e61a45 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_terminal_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__close_terminal_pane_plugin_command.snap
@@ -12,6 +12,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
None,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_plugin_command.snap
new file mode 100644
index 00000000..cc4717e3
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_plugin_command.snap
@@ -0,0 +1,10 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", copy_instruction)"
+---
+Some(
+ (
+ "test clipboard text",
+ 0,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_without_permission.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_without_permission.snap
new file mode 100644
index 00000000..333c0389
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__copy_to_clipboard_without_permission.snap
@@ -0,0 +1,5 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", copy_instruction)"
+---
+None
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__detach_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__detach_plugin_command.snap
index ea71beac..1de9477d 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__detach_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__detach_plugin_command.snap
@@ -11,6 +11,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_plugin_command.snap
index cc572e0c..370a7bbf 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__edit_scrollback_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_next_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_next_pane_plugin_command.snap
index f8cf4623..3ef17f9d 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_next_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_next_pane_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_or_create_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_or_create_tab_plugin_command.snap
index 305eb5a5..fbd45fd0 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_or_create_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_or_create_tab_plugin_command.snap
@@ -18,6 +18,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_plugin_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_plugin_pane_plugin_command.snap
index 6b98856a..31a63f73 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_plugin_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_plugin_pane_plugin_command.snap
@@ -8,11 +8,14 @@ Some(
1,
),
false,
+ false,
1,
Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_previous_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_previous_pane_plugin_command.snap
index 0b37d318..8073ceb8 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_previous_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_previous_pane_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_terminal_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_terminal_pane_plugin_command.snap
index 345f0479..0a3a1d23 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_terminal_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__focus_terminal_pane_plugin_command.snap
@@ -8,11 +8,14 @@ Some(
1,
),
false,
+ false,
1,
Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_next_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_next_tab_plugin_command.snap
index ca2edc5f..c76a5ecc 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_next_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_next_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_previous_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_previous_tab_plugin_command.snap
index 1451fa50..f4653b62 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_previous_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_previous_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab.snap
index 13bbdb4a..48cf0870 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab.snap
@@ -12,6 +12,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
index 3fd2e40f..7e45c1fc 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__go_to_tab_name_plugin_command.snap
@@ -18,6 +18,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
index 28c7dedc..91ec8a9b 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
@@ -1,6 +1,5 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5505
expression: "format!(\"{:#?}\", permissions)"
---
Some(
@@ -15,5 +14,7 @@ Some(
ReadCliPipes,
MessageAndLaunchOtherPlugins,
Reconfigure,
+ RunActionsAsUser,
+ WriteToClipboard,
],
)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_cursor_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_cursor_plugin_command.snap
new file mode 100644
index 00000000..31880c73
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__hide_cursor_plugin_command.snap
@@ -0,0 +1,11 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", hide_cursor_instruction)"
+---
+Some(
+ (
+ 0,
+ 1,
+ None,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_or_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_or_tab_plugin_command.snap
index c3b1d5f2..94419199 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_or_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_or_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_plugin_command.snap
index 72b7dc15..f901ba75 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_focus_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_plugin_command.snap
index b9306571..1c2a8fc6 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_direction_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_direction_plugin_command.snap
index 9da82855..51f2cc15 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_direction_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__move_pane_with_direction_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tab_plugin_command.snap
index 1213308e..128c7abe 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tab_plugin_command.snap
@@ -17,6 +17,8 @@ Some(
[],
[],
),
+ None,
+ false,
true,
(
1,
@@ -26,6 +28,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command-2.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command-2.snap
index bc4faa99..1c74a94b 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command-2.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command-2.snap
@@ -63,6 +63,8 @@ Some(
[],
[],
),
+ None,
+ false,
false,
(
1,
@@ -72,6 +74,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command.snap
index 2ba4de2a..889a6e9d 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__new_tabs_with_layout_plugin_command.snap
@@ -63,6 +63,8 @@ Some(
[],
[],
),
+ None,
+ false,
true,
(
1,
@@ -72,6 +74,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__next_swap_layout_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__next_swap_layout_plugin_command.snap
index 21c4257c..87c3264c 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__next_swap_layout_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__next_swap_layout_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap
index ac0838cc..3afcead6 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_floating_plugin_command.snap
@@ -38,6 +38,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap
index ca751a22..596700c5 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_command_pane_plugin_command.snap
@@ -38,6 +38,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap
index eb598cf0..be8503de 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_floating_plugin_command.snap
@@ -36,6 +36,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap
index d235c416..b899e596 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_plugin_command.snap
@@ -36,6 +36,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap
index cde191a5..fc2087d6 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_floating_plugin_command.snap
@@ -38,6 +38,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap
index 89986fd2..4d2689db 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_file_with_line_plugin_command.snap
@@ -38,6 +38,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap
index b2f0f9ae..b42943fc 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_floating_plugin_command.snap
@@ -31,6 +31,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap
index 5cc2ce7c..dd197b76 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__open_terminal_plugin_command.snap
@@ -31,6 +31,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
false,
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_plugin_command.snap
index d85ffc61..5292281b 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_down_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_plugin_command.snap
index 014ac3d3..5709731c 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__page_scroll_up_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__previous_swap_layout_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__previous_swap_layout_plugin_command.snap
index b35b3b0b..523e76fd 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__previous_swap_layout_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__previous_swap_layout_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__quit_zellij_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__quit_zellij_plugin_command.snap
index d00a7663..5057701a 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__quit_zellij_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__quit_zellij_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_plugin_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_plugin_pane_plugin_command.snap
index 5816c283..119444e2 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_plugin_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_plugin_pane_plugin_command.snap
@@ -33,6 +33,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_tab_plugin_command.snap
index 9450f7c0..4846cd65 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_tab_plugin_command.snap
@@ -23,6 +23,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_terminal_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_terminal_pane_plugin_command.snap
index f23a792c..53b81db8 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_terminal_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__rename_terminal_pane_plugin_command.snap
@@ -35,6 +35,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
index 75f47da7..1400f081 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
@@ -1,6 +1,5 @@
---
source: zellij-server/src/plugins/./unit/plugin_tests.rs
-assertion_line: 5414
expression: "format!(\"{:#?}\", new_tab_event)"
---
Some(
@@ -17,5 +16,7 @@ Some(
ReadCliPipes,
MessageAndLaunchOtherPlugins,
Reconfigure,
+ WriteToClipboard,
+ RunActionsAsUser,
],
)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_plugin_command.snap
index 37275f43..0184c9a6 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_plugin_command.snap
@@ -14,6 +14,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_with_direction_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_with_direction_plugin_command.snap
index d9fa542d..71766b1e 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_with_direction_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__resize_focused_pane_with_direction_plugin_command.snap
@@ -16,6 +16,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_plugin_command.snap
new file mode 100644
index 00000000..dcbf7004
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_plugin_command.snap
@@ -0,0 +1,17 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", move_focus_instruction)"
+---
+Some(
+ (
+ 1,
+ Some(
+ NotificationEnd {
+ channel: None,
+ exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
+ },
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_without_permission.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_without_permission.snap
new file mode 100644
index 00000000..102abab0
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__run_action_without_permission.snap
@@ -0,0 +1,5 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", move_focus_instruction)"
+---
+None
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_plugin_command.snap
index c8605a1c..738a7625 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_down_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_plugin_command.snap
index 410902d5..339ea8c4 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_bottom_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_plugin_command.snap
index 9f2effac..5c0b9088 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_to_top_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_plugin_command.snap
index acc0d6a6..d0250b34 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__scroll_up_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
index aa9592c2..14438cf8 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_configuration_to_plugins.snap
@@ -18,6 +18,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigint_to_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigint_to_pane_id_plugin_command.snap
new file mode 100644
index 00000000..a7558487
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigint_to_pane_id_plugin_command.snap
@@ -0,0 +1,9 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", sigint_instruction)"
+---
+Some(
+ Terminal(
+ 1,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigkill_to_pane_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigkill_to_pane_id_plugin_command.snap
new file mode 100644
index 00000000..fb7dc0a1
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__send_sigkill_to_pane_id_plugin_command.snap
@@ -0,0 +1,9 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", sigkill_instruction)"
+---
+Some(
+ Terminal(
+ 1,
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_cursor_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_cursor_plugin_command.snap
new file mode 100644
index 00000000..5b53f360
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_cursor_plugin_command.snap
@@ -0,0 +1,16 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", show_cursor_instruction)"
+---
+Some(
+ (
+ 0,
+ 1,
+ Some(
+ (
+ 5,
+ 10,
+ ),
+ ),
+ ),
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap
index 24f6c12f..457bebdc 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_pane_with_id_plugin_command.snap
@@ -8,6 +8,7 @@ Some(
1,
),
true,
+ false,
1,
None,
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_self_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_self_plugin_command.snap
index a99b0230..41396ddb 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_self_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__show_self_plugin_command.snap
@@ -8,11 +8,14 @@ Some(
0,
),
false,
+ false,
1,
Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__start_or_reload_plugin.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__start_or_reload_plugin.snap
index 290247c0..845f37ba 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__start_or_reload_plugin.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__start_or_reload_plugin.snap
@@ -21,6 +21,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
index 6df0b5df..b4ff3c00 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command.snap
@@ -330,6 +330,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_active_tab_sync_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_active_tab_sync_plugin_command.snap
index f8959104..06cde256 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_active_tab_sync_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_active_tab_sync_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_focus_fullscreen_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_focus_fullscreen_plugin_command.snap
index 34e96dae..44747b66 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_focus_fullscreen_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_focus_fullscreen_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_plugin_command.snap
index 8f375b9c..8e24bff8 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_embed_or_eject_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_frames_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_frames_plugin_command.snap
index e1bded4d..c91f66aa 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_frames_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_pane_frames_plugin_command.snap
@@ -8,6 +8,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_tab_plugin_command.snap
index e088770b..32965d58 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__toggle_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_pane_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_pane_plugin_command.snap
index 6c500286..99df8475 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_pane_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_pane_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_tab_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_tab_plugin_command.snap
index 54566d68..7836e7d2 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_tab_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__undo_rename_tab_plugin_command.snap
@@ -9,6 +9,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
index 8bb05ce1..abf3d1a2 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_chars_plugin_command.snap
@@ -16,6 +16,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_plugin_command.snap
index 8bb05ce1..abf3d1a2 100644
--- a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_plugin_command.snap
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__write_plugin_command.snap
@@ -16,6 +16,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index 5dd78d13..2453a0a1 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -113,7 +113,7 @@ impl LoadingContext {
let plugin_own_data_dir = ZELLIJ_SESSION_CACHE_DIR
.join(Url::from(&plugin_config.location).to_string())
.join(format!("{}-{}", plugin_id, client_id));
- let plugin_own_cache_dir = ZELLIJ_SESSION_CACHE_DIR
+ let plugin_own_cache_dir = ZELLIJ_CACHE_DIR
.join(Url::from(&plugin_config.location).to_string())
.join(format!("plugin_cache"));
let default_mode = wasm_bridge
@@ -1901,9 +1901,11 @@ fn check_event_permission(
| Event::EditPaneExited(..)
| Event::FailedToWriteConfigToDisk(..)
| Event::CommandPaneReRun(..)
+ | Event::CwdChanged(..)
| Event::InputReceived => PermissionType::ReadApplicationState,
Event::WebServerStatus(..) => PermissionType::StartWebServer,
Event::PaneRenderReport(..) => PermissionType::ReadPaneContents,
+ Event::UserAction(..) => PermissionType::InterceptInput,
_ => return (PermissionStatus::Granted, None),
};
@@ -1943,54 +1945,63 @@ pub fn apply_event_to_plugin(
mode_info.keybinds = running_plugin.store.data().keybinds.to_keybinds_vec();
mode_info.base_mode = Some(running_plugin.store.data().default_mode);
}
- let protobuf_event: ProtobufEvent = event
- .clone()
- .try_into()
- .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
- let update = instance
- .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
- .with_context(err_context)?;
- wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
- .with_context(err_context)?;
- let should_render = update
- .call(&mut running_plugin.store, ())
- .with_context(err_context)?;
- let mut should_render = should_render == 1;
- if let Event::PermissionRequestResult(..) = event {
- // we always render in this case, otherwise the request permission screen stays on
- // screen
- should_render = true;
- }
- if rows > 0 && columns > 0 && should_render {
- let rendered_bytes = instance
- .get_typed_func::<(i32, i32), ()>(&mut running_plugin.store, "render")
- .and_then(|render| {
- render.call(&mut running_plugin.store, (rows as i32, columns as i32))
- })
- .map_err(|e| anyhow!(e))
- .and_then(|_| {
- wasi_read_string(running_plugin.store.data()).map_err(|e| anyhow!(e))
- })
- .with_context(err_context)?;
- let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
- let plugin_render_asset = PluginRenderAsset::new(
- plugin_id,
- client_id,
- rendered_bytes.as_bytes().to_vec(),
- )
- .with_pipes(pipes_to_block_or_unblock);
- plugin_render_assets.push(plugin_render_asset);
- } else {
- // This is a bit of a hack to get around the fact that plugins are allowed not to
- // render and still unblock CLI pipes
- let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
- let plugin_render_asset = PluginRenderAsset::new(plugin_id, client_id, vec![])
- .with_pipes(pipes_to_block_or_unblock);
- let _ = senders
- .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
- plugin_render_asset,
- ]))
- .context("failed to unblock input pipe");
+ let protobuf_event: Result<ProtobufEvent, _> = event.clone().try_into();
+ match protobuf_event {
+ Ok(protobuf_event) => {
+ let update = instance
+ .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
+ .with_context(err_context)?;
+ wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
+ .with_context(err_context)?;
+ let should_render = update
+ .call(&mut running_plugin.store, ())
+ .with_context(err_context)?;
+ let mut should_render = should_render == 1;
+ if let Event::PermissionRequestResult(..) = event {
+ // we always render in this case, otherwise the request permission screen stays on
+ // screen
+ should_render = true;
+ }
+ if rows > 0 && columns > 0 && should_render {
+ let rendered_bytes = instance
+ .get_typed_func::<(i32, i32), ()>(&mut running_plugin.store, "render")
+ .and_then(|render| {
+ render
+ .call(&mut running_plugin.store, (rows as i32, columns as i32))
+ })
+ .map_err(|e| anyhow!(e))
+ .and_then(|_| {
+ wasi_read_string(running_plugin.store.data())
+ .map_err(|e| anyhow!(e))
+ })
+ .with_context(err_context)?;
+ let pipes_to_block_or_unblock =
+ pipes_to_block_or_unblock(running_plugin, None);
+ let plugin_render_asset = PluginRenderAsset::new(
+ plugin_id,
+ client_id,
+ rendered_bytes.as_bytes().to_vec(),
+ )
+ .with_pipes(pipes_to_block_or_unblock);
+ plugin_render_assets.push(plugin_render_asset);
+ } else {
+ // This is a bit of a hack to get around the fact that plugins are allowed not to
+ // render and still unblock CLI pipes
+ let pipes_to_block_or_unblock =
+ pipes_to_block_or_unblock(running_plugin, None);
+ let plugin_render_asset =
+ PluginRenderAsset::new(plugin_id, client_id, vec![])
+ .with_pipes(pipes_to_block_or_unblock);
+ let _ = senders
+ .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
+ plugin_render_asset,
+ ]))
+ .context("failed to unblock input pipe");
+ }
+ },
+ Err(e) => {
+ log::error!("Failed to convert to protobuf: {:?}", e);
+ },
}
},
(PermissionStatus::Denied, permission) => {
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 526b4bcb..f64a9e18 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -20,9 +20,9 @@ use std::{
};
use wasmi::{Caller, Linker};
use zellij_utils::data::{
- CommandType, ConnectToSession, FloatingPaneCoordinates, HttpVerb, KeyWithModifier, LayoutInfo,
- MessageToPlugin, NewPanePlacement, OriginatingPlugin, PaneScrollbackResponse, PermissionStatus,
- PermissionType, PluginPermission,
+ CommandType, ConnectToSession, Event, FloatingPaneCoordinates, GetPanePidResponse, HttpVerb,
+ KeyWithModifier, LayoutInfo, MessageToPlugin, NewPanePlacement, OriginatingPlugin,
+ PaneScrollbackResponse, PermissionStatus, PermissionType, PluginPermission,
};
use zellij_utils::input::permission::PermissionCache;
use zellij_utils::ipc::{ClientToServerMsg, IpcSenderWithContext};
@@ -39,7 +39,7 @@ use prost::Message;
use zellij_utils::{
consts::{VERSION, ZELLIJ_SESSION_INFO_CACHE_DIR, ZELLIJ_SOCK_DIR},
data::{
- CommandToRun, Direction, Event, EventType, FileToOpen, InputMode, PluginCommand, PluginIds,
+ CommandToRun, Direction, EventType, FileToOpen, InputMode, PluginCommand, PluginIds,
PluginMessage, Resize, ResizeStrategy,
},
errors::prelude::*,
@@ -50,7 +50,7 @@ use zellij_utils::{
},
plugin_api::{
event::ProtobufPaneScrollbackResponse,
- plugin_command::ProtobufPluginCommand,
+ plugin_command::{ProtobufGetPanePidResponse, ProtobufPluginCommand},
plugin_ids::{ProtobufPluginIds, ProtobufZellijVersion},
},
};
@@ -104,6 +104,7 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::Subscribe(event_list) => subscribe(env, event_list)?,
PluginCommand::Unsubscribe(event_list) => unsubscribe(env, event_list)?,
PluginCommand::SetSelectable(selectable) => set_selectable(env, selectable),
+ PluginCommand::ShowCursor(cursor_position) => show_cursor(env, cursor_position),
PluginCommand::GetPluginIds => get_plugin_ids(env),
PluginCommand::GetZellijVersion => get_zellij_version(env),
PluginCommand::OpenFile(file_to_open, context) => {
@@ -198,6 +199,7 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::EditScrollback => edit_scrollback(env),
PluginCommand::Write(bytes) => write(env, bytes),
PluginCommand::WriteChars(chars) => write_chars(env, chars),
+ PluginCommand::CopyToClipboard(text) => copy_to_clipboard(env, text)?,
PluginCommand::ToggleTab => toggle_tab(env),
PluginCommand::MovePane => move_pane(env),
PluginCommand::MovePaneWithDirection(direction) => {
@@ -233,12 +235,26 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::ClosePluginPane(plugin_pane_id) => {
close_plugin_pane(env, plugin_pane_id)
},
- PluginCommand::FocusTerminalPane(terminal_pane_id, should_float_if_hidden) => {
- focus_terminal_pane(env, terminal_pane_id, should_float_if_hidden)
- },
- PluginCommand::FocusPluginPane(plugin_pane_id, should_float_if_hidden) => {
- focus_plugin_pane(env, plugin_pane_id, should_float_if_hidden)
- },
+ PluginCommand::FocusTerminalPane(
+ terminal_pane_id,
+ should_float_if_hidden,
+ should_be_in_place_if_hidden,
+ ) => focus_terminal_pane(
+ env,
+ terminal_pane_id,
+ should_float_if_hidden,
+ should_be_in_place_if_hidden,
+ ),
+ PluginCommand::FocusPluginPane(
+ plugin_pane_id,
+ should_float_if_hidden,
+ should_be_in_place_if_hidden,
+ ) => focus_plugin_pane(
+ env,
+ plugin_pane_id,
+ should_float_if_hidden,
+ should_be_in_place_if_hidden,
+ ),
PluginCommand::RenameTerminalPane(terminal_pane_id, new_name) => {
rename_terminal_pane(env, terminal_pane_id, &new_name)
},
@@ -317,9 +333,16 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::HidePaneWithId(pane_id) => {
hide_pane_with_id(env, pane_id.into())?
},
- PluginCommand::ShowPaneWithId(pane_id, should_float_if_hidden) => {
- show_pane_with_id(env, pane_id.into(), should_float_if_hidden)
- },
+ PluginCommand::ShowPaneWithId(
+ pane_id,
+ should_float_if_hidden,
+ should_focus_pane,
+ ) => show_pane_with_id(
+ env,
+ pane_id.into(),
+ should_float_if_hidden,
+ should_focus_pane,
+ ),
PluginCommand::OpenCommandPaneBackground(command_to_run, context) => {
open_command_pane_background(env, command_to_run, context)
},
@@ -342,6 +365,13 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::WriteCharsToPaneId(chars, pane_id) => {
write_chars_to_pane_id(env, chars, pane_id.into())
},
+ PluginCommand::SendSigintToPaneId(pane_id) => {
+ send_sigint_to_pane_id(env, pane_id.into())
+ },
+ PluginCommand::SendSigkillToPaneId(pane_id) => {
+ send_sigkill_to_pane_id(env, pane_id.into())
+ },
+ PluginCommand::GetPanePid { pane_id } => get_pane_pid(env, pane_id.into()),
PluginCommand::MovePaneWithPaneId(pane_id) => {
move_pane_with_pane_id(env, pane_id.into())
},
@@ -509,11 +539,14 @@ fn host_run_plugin_command(mut caller: Caller<'_, PluginEnv>) {
PluginCommand::ReplacePaneWithExistingPane(
pane_id_to_replace,
existing_pane_id,
+ suppress_replaced_pane,
) => replace_pane_with_existing_pane(
&mut env,
pane_id_to_replace.into(),
existing_pane_id.into(),
+ suppress_replaced_pane,
),
+ PluginCommand::RunAction(action, context) => run_action(&env, action, context),
},
(PermissionStatus::Denied, permission) => {
log::error!(
@@ -595,6 +628,27 @@ fn set_selectable(env: &PluginEnv, selectable: bool) {
.non_fatal();
}
+fn show_cursor(env: &PluginEnv, cursor_position: Option<(usize, usize)>) {
+ env.senders
+ .send_to_screen(ScreenInstruction::ShowPluginCursor(
+ env.plugin_id,
+ env.client_id,
+ cursor_position,
+ ))
+ .with_context(|| {
+ format!(
+ "failed to {} plugin cursor from plugin {}",
+ if cursor_position.is_some() {
+ "show"
+ } else {
+ "hide"
+ },
+ env.name()
+ )
+ })
+ .non_fatal();
+}
+
fn request_permission(env: &PluginEnv, permissions: Vec<PermissionType>) -> Result<()> {
if PermissionCache::from_path_or_default(None)
.check_permissions(env.plugin.location.to_string(), &permissions)
@@ -680,10 +734,74 @@ fn open_file(env: &PluginEnv, file_to_open: FileToOpen, context: BTreeMap<String
in_place,
start_suppressed,
coordinates: None,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
+fn run_action(env: &PluginEnv, mut action: Action, context: BTreeMap<String, String>) {
+ // Clone the necessary data to move into the thread
+ action.populate_originating_plugin(OriginatingPlugin::new(
+ env.plugin_id,
+ env.client_id,
+ context.clone(),
+ ));
+ let action_clone = action.clone();
+ let client_id = env.client_id;
+ let plugin_id = env.plugin_id;
+ let senders = env.senders.clone();
+ let capabilities = env.capabilities.clone();
+ let client_attributes = env.client_attributes.clone();
+ let default_shell = env.default_shell.clone();
+ let default_layout = env.default_layout.clone();
+ let keybinds = env.keybinds.clone();
+ let default_mode = env.default_mode.clone();
+ let plugin_name = env.name().to_string();
+
+ // Spawn a new thread to execute the action
+ thread::spawn(move || {
+ // Execute the action and capture the result
+ let pane_id = match route_action(
+ action,
+ client_id,
+ None,
+ Some(PaneId::Plugin(plugin_id)),
+ senders.clone(),
+ capabilities,
+ client_attributes,
+ default_shell,
+ default_layout,
+ None,
+ keybinds,
+ default_mode,
+ None,
+ ) {
+ Ok((_should_break, result)) => {
+ // Extract pane_id from ActionCompletionResult
+ result.and_then(|r| r.affected_pane_id)
+ },
+ Err(e) => {
+ log::error!("failed to run action in plugin {}: {:?}", plugin_name, e);
+ None
+ },
+ };
+
+ // After action completes, send ActionComplete event with context
+ // Convert server PaneId to zellij_utils PaneId
+ let pane_id_for_event = pane_id.map(|p| p.into());
+ let updates = vec![(
+ Some(plugin_id),
+ Some(client_id),
+ Event::ActionComplete(action_clone, pane_id_for_event, context),
+ )];
+
+ // Send the ActionComplete event back to the plugin
+ if let Err(e) = senders.send_to_plugin(PluginInstruction::Update(updates)) {
+ log::error!("Failed to send ActionComplete event: {:?}", e);
+ }
+ });
+}
+
fn open_file_floating(
env: &PluginEnv,
file_to_open: FileToOpen,
@@ -708,6 +826,7 @@ fn open_file_floating(
in_place,
start_suppressed,
coordinates: floating_pane_coordinates,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -736,6 +855,7 @@ fn open_file_in_place(
in_place,
start_suppressed,
coordinates: None,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -845,6 +965,7 @@ fn open_terminal(env: &PluginEnv, cwd: PathBuf) {
direction: None,
command: run_command_action,
pane_name: None,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -894,6 +1015,7 @@ fn open_terminal_floating(
command: run_command_action,
pane_name: None,
coordinates: floating_pane_coordinates,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -942,6 +1064,9 @@ fn open_terminal_in_place(env: &PluginEnv, cwd: PathBuf) {
let action = Action::NewInPlacePane {
command: run_command_action,
pane_name: None,
+ near_current_pane: false,
+ pane_id_to_replace: None,
+ close_replace_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -1044,6 +1169,7 @@ fn open_command_pane(
direction,
command: Some(run_command_action),
pane_name: name,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -1120,6 +1246,7 @@ fn open_command_pane_floating(
command: Some(run_command_action),
pane_name: name,
coordinates: floating_pane_coordinates,
+ near_current_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -1195,6 +1322,9 @@ fn open_command_pane_in_place(
let action = Action::NewInPlacePane {
command: Some(run_command_action),
pane_name: name,
+ near_current_pane: false,
+ pane_id_to_replace: None,
+ close_replace_pane: false,
};
apply_action!(action, error_msg, env);
}
@@ -1418,20 +1548,36 @@ fn show_self(env: &PluginEnv, should_float_if_hidden: bool) {
let action = Action::FocusPluginPaneWithId {
pane_id: env.plugin_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden: false,
};
let error_msg = || format!("Failed to show self for plugin");
apply_action!(action, error_msg, env);
}
-fn show_pane_with_id(env: &PluginEnv, pane_id: PaneId, should_float_if_hidden: bool) {
- let _ = env
- .senders
- .send_to_screen(ScreenInstruction::FocusPaneWithId(
- pane_id,
- should_float_if_hidden,
- env.client_id,
- None,
- ));
+fn show_pane_with_id(
+ env: &PluginEnv,
+ pane_id: PaneId,
+ should_float_if_hidden: bool,
+ should_focus_pane: bool,
+) {
+ if should_focus_pane {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::FocusPaneWithId(
+ pane_id,
+ should_float_if_hidden,
+ false,
+ env.client_id,
+ None,
+ ));
+ } else {
+ let _ = env
+ .senders
+ .send_to_screen(ScreenInstruction::UnsuppressOrExpandPane(
+ pane_id,
+ should_float_if_hidden,
+ ));
+ }
}
fn close_self(env: &PluginEnv) {
@@ -1524,6 +1670,8 @@ fn apply_layout(env: &PluginEnv, layout: Layout) {
tab_name: None,
should_change_focus_to_new_tab: true,
cwd,
+ initial_panes: None,
+ first_pane_unblock_condition: None,
};
tabs_to_open.push(action);
} else {
@@ -1542,6 +1690,8 @@ fn apply_layout(env: &PluginEnv, layout: Layout) {
tab_name,
should_change_focus_to_new_tab: should_focus_tab,
cwd: cwd.clone(),
+ initial_panes: None,
+ first_pane_unblock_condition: None,
};
tabs_to_open.push(action);
}
@@ -1562,6 +1712,8 @@ fn new_tab(env: &PluginEnv, name: Option<String>, cwd: Option<String>) {
tab_name: name,
should_change_focus_to_new_tab: true,
cwd,
+ initial_panes: None,
+ first_pane_unblock_condition: None,
};
let error_msg = || format!("Failed to open new tab");
apply_action!(action, error_msg, env);
@@ -1741,6 +1893,12 @@ fn write_chars(env: &PluginEnv, chars_to_write: String) {
apply_action!(action, error_msg, env);
}
+fn copy_to_clipboard(env: &PluginEnv, text: String) -> Result<()> {
+ env.senders
+ .send_to_screen(ScreenInstruction::CopyTextToClipboard(text, env.plugin_id))
+ .with_context(|| format!("failed to copy to clipboard in plugin {}", env.name()))
+}
+
fn toggle_tab(env: &PluginEnv) {
let error_msg = || format!("Failed to toggle tab");
let action = Action::ToggleTab;
@@ -1938,19 +2096,31 @@ fn close_plugin_pane(env: &PluginEnv, plugin_pane_id: u32) {
.non_fatal();
}
-fn focus_terminal_pane(env: &PluginEnv, terminal_pane_id: u32, should_float_if_hidden: bool) {
+fn focus_terminal_pane(
+ env: &PluginEnv,
+ terminal_pane_id: u32,
+ should_float_if_hidden: bool,
+ should_be_in_place_if_hidden: bool,
+) {
let action = Action::FocusTerminalPaneWithId {
pane_id: terminal_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
};
let error_msg = || format!("Failed to focus terminal pane");
apply_action!(action, error_msg, env);
}
-fn focus_plugin_pane(env: &PluginEnv, plugin_pane_id: u32, should_float_if_hidden: bool) {
+fn focus_plugin_pane(
+ env: &PluginEnv,
+ plugin_pane_id: u32,
+ should_float_if_hidden: bool,
+ should_be_in_place_if_hidden: bool,
+) {
let action = Action::FocusPluginPaneWithId {
pane_id: plugin_pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
};
let error_msg = || format!("Failed to focus plugin pane");
apply_action!(action, error_msg, env);
@@ -2237,6 +2407,92 @@ fn write_chars_to_pane_id(env: &PluginEnv, chars: String, pane_id: PaneId) {
.send_to_screen(ScreenInstruction::WriteToPaneId(bytes, pane_id));
}
+fn send_sigint_to_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let err_context = || {
+ format!(
+ "failed to send SIGINT to pane {:?} from plugin {}",
+ pane_id,
+ env.name()
+ )
+ };
+
+ env.senders
+ .send_to_pty(PtyInstruction::SendSigintToPaneId(pane_id))
+ .with_context(err_context)
+ .non_fatal();
+}
+
+fn send_sigkill_to_pane_id(env: &PluginEnv, pane_id: PaneId) {
+ let err_context = || {
+ format!(
+ "failed to send SIGKILL to pane {:?} from plugin {}",
+ pane_id,
+ env.name()
+ )
+ };
+
+ env.senders
+ .send_to_pty(PtyInstruction::SendSigkillToPaneId(pane_id))
+ .with_context(err_context)
+ .non_fatal();
+}
+
+fn get_pane_pid(env: &PluginEnv, pane_id: PaneId) {
+ use crossbeam::channel::RecvTimeoutError;
+ use std::time::Duration;
+
+ let err_context = || {
+ format!(
+ "failed to get PID for pane {:?} from plugin {}",
+ pane_id,
+ env.name()
+ )
+ };
+
+ // Create oneshot channel for response
+ let (response_sender, response_receiver) = crossbeam::channel::bounded(1);
+
+ // Send request directly to PTY thread
+ env.senders
+ .send_to_pty(PtyInstruction::GetPanePid {
+ pane_id,
+ response_channel: response_sender,
+ })
+ .with_context(err_context)
+ .non_fatal();
+
+ // Block waiting for response with 100ms timeout
+ let response = match response_receiver.recv_timeout(Duration::from_millis(100)) {
+ Ok(response) => response,
+ Err(RecvTimeoutError::Timeout) => {
+ log::error!(
+ "GetPanePid timed out after 5s for plugin {} requesting pane {:?}",
+ env.plugin_id,
+ pane_id
+ );
+ GetPanePidResponse::Err(format!("Timeout retrieving PID for pane {:?}", pane_id))
+ },
+ Err(RecvTimeoutError::Disconnected) => {
+ log::error!(
+ "GetPanePid channel disconnected for plugin {} requesting pane {:?}",
+ env.plugin_id,
+ pane_id
+ );
+ GetPanePidResponse::Err(format!(
+ "Channel disconnected while retrieving PID for pane {:?}",
+ pane_id
+ ))
+ },
+ };
+
+ // Encode response and write to plugin's stdin
+ let protobuf_response = ProtobufGetPanePidResponse::from(response);
+ let serialized = protobuf_response.encode_to_vec();
+ wasi_write_object(env, &serialized)
+ .with_context(err_context)
+ .non_fatal();
+}
+
fn move_pane_with_pane_id(env: &PluginEnv, pane_id: PaneId) {
let _ = env
.senders
@@ -2666,12 +2922,14 @@ fn replace_pane_with_existing_pane(
env: &mut PluginEnv,
pane_to_replace: PaneId,
existing_pane: PaneId,
+ suppress_replaced_pane: bool,
) {
let _ = env
.senders
.send_to_screen(ScreenInstruction::ReplacePaneWithExistingPane(
pane_to_replace,
existing_pane,
+ suppress_replaced_pane,
));
}
@@ -2762,6 +3020,7 @@ fn check_command_permission(
| PluginCommand::WriteChars(..)
| PluginCommand::WriteToPaneId(..)
| PluginCommand::WriteCharsToPaneId(..) => PermissionType::WriteToStdin,
+ PluginCommand::CopyToClipboard(..) => PermissionType::WriteToClipboard,
PluginCommand::SwitchTabTo(..)
| PluginCommand::SwitchToMode(..)
| PluginCommand::NewTabsWithLayout(..)
@@ -2842,14 +3101,16 @@ fn check_command_permission(
| PluginCommand::FloatMultiplePanes(..)
| PluginCommand::EmbedMultiplePanes(..)
| PluginCommand::ReplacePaneWithExistingPane(..)
- | PluginCommand::KillSessions(..) => PermissionType::ChangeApplicationState,
+ | PluginCommand::KillSessions(..)
+ | PluginCommand::SendSigintToPaneId(..)
+ | PluginCommand::SendSigkillToPaneId(..) => PermissionType::ChangeApplicationState,
PluginCommand::UnblockCliPipeInput(..)
| PluginCommand::BlockCliPipeInput(..)
| PluginCommand::CliPipeOutput(..) => PermissionType::ReadCliPipes,
PluginCommand::MessageToPlugin(..) => PermissionType::MessageAndLaunchOtherPlugins,
- PluginCommand::ListClients | PluginCommand::DumpSessionLayout => {
- PermissionType::ReadApplicationState
- },
+ PluginCommand::ListClients
+ | PluginCommand::DumpSessionLayout
+ | PluginCommand::GetPanePid { .. } => PermissionType::ReadApplicationState,
PluginCommand::RebindKeys { .. } | PluginCommand::Reconfigure(..) => {
PermissionType::Reconfigure
},
@@ -2868,6 +3129,7 @@ fn check_command_permission(
PermissionType::InterceptInput
},
PluginCommand::GetPaneScrollback { .. } => PermissionType::ReadPaneContents,
+ PluginCommand::RunAction(..) => PermissionType::RunActionsAsUser,
_ => return (PermissionStatus::Granted, None),
};
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index 39bf4668..fee6054e 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -17,7 +17,10 @@ use nix::unistd::Pid;
use std::sync::Arc;
use std::{collections::HashMap, os::unix::io::RawFd, path::PathBuf};
use zellij_utils::{
- data::{Direction, Event, FloatingPaneCoordinates, NewPanePlacement, OriginatingPlugin},
+ data::{
+ CommandOrPlugin, Event, FloatingPaneCoordinates, GetPanePidResponse, NewPanePlacement,
+ OriginatingPlugin,
+ },
errors::prelude::*,
errors::{ContextType, PtyContext},
input::{
@@ -66,6 +69,8 @@ pub enum PtyInstruction {
Vec<FloatingPaneLayout>,
usize, // tab_index
HashMap<RunPluginOrAlias, Vec<u32>>, // plugin_ids
+ Option<Vec<CommandOrPlugin>>, // initial_panes
+ bool, // block_on_first_terminal
bool, // should change focus to new tab
(ClientId, bool), // bool -> is_web_client
Option<NotificationEnd>, // completion signal
@@ -112,6 +117,13 @@ pub enum PtyInstruction {
},
ListClientsToPlugin(SessionLayoutMetadata, PluginId, ClientId),
ReportPluginCwd(PluginId, PathBuf),
+ SendSigintToPaneId(PaneId),
+ SendSigkillToPaneId(PaneId),
+ GetPanePid {
+ pane_id: PaneId,
+ response_channel: crossbeam::channel::Sender<GetPanePidResponse>,
+ },
+ UpdateAndReportCwds,
Exit,
}
@@ -136,6 +148,10 @@ impl From<&PtyInstruction> for PtyContext {
PtyInstruction::Reconfigure { .. } => PtyContext::Reconfigure,
PtyInstruction::ListClientsToPlugin(..) => PtyContext::ListClientsToPlugin,
PtyInstruction::ReportPluginCwd(..) => PtyContext::ReportPluginCwd,
+ PtyInstruction::SendSigintToPaneId(..) => PtyContext::SendSigintToPaneId,
+ PtyInstruction::SendSigkillToPaneId(..) => PtyContext::SendSigkillToPaneId,
+ PtyInstruction::GetPanePid { .. } => PtyContext::GetPanePid,
+ PtyInstruction::UpdateAndReportCwds => PtyContext::UpdateAndReportCwds,
PtyInstruction::Exit => PtyContext::Exit,
}
}
@@ -150,7 +166,8 @@ pub(crate) struct Pty {
task_handles: HashMap<u32, JoinHandle<()>>, // terminal_id to join-handle
default_editor: Option<PathBuf>,
post_command_discovery_hook: Option<String>,
- plugin_cwds: HashMap<u32, PathBuf>, // plugin_id -> cwd
+ plugin_cwds: HashMap<u32, PathBuf>, // plugin_id -> cwd
+ terminal_cwds: HashMap<u32, PathBuf>, // terminal_id -> cwd
}
pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
@@ -430,6 +447,8 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
floating_panes_layout,
tab_index,
plugin_ids,
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
client_id_and_is_web_client,
completion_tx,
@@ -447,7 +466,9 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
floating_panes_layout,
terminal_action.clone(),
plugin_ids,
+ initial_panes,
tab_index,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
client_id_and_is_web_client,
completion_tx,
@@ -702,6 +723,22 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
} => {
pty.reconfigure(default_editor, post_command_discovery_hook);
},
+ PtyInstruction::SendSigintToPaneId(pane_id) => {
+ pty.send_sigint_to_pane(pane_id);
+ },
+ PtyInstruction::SendSigkillToPaneId(pane_id) => {
+ pty.send_sigkill_to_pane(pane_id);
+ },
+ PtyInstruction::GetPanePid {
+ pane_id,
+ response_channel,
+ } => {
+ let response = pty.get_pane_pid(pane_id);
+ let _ = response_channel.send(response);
+ },
+ PtyInstruction::UpdateAndReportCwds => {
+ pty.update_and_report_cwds();
+ },
PtyInstruction::Exit => break,
}
}
@@ -725,6 +762,7 @@ impl Pty {
originating_plugins: HashMap::new(),
post_command_discovery_hook,
plugin_cwds: HashMap::new(),
+ terminal_cwds: HashMap::new(),
}
}
pub fn get_default_terminal(
@@ -774,12 +812,18 @@ impl Pty {
.get(&client_id)
.and_then(|pane| match pane {
PaneId::Plugin(plugin_id) => self.plugin_cwds.get(plugin_id).cloned(),
- PaneId::Terminal(id) => self.id_to_child_pid.get(id).and_then(|&id| {
- self.bus
- .os_input
- .as_ref()
- .and_then(|input| input.get_cwd(Pid::from_raw(id)))
- }),
+ PaneId::Terminal(id) => {
+ // Try to get CWD from OS, fall back to cached value
+ self.id_to_child_pid
+ .get(id)
+ .and_then(|&pid| {
+ self.bus
+ .os_input
+ .as_ref()
+ .and_then(|input| input.get_cwd(Pid::from_raw(pid)))
+ })
+ .or_else(|| self.terminal_cwds.get(id).cloned())
+ },
})
};
};
@@ -789,12 +833,16 @@ impl Pty {
if run_command.cwd.is_none() {
run_command.cwd = match pane_id {
PaneId::Terminal(terminal_pane_id) => {
- self.id_to_child_pid.get(terminal_pane_id).and_then(|&id| {
- self.bus
- .os_input
- .as_ref()
- .and_then(|input| input.get_cwd(Pid::from_raw(id)))
- })
+ // Try to get CWD from OS, fall back to cached value
+ self.id_to_child_pid
+ .get(terminal_pane_id)
+ .and_then(|&pid| {
+ self.bus
+ .os_input
+ .as_ref()
+ .and_then(|input| input.get_cwd(Pid::from_raw(pid)))
+ })
+ .or_else(|| self.terminal_cwds.get(terminal_pane_id).cloned())
},
PaneId::Plugin(plugin_id) => self.plugin_cwds.get(plugin_id).cloned(),
};
@@ -938,6 +986,8 @@ impl Pty {
self.task_handles.insert(terminal_id, terminal_bytes);
self.id_to_child_pid.insert(terminal_id, child_fd);
+ self.capture_initial_cwd(terminal_id, child_fd);
+
let starts_held = false;
Ok((terminal_id, starts_held))
}
@@ -948,7 +998,9 @@ impl Pty {
floating_panes_layout: Vec<FloatingPaneLayout>,
default_shell: Option<TerminalAction>,
plugin_ids: HashMap<RunPluginOrAlias, Vec<u32>>,
+ initial_panes: Option<Vec<CommandOrPlugin>>,
tab_index: usize,
+ block_on_first_terminal: bool,
should_change_focus_to_new_tab: bool,
client_id_and_is_web_client: (ClientId, bool),
completion_tx: Option<NotificationEnd>,
@@ -959,6 +1011,22 @@ impl Pty {
default_shell.unwrap_or_else(|| self.get_default_terminal(cwd, None));
let (client_id, is_web_client) = client_id_and_is_web_client;
self.fill_cwd(&mut default_shell, client_id);
+
+ // Match initial_panes commands to empty slots in the layout
+ let mut layout = layout;
+ if let Some(ref initial_panes_vec) = initial_panes {
+ for initial_pane in initial_panes_vec.iter() {
+ if let CommandOrPlugin::Command(run_command_action) = initial_pane {
+ let run_command: RunCommand = run_command_action.clone().into();
+ if !layout.replace_next_empty_slot_with_run(Run::Command(run_command)) {
+ log::warn!("More initial_panes provided than empty slots available");
+ break;
+ }
+ }
+ // Skip CommandOrPlugin::Plugin entries (already handled by plugin thread)
+ }
+ }
+
let extracted_run_instructions = layout.extract_run_instructions();
let extracted_floating_run_instructions = floating_panes_layout
.iter()
@@ -968,24 +1036,55 @@ impl Pty {
// starts_held,
// run_command,
// file_descriptor)
+
let mut new_floating_panes_pids: Vec<(u32, bool, Option<RunCommand>, Result<RawFd>)> =
vec![]; // same
// as
// new_pane_pids
+
+ let mut originating_plugins_to_inform = vec![];
+
for run_instruction in extracted_run_instructions {
+ let originating_plugin = run_instruction.as_ref().and_then(|r| {
+ if let Run::Command(run_command) = r {
+ run_command.originating_plugin.clone()
+ } else {
+ None
+ }
+ });
+ let mut terminal_id = None;
if let Some(new_pane_data) =
self.apply_run_instruction(run_instruction, default_shell.clone())?
{
+ terminal_id = Some(new_pane_data.0);
new_pane_pids.push(new_pane_data);
}
+ if let (Some(originating_plugin), Some(terminal_id)) = (originating_plugin, terminal_id)
+ {
+ originating_plugins_to_inform.push((terminal_id, originating_plugin));
+ }
}
for run_instruction in extracted_floating_run_instructions {
+ let originating_plugin = run_instruction.as_ref().and_then(|r| {
+ if let Run::Command(run_command) = r {
+ run_command.originating_plugin.clone()
+ } else {
+ None
+ }
+ });
+ let mut terminal_id = None;
if let Some(new_pane_data) =
self.apply_run_instruction(run_instruction, default_shell.clone())?
{
+ terminal_id = Some(new_pane_data.0);
new_floating_panes_pids.push(new_pane_data);
}
+ if let (Some(originating_plugin), Some(terminal_id)) = (originating_plugin, terminal_id)
+ {
+ originating_plugins_to_inform.push((terminal_id, originating_plugin));
+ }
}
+
// Option<RunCommand> should only be Some if the pane starts held
let new_tab_pane_ids: Vec<(u32, Option<RunCommand>)> = new_pane_pids
.iter()
@@ -1007,21 +1106,40 @@ impl Pty {
}
})
.collect();
+
+ // Track the first terminal_id if blocking is requested
+ let first_initial_pane_terminal_id = if block_on_first_terminal && !new_pane_pids.is_empty()
+ {
+ Some(new_pane_pids[0].0)
+ } else {
+ None
+ };
+
+ // Prepare blocking_terminal for ApplyLayout
+ let (direct_completion_tx, blocking_terminal) =
+ if let Some(terminal_id) = first_initial_pane_terminal_id {
+ (None, completion_tx.map(|tx| (terminal_id, tx)))
+ } else {
+ (completion_tx, None)
+ };
+
self.bus
.senders
.send_to_screen(ScreenInstruction::ApplyLayout(
layout,
floating_panes_layout,
- new_tab_pane_ids,
- new_tab_floating_pane_ids,
+ new_tab_pane_ids.clone(),
+ new_tab_floating_pane_ids.clone(),
plugin_ids,
tab_index,
should_change_focus_to_new_tab,
(client_id, is_web_client),
- completion_tx,
+ direct_completion_tx,
+ blocking_terminal,
))
.with_context(err_context)?;
let mut terminals_to_start = vec![];
+
terminals_to_start.append(&mut new_pane_pids);
terminals_to_start.append(&mut new_floating_panes_pids);
for (terminal_id, starts_held, run_command, pid_primary) in terminals_to_start {
@@ -1077,8 +1195,29 @@ impl Pty {
},
}
}
+ for (terminal_id, originating_plugin) in originating_plugins_to_inform {
+ self.inform_originating_plugin_of_open(terminal_id, originating_plugin);
+ }
Ok(())
}
+ fn inform_originating_plugin_of_open(
+ &mut self,
+ terminal_id: u32,
+ originating_plugin: OriginatingPlugin,
+ ) {
+ self.originating_plugins
+ .insert(terminal_id, originating_plugin.clone());
+ let update_event = Event::CommandPaneOpened(terminal_id, originating_plugin.context);
+ let _ = self
+ .bus
+ .senders
+ .send_to_plugin(PluginInstruction::Update(vec![(
+ Some(originating_plugin.plugin_id),
+ Some(originating_plugin.client_id),
+ update_event,
+ )]));
+ }
+
fn apply_run_instruction(
&mut self,
run_instruction: Option<Run>,
@@ -1162,6 +1301,7 @@ impl Pty {
{
Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd);
+ self.capture_initial_cwd(terminal_id, child_fd);
Ok(Some((
terminal_id,
starts_held,
@@ -1194,6 +1334,7 @@ impl Pty {
{
Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd);
+ self.capture_initial_cwd(terminal_id, child_fd);
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
},
Err(err) => match err.downcast_ref::<ZellijError>() {
@@ -1225,6 +1366,7 @@ impl Pty {
{
Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd);
+ self.capture_initial_cwd(terminal_id, child_fd);
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
},
Err(err) => match err.downcast_ref::<ZellijError>() {
@@ -1248,6 +1390,7 @@ impl Pty {
{
Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd);
+ self.capture_initial_cwd(terminal_id, child_fd);
Ok(Some((terminal_id, starts_held, None, Ok(pid_primary))))
},
Err(err) => match err.downcast_ref::<ZellijError>() {
@@ -1389,6 +1532,7 @@ impl Pty {
self.task_handles.insert(id, terminal_bytes);
self.id_to_child_pid.insert(id, child_fd);
+ self.capture_initial_cwd(id, child_fd);
if let Some(originating_plugin) = self.originating_plugins.get(&id) {
self.bus
.senders
@@ -1405,9 +1549,10 @@ impl Pty {
}
}
pub fn populate_session_layout_metadata(
- &self,
+ &mut self,
session_layout_metadata: &mut SessionLayoutMetadata,
) {
+ log::info!("populate_session_layout_metadata");
let terminal_ids = session_layout_metadata.all_terminal_ids();
let mut terminal_ids_to_commands: HashMap<u32, Vec<String>> = HashMap::new();
let mut terminal_ids_to_cwds: HashMap<u32, PathBuf> = HashMap::new();
@@ -1476,12 +1621,18 @@ impl Pty {
.get(&client_id)
.and_then(|pane| match pane {
PaneId::Plugin(plugin_id) => self.plugin_cwds.get(plugin_id).cloned(),
- PaneId::Terminal(id) => self.id_to_child_pid.get(id).and_then(|&id| {
- self.bus
- .os_input
- .as_ref()
- .and_then(|input| input.get_cwd(Pid::from_raw(id)))
- }),
+ PaneId::Terminal(id) => {
+ // Try to get CWD from OS, fall back to cached value
+ self.id_to_child_pid
+ .get(id)
+ .and_then(|&pid| {
+ self.bus
+ .os_input
+ .as_ref()
+ .and_then(|input| input.get_cwd(Pid::from_raw(pid)))
+ })
+ .or_else(|| self.terminal_cwds.get(id).cloned())
+ },
})
};
@@ -1516,6 +1667,59 @@ impl Pty {
))?;
Ok(())
}
+ fn capture_initial_cwd(&mut self, terminal_id: u32, child_fd: RawFd) {
+ if let Some(os_input) = self.bus.os_input.as_ref() {
+ if let Some(cwd) = os_input.get_cwd(Pid::from_raw(child_fd)) {
+ self.terminal_cwds.insert(terminal_id, cwd);
+ }
+ }
+ }
+
+ pub fn update_and_report_cwds(&mut self) {
+ let terminal_ids: Vec<u32> = self.id_to_child_pid.keys().copied().collect();
+
+ let pids: Vec<_> = terminal_ids
+ .iter()
+ .filter_map(|id| self.id_to_child_pid.get(&id))
+ .map(|pid| Pid::from_raw(*pid))
+ .collect();
+
+ let (pids_to_cwds, _) = self
+ .bus
+ .os_input
+ .as_ref()
+ .map(|os_input| os_input.get_cwds(pids))
+ .unwrap_or_default();
+
+ for terminal_id in terminal_ids {
+ let process_id = self.id_to_child_pid.get(&terminal_id);
+ let cwd = process_id
+ .as_ref()
+ .and_then(|pid| pids_to_cwds.get(&Pid::from_raw(**pid)));
+
+ if let Some(cwd) = cwd {
+ if self.terminal_cwds.get(&terminal_id) != Some(cwd) {
+ let pane_id = PaneId::Terminal(terminal_id);
+ let focused_client_ids: Vec<ClientId> = self
+ .active_panes
+ .iter()
+ .filter(|(_, active_pane)| *active_pane == &pane_id)
+ .map(|(client_id, _)| *client_id)
+ .collect();
+ let _ = self
+ .bus
+ .senders
+ .send_to_plugin(PluginInstruction::Update(vec![(
+ None,
+ None,
+ Event::CwdChanged(pane_id.into(), cwd.clone(), focused_client_ids),
+ )]));
+ }
+ self.terminal_cwds.insert(terminal_id, cwd.clone());
+ }
+ }
+ }
+
pub fn reconfigure(
&mut self,
default_editor: Option<PathBuf>,
@@ -1524,6 +1728,72 @@ impl Pty {
self.default_editor = default_editor;
self.post_command_discovery_hook = post_command_discovery_hook;
}
+
+ pub fn send_sigint_to_pane(&self, pane_id: PaneId) {
+ let err_context = || format!("failed to send SIGINT to pane {:?}", pane_id);
+
+ match pane_id {
+ PaneId::Terminal(terminal_id) => {
+ if let Some(&child_fd) = self.id_to_child_pid.get(&terminal_id) {
+ let pid = Pid::from_raw(child_fd);
+ self.bus
+ .os_input
+ .as_ref()
+ .context("no OS I/O interface found")
+ .and_then(|os_input| os_input.send_sigint(pid))
+ .with_context(err_context)
+ .non_fatal();
+ } else {
+ log::warn!("Terminal pane {} not found or not running", terminal_id);
+ }
+ },
+ PaneId::Plugin(plugin_id) => {
+ log::warn!("Cannot send SIGINT to plugin pane {}", plugin_id);
+ },
+ }
+ }
+
+ pub fn send_sigkill_to_pane(&self, pane_id: PaneId) {
+ let err_context = || format!("failed to send SIGKILL to pane {:?}", pane_id);
+
+ match pane_id {
+ PaneId::Terminal(terminal_id) => {
+ if let Some(&child_fd) = self.id_to_child_pid.get(&terminal_id) {
+ let pid = Pid::from_raw(child_fd);
+ self.bus
+ .os_input
+ .as_ref()
+ .context("no OS I/O interface found")
+ .and_then(|os_input| os_input.force_kill(pid))
+ .with_context(err_context)
+ .non_fatal();
+ } else {
+ log::warn!("Terminal pane {} not found or not running", terminal_id);
+ }
+ },
+ PaneId::Plugin(plugin_id) => {
+ log::warn!("Cannot send SIGKILL to plugin pane {}", plugin_id);
+ },
+ }
+ }
+
+ pub fn get_pane_pid(&self, pane_id: PaneId) -> GetPanePidResponse {
+ match pane_id {
+ PaneId::Terminal(terminal_id) => {
+ if let Some(&child_fd) = self.id_to_child_pid.get(&terminal_id) {
+ GetPanePidResponse::Ok(child_fd)
+ } else {
+ GetPanePidResponse::Err(format!(
+ "Terminal pane {} not found or not running",
+ terminal_id
+ ))
+ }
+ },
+ PaneId::Plugin(plugin_id) => {
+ GetPanePidResponse::Err(format!("Cannot get PID for plugin pane {}", plugin_id))
+ },
+ }
+ }
}
impl Drop for Pty {
diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs
index 7d11ba65..ead4282d 100644
--- a/zellij-server/src/route.rs
+++ b/zellij-server/src/route.rs
@@ -19,7 +19,7 @@ use zellij_utils::{
channels::SenderWithContext,
data::{
BareKey, ConnectToSession, Direction, Event, InputMode, KeyModifier, NewPanePlacement,
- PluginCapabilities, ResizeStrategy,
+ PluginCapabilities, ResizeStrategy, UnblockCondition,
},
envs,
errors::prelude::*,
@@ -39,21 +39,28 @@ use crate::ClientId;
const ACTION_COMPLETION_TIMEOUT: Duration = Duration::from_secs(1);
+#[derive(Debug, Clone)]
+pub struct ActionCompletionResult {
+ pub exit_status: Option<i32>,
+ pub affected_pane_id: Option<PaneId>,
+}
+
fn wait_for_action_completion(
- receiver: oneshot::Receiver<Option<i32>>, // currently i32 signals an exit status, can be expanded as
- // needed
+ receiver: oneshot::Receiver<ActionCompletionResult>,
action_name: &str,
wait_forever: bool,
-) -> Option<i32> {
+) -> ActionCompletionResult {
let runtime = get_tokio_runtime();
if wait_forever {
runtime.block_on(async {
match receiver.await {
- Ok(Some(payload)) => Some(payload),
- Ok(None) => None,
+ Ok(result) => result,
Err(e) => {
log::error!("Failed to wait for action {}: {}", action_name, e);
- None
+ ActionCompletionResult {
+ exit_status: None,
+ affected_pane_id: None,
+ }
},
}
})
@@ -61,15 +68,17 @@ fn wait_for_action_completion(
match runtime
.block_on(async { tokio::time::timeout(ACTION_COMPLETION_TIMEOUT, receiver).await })
{
- Ok(Ok(Some(payload))) => Some(payload),
- Ok(Ok(None)) => None,
+ Ok(Ok(result)) => result,
Err(_) | Ok(Err(_)) => {
log::error!(
"Action {} did not complete within {:?} timeout",
action_name,
ACTION_COMPLETION_TIMEOUT
);
- None
+ ActionCompletionResult {
+ exit_status: None,
+ affected_pane_id: None,
+ }
},
}
}
@@ -83,8 +92,10 @@ fn wait_for_action_completion(
// that it can be included in various other larger structs - DO NOT RELY ON CLONING IT!
#[derive(Debug)]
pub struct NotificationEnd {
- channel: Option<oneshot::Sender<Option<i32>>>,
+ channel: Option<oneshot::Sender<ActionCompletionResult>>,
exit_status: Option<i32>,
+ unblock_condition: Option<UnblockCondition>,
+ affected_pane_id: Option<PaneId>, // optional payload of the pane id affected by this action
}
impl Clone for NotificationEnd {
@@ -93,26 +104,55 @@ impl Clone for NotificationEnd {
NotificationEnd {
channel: None,
exit_status: self.exit_status,
+ unblock_condition: self.unblock_condition,
+ affected_pane_id: self.affected_pane_id,
}
}
}
impl NotificationEnd {
- pub fn new(sender: oneshot::Sender<Option<i32>>) -> Self {
+ pub fn new(sender: oneshot::Sender<ActionCompletionResult>) -> Self {
+ NotificationEnd {
+ channel: Some(sender),
+ exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
+ }
+ }
+
+ pub fn new_with_condition(
+ sender: oneshot::Sender<ActionCompletionResult>,
+ unblock_condition: UnblockCondition,
+ ) -> Self {
NotificationEnd {
channel: Some(sender),
exit_status: None,
+ unblock_condition: Some(unblock_condition),
+ affected_pane_id: None,
}
}
+
pub fn set_exit_status(&mut self, exit_status: i32) {
self.exit_status = Some(exit_status);
}
+
+ pub fn set_affected_pane_id(&mut self, pane_id: PaneId) {
+ self.affected_pane_id = Some(pane_id);
+ }
+
+ pub fn unblock_condition(&self) -> Option<UnblockCondition> {
+ self.unblock_condition
+ }
}
impl Drop for NotificationEnd {
fn drop(&mut self) {
if let Some(tx) = self.channel.take() {
- let _ = tx.send(self.exit_status);
+ let result = ActionCompletionResult {
+ exit_status: self.exit_status,
+ affected_pane_id: self.affected_pane_id,
+ };
+ let _ = tx.send(result);
}
}
}
@@ -131,7 +171,7 @@ pub(crate) fn route_action(
client_keybinds: Keybinds,
default_mode: InputMode,
os_input: Option<Box<dyn ServerOsApi>>,
-) -> Result<bool> {
+) -> Result<(bool, Option<ActionCompletionResult>)> {
let mut should_break = false;
let err_context = || format!("failed to route action for client {client_id}");
let action_name = action.to_string();
@@ -480,19 +520,51 @@ pub(crate) fn route_action(
placement,
pane_name,
command,
+ unblock_condition,
+ near_current_pane,
} => {
let command = command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
let set_pane_blocking = true;
+
+ let notification_end = if let Some(condition) = unblock_condition {
+ Some(NotificationEnd::new_with_condition(
+ completion_tx,
+ condition,
+ ))
+ } else {
+ Some(NotificationEnd::new(completion_tx))
+ };
+
+ // we prefer the pane id provided by the action explicitly over the one that originated
+ // it (this might be a bit misleading with "near_current_pane", but it's still the
+ // right behavior - in the latter case, if the originator does not wish for this
+ // behavior, they should not provide pane
+ // inside the placement, but rather have the current pane id be picked up instead)
+ let pane_id = match placement {
+ NewPanePlacement::Stacked(pane_id_to_stack_under) => {
+ pane_id_to_stack_under.map(|p| p.into()).or(pane_id)
+ },
+ NewPanePlacement::InPlace {
+ pane_id_to_replace, ..
+ } => pane_id_to_replace.map(|p| p.into()).or(pane_id),
+ _ => pane_id,
+ };
+
+ let client_tab_index_or_paneid = if near_current_pane && pane_id.is_some() {
+ ClientTabIndexOrPaneId::PaneId(pane_id.unwrap())
+ } else {
+ ClientTabIndexOrPaneId::ClientId(client_id)
+ };
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
command,
pane_name,
placement,
false,
- ClientTabIndexOrPaneId::ClientId(client_id),
- Some(NotificationEnd::new(completion_tx)),
+ client_tab_index_or_paneid,
+ notification_end,
set_pane_blocking,
))
.with_context(err_context)?;
@@ -505,19 +577,20 @@ pub(crate) fn route_action(
in_place: should_open_in_place,
start_suppressed,
coordinates: floating_pane_coordinates,
+ near_current_pane,
} => {
let title = format!("Editing: {}", open_file_payload.path.display());
let open_file = TerminalAction::OpenFile(open_file_payload);
let pty_instr = if should_open_in_place {
match pane_id {
- Some(pane_id) => PtyInstruction::SpawnInPlaceTerminal(
+ Some(pane_id) if near_current_pane => PtyInstruction::SpawnInPlaceTerminal(
Some(open_file),
Some(title),
false,
ClientTabIndexOrPaneId::PaneId(pane_id),
Some(NotificationEnd::new(completion_tx)),
),
- None => PtyInstruction::SpawnInPlaceTerminal(
+ _ => PtyInstruction::SpawnInPlaceTerminal(
Some(open_file),
Some(title),
false,
@@ -579,17 +652,23 @@ pub(crate) fn route_action(
command: run_command,
pane_name: name,
coordinates: floating_pane_coordinates,
+ near_current_pane,
} => {
let run_cmd = run_command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
+ let client_tab_index_or_paneid = if near_current_pane && pane_id.is_some() {
+ ClientTabIndexOrPaneId::PaneId(pane_id.unwrap())
+ } else {
+ ClientTabIndexOrPaneId::ClientId(client_id)
+ };
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
run_cmd,
name,
NewPanePlacement::Floating(floating_pane_coordinates),
false,
- ClientTabIndexOrPaneId::ClientId(client_id),
+ client_tab_index_or_paneid,
Some(NotificationEnd::new(completion_tx)),
false, // set_blocking
))
@@ -598,28 +677,35 @@ pub(crate) fn route_action(
Action::NewInPlacePane {
command: run_command,
pane_name: name,
+ near_current_pane,
+ pane_id_to_replace,
+ close_replace_pane,
} => {
let run_cmd = run_command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
+ let pane_id = match pane_id_to_replace {
+ Some(pane_id_to_replace) => pane_id_to_replace.try_into().ok(),
+ None => pane_id,
+ };
match pane_id {
- Some(pane_id) => {
+ Some(pane_id) if near_current_pane => {
senders
.send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
run_cmd,
name,
- false,
+ close_replace_pane,
ClientTabIndexOrPaneId::PaneId(pane_id),
Some(NotificationEnd::new(completion_tx)),
))
.with_context(err_context)?;
},
- None => {
+ _ => {
senders
.send_to_pty(PtyInstruction::SpawnInPlaceTerminal(
run_cmd,
name,
- false,
+ close_replace_pane,
ClientTabIndexOrPaneId::ClientId(client_id),
Some(NotificationEnd::new(completion_tx)),
))
@@ -630,12 +716,14 @@ pub(crate) fn route_action(
Action::NewStackedPane {
command: run_command,
pane_name: name,
+ near_current_pane,
} => {
let run_cmd = run_command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
+
match pane_id {
- Some(pane_id) => {
+ Some(pane_id) if near_current_pane => {
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
run_cmd,
@@ -648,7 +736,7 @@ pub(crate) fn route_action(
))
.with_context(err_context)?;
},
- None => {
+ _ => {
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
run_cmd,
@@ -667,17 +755,23 @@ pub(crate) fn route_action(
direction,
command: run_command,
pane_name: name,
+ near_current_pane,
} => {
let run_cmd = run_command
.map(|cmd| TerminalAction::RunCommand(cmd.into()))
.or_else(|| default_shell.clone());
+ let client_tab_index_or_paneid = if near_current_pane && pane_id.is_some() {
+ ClientTabIndexOrPaneId::PaneId(pane_id.unwrap())
+ } else {
+ ClientTabIndexOrPaneId::ClientId(client_id)
+ };
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
run_cmd,
name,
NewPanePlacement::Tiled(direction),
false,
- ClientTabIndexOrPaneId::ClientId(client_id),
+ client_tab_index_or_paneid,
Some(NotificationEnd::new(completion_tx)),
false, // set_blocking
))
@@ -717,15 +811,23 @@ pub(crate) fn route_action(
))
.with_context(err_context)?;
},
- Action::Run { command } => {
+ Action::Run {
+ command,
+ near_current_pane,
+ } => {
let run_cmd = Some(TerminalAction::RunCommand(command.clone().into()));
+ let client_tab_index_or_paneid = if near_current_pane && pane_id.is_some() {
+ ClientTabIndexOrPaneId::PaneId(pane_id.unwrap())
+ } else {
+ ClientTabIndexOrPaneId::ClientId(client_id)
+ };
senders
.send_to_pty(PtyInstruction::SpawnTerminal(
run_cmd,
None,
NewPanePlacement::Tiled(command.direction),
false,
- ClientTabIndexOrPaneId::ClientId(client_id),
+ client_tab_index_or_paneid,
Some(NotificationEnd::new(completion_tx)),
false, // set_blocking
))
@@ -747,6 +849,8 @@ pub(crate) fn route_action(
tab_name,
should_change_focus_to_new_tab,
cwd,
+ initial_panes,
+ first_pane_unblock_condition,
} => {
let shell = default_shell.clone();
let swap_tiled_layouts =
@@ -755,6 +859,17 @@ pub(crate) fn route_action(
.unwrap_or_else(|| default_layout.swap_floating_layouts.clone());
let is_web_client = false; // actions cannot be initiated directly from the web
+ // Construct completion_tx conditionally
+ let (completion_tx, block_on_first_terminal) = if let Some(condition) =
+ first_pane_unblock_condition
+ {
+ let notification = NotificationEnd::new_with_condition(completion_tx, condition);
+ wait_forever = true;
+ (notification, true)
+ } else {
+ (NotificationEnd::new(completion_tx), false)
+ };
+
senders
.send_to_screen(ScreenInstruction::NewTab(
cwd,
@@ -763,9 +878,11 @@ pub(crate) fn route_action(
floating_panes_layout,
tab_name,
(swap_tiled_layouts, swap_floating_layouts),
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
(client_id, is_web_client),
- Some(NotificationEnd::new(completion_tx)),
+ Some(completion_tx),
))
.with_context(err_context)?;
},
@@ -921,20 +1038,12 @@ pub(crate) fn route_action(
.with_context(err_context)?;
},
Action::Confirm => {
- senders
- .send_to_screen(ScreenInstruction::ConfirmPrompt(
- client_id,
- Some(NotificationEnd::new(completion_tx)),
- ))
- .with_context(err_context)?;
+ // no-op, these are deprecated and should be removed when we upgrade the server/client
+ // contract
},
Action::Deny => {
- senders
- .send_to_screen(ScreenInstruction::DenyPrompt(
- client_id,
- Some(NotificationEnd::new(completion_tx)),
- ))
- .with_context(err_context)?;
+ // no-op, these are deprecated and should be removed when we upgrade the server/client
+ // contract
},
#[allow(clippy::single_match)]
Action::SkipConfirm { action } => match *action {
@@ -1147,11 +1256,13 @@ pub(crate) fn route_action(
Action::FocusTerminalPaneWithId {
pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
} => {
senders
.send_to_screen(ScreenInstruction::FocusPaneWithId(
PaneId::Terminal(pane_id),
should_float_if_hidden,
+ should_be_in_place_if_hidden,
client_id,
Some(NotificationEnd::new(completion_tx)),
))
@@ -1160,11 +1271,13 @@ pub(crate) fn route_action(
Action::FocusPluginPaneWithId {
pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
} => {
senders
.send_to_screen(ScreenInstruction::FocusPaneWithId(
PaneId::Plugin(pane_id),
should_float_if_hidden,
+ should_be_in_place_if_hidden,
client_id,
Some(NotificationEnd::new(completion_tx)),
))
@@ -1400,8 +1513,8 @@ pub(crate) fn route_action(
.with_context(err_context)?;
},
}
- let exit_status = wait_for_action_completion(completion_rx, &action_name, wait_forever);
- if let Some(exit_status) = exit_status {
+ let result = wait_for_action_completion(completion_rx, &action_name, wait_forever);
+ if let Some(exit_status) = result.exit_status {
if let Some(cli_client_id) = cli_client_id {
if let Some(os_input) = os_input {
let _ = os_input.send_to_client(
@@ -1413,14 +1526,14 @@ pub(crate) fn route_action(
}
}
}
- Ok(should_break)
+ Ok((should_break, Some(result)))
}
// this should only be used for one-off startup instructions
macro_rules! send_to_screen_or_retry_queue {
- ($rlocked_sessions:expr, $message:expr, $instruction: expr, $retry_queue:expr) => {{
- match $rlocked_sessions.as_ref() {
- Some(session_metadata) => session_metadata.senders.send_to_screen($message),
+ ($senders:expr, $message:expr, $instruction: expr, $retry_queue:expr) => {{
+ match $senders.as_ref() {
+ Some(senders) => senders.send_to_screen($message),
None => {
log::warn!("Server not ready, trying to place instruction in retry queue...");
if let Some(retry_queue) = $retry_queue.as_mut() {
@@ -1455,8 +1568,11 @@ pub(crate) fn route_thread_main(
>|
-> Result<bool> {
let mut should_break = false;
- let rlocked_sessions =
- session_data.read().to_anyhow().with_context(err_context)?;
+ let senders = session_data
+ .read()
+ .to_anyhow()
+ .ok()
+ .and_then(|r| r.as_ref().map(|r| r.senders.clone()));
// Check if this is a watcher client and ignore input messages
let is_watcher = session_state.read().unwrap().is_watcher(&client_id);
@@ -1475,10 +1591,10 @@ pub(crate) fn route_thread_main(
exit_reason: ExitReason::Normal,
},
);
- let _ = rlocked_sessions.as_ref().map(|r| {
- r.senders.send_to_screen(
- ScreenInstruction::RemoveWatcherClient(client_id),
- )
+ let _ = senders.as_ref().map(|s| {
+ s.send_to_screen(ScreenInstruction::RemoveWatcherClient(
+ client_id,
+ ))
});
should_break = true;
}
@@ -1487,7 +1603,7 @@ pub(crate) fn route_thread_main(
// For watchers: send size to Screen for rendering adjustments, but
// this does not affect the screen size
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders,
ScreenInstruction::WatcherTerminalResize(client_id, *new_size),
instruction.clone(),
retry_queue
@@ -1514,46 +1630,75 @@ pub(crate) fn route_thread_main(
.unwrap()
.set_last_active_client(client_id);
- if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
- match rlocked_sessions.get_client_keybinds_and_mode(&client_id) {
- Some((keybinds, input_mode, default_input_mode)) => {
- for action in keybinds
- .get_actions_for_key_in_mode_or_default_action(
- &input_mode,
- &key,
- raw_bytes,
- default_input_mode,
- is_kitty_keyboard_protocol,
- )
- {
- if route_action(
- action,
+ let session_data_assets =
+ session_data.read().as_ref().unwrap().as_ref().map(|s| {
+ (
+ s.senders.clone(),
+ s.capabilities.clone(),
+ s.client_attributes.clone(),
+ s.default_shell.clone(),
+ s.layout.clone(),
+ s.session_configuration
+ .get_client_configuration(&client_id)
+ .options
+ .default_mode
+ .unwrap_or(InputMode::Normal)
+ .clone(),
+ )
+ });
+ if let Some((keybinds, input_mode, default_input_mode)) = session_data
+ .read()
+ .unwrap()
+ .as_ref()
+ .and_then(|s| s.get_client_keybinds_and_mode(&client_id))
+ {
+ if let Some((
+ senders,
+ capabilities,
+ client_attributes,
+ default_shell,
+ layout,
+ client_input_mode,
+ )) = session_data_assets
+ {
+ for action in keybinds
+ .get_actions_for_key_in_mode_or_default_action(
+ &input_mode,
+ &key,
+ raw_bytes,
+ default_input_mode,
+ is_kitty_keyboard_protocol,
+ )
+ {
+ // Send user input to plugin thread for logging
+ let _ =
+ senders.send_to_plugin(PluginInstruction::UserInput {
client_id,
- None,
- None,
- rlocked_sessions.senders.clone(),
- rlocked_sessions.capabilities.clone(),
- rlocked_sessions.client_attributes.clone(),
- rlocked_sessions.default_shell.clone(),
- rlocked_sessions.layout.clone(),
- Some(&mut seen_cli_pipes),
- keybinds.clone(),
- rlocked_sessions
- .session_configuration
- .get_client_configuration(&client_id)
- .options
- .default_mode
- .unwrap_or(InputMode::Normal)
- .clone(),
- Some(os_input.clone()),
- )? {
- should_break = true;
- }
+ action: action.clone(),
+ terminal_id: None,
+ cli_client_id: None,
+ });
+
+ if route_action(
+ action,
+ client_id,
+ None,
+ None,
+ senders.clone(),
+ capabilities,
+ client_attributes.clone(),
+ default_shell.clone(),
+ layout.clone(),
+ Some(&mut seen_cli_pipes),
+ keybinds.clone(),
+ client_input_mode,
+ Some(os_input.clone()),
+ )?
+ .0
+ {
+ should_break = true;
}
- },
- None => {
- log::error!("Failed to get keybindings for client");
- },
+ }
}
}
},
@@ -1580,31 +1725,67 @@ pub(crate) fn route_thread_main(
} else {
maybe_client_id.unwrap_or(client_id)
};
- if let Some(rlocked_sessions) = rlocked_sessions.as_ref() {
+
+ // Send user input to plugin thread for logging
+ if let Some(ref senders) = senders {
+ let _ = senders.send_to_plugin(PluginInstruction::UserInput {
+ client_id,
+ action: action.clone(),
+ terminal_id: maybe_pane_id,
+ cli_client_id: if is_cli_client {
+ Some(cli_client_id)
+ } else {
+ None
+ },
+ });
+ }
+
+ let session_data_assets =
+ session_data.read().unwrap().as_ref().map(|s| {
+ (
+ s.senders.clone(),
+ s.capabilities.clone(),
+ s.client_attributes.clone(),
+ s.default_shell.clone(),
+ s.layout.clone(),
+ s.session_configuration
+ .get_client_configuration(&client_id)
+ .options
+ .default_mode
+ .unwrap_or(InputMode::Normal)
+ .clone(),
+ s.session_configuration
+ .get_client_keybinds(&client_id)
+ .clone(),
+ )
+ });
+ if let Some((
+ senders,
+ capabilities,
+ client_attributes,
+ default_shell,
+ layout,
+ client_input_mode,
+ client_keybinds,
+ )) = session_data_assets
+ {
if route_action(
action,
client_id,
Some(cli_client_id),
maybe_pane_id.map(|p| PaneId::Terminal(p)),
- rlocked_sessions.senders.clone(),
- rlocked_sessions.capabilities.clone(),
- rlocked_sessions.client_attributes.clone(),
- rlocked_sessions.default_shell.clone(),
- rlocked_sessions.layout.clone(),
+ senders,
+ capabilities,
+ client_attributes,
+ default_shell,
+ layout,
Some(&mut seen_cli_pipes),
- rlocked_sessions
- .session_configuration
- .get_client_keybinds(&client_id)
- .clone(),
- rlocked_sessions
- .session_configuration
- .get_client_configuration(&client_id)
- .options
- .default_mode
- .unwrap_or(InputMode::Normal)
- .clone(),
+ client_keybinds,
+ client_input_mode,
Some(os_input.clone()),
- )? {
+ )?
+ .0
+ {
should_break = true;
}
}
@@ -1614,7 +1795,7 @@ pub(crate) fn route_thread_main(
if is_watcher {
// For watchers: send size to Screen for tracking, don't affect screen size
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders.clone(),
ScreenInstruction::WatcherTerminalResize(client_id, new_size),
instruction,
retry_queue
@@ -1635,22 +1816,19 @@ pub(crate) fn route_thread_main(
))
})
.and_then(|min_size| {
- rlocked_sessions
- .as_ref()
- .context(
- "couldn't get reference to read-locked session",
- )?
- .senders
- .send_to_screen(ScreenInstruction::TerminalResize(
+ let _ = senders.as_ref().map(|s| {
+ s.send_to_screen(ScreenInstruction::TerminalResize(
min_size,
))
+ });
+ Ok(())
})
.with_context(err_context)?;
}
},
ClientToServerMsg::TerminalPixelDimensions { pixel_dimensions } => {
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders,
ScreenInstruction::TerminalPixelDimensions(pixel_dimensions),
instruction,
retry_queue
@@ -1661,7 +1839,7 @@ pub(crate) fn route_thread_main(
color: ref background_color_instruction,
} => {
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders,
ScreenInstruction::TerminalBackgroundColor(
background_color_instruction.clone()
),
@@ -1674,7 +1852,7 @@ pub(crate) fn route_thread_main(
color: ref foreground_color_instruction,
} => {
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders,
ScreenInstruction::TerminalForegroundColor(
foreground_color_instruction.clone()
),
@@ -1687,7 +1865,7 @@ pub(crate) fn route_thread_main(
ref color_registers,
} => {
send_to_screen_or_retry_queue!(
- rlocked_sessions,
+ senders,
ScreenInstruction::TerminalColorRegisters(
color_registers
.iter()
@@ -1718,10 +1896,11 @@ pub(crate) fn route_thread_main(
pane_to_focus: pane_id_to_focus,
is_web_client,
} => {
- let allow_web_connections = rlocked_sessions
- .as_ref()
- .map(|rlocked_sessions| {
- rlocked_sessions.web_sharing.web_clients_allowed()
+ let allow_web_connections = session_data
+ .read()
+ .ok()
+ .and_then(|s| {
+ s.as_ref().map(|s| s.web_sharing.web_clients_allowed())
})
.unwrap_or(false);
let should_allow_connection = !is_web_client || allow_web_connections;
@@ -1801,7 +1980,9 @@ pub(crate) fn route_thread_main(
},
None => {
consecutive_unknown_messages_received += 1;
- log::error!("Received unknown message from client.");
+ if consecutive_unknown_messages_received == 1 {
+ log::error!("Received unknown message from client.");
+ }
if consecutive_unknown_messages_received >= 1000 {
log::error!("Client sent over 1000 consecutive unknown messages, this is probably an infinite loop, logging client out");
let _ = os_input.send_to_client(
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 3c83d6b0..c8998831 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -12,9 +12,9 @@ use crate::route::NotificationEnd;
use log::{debug, warn};
use zellij_utils::data::{
- Direction, FloatingPaneCoordinates, KeyWithModifier, NewPanePlacement, PaneContents,
- PaneManifest, PaneScrollbackResponse, PluginPermission, Resize, ResizeStrategy, SessionInfo,
- Styling, WebSharing,
+ CommandOrPlugin, Direction, FloatingPaneCoordinates, KeyWithModifier, NewPanePlacement,
+ PaneContents, PaneManifest, PaneScrollbackResponse, PluginPermission, Resize, ResizeStrategy,
+ SessionInfo, Styling, WebSharing,
};
use zellij_utils::errors::prelude::*;
use zellij_utils::input::command::RunCommand;
@@ -51,10 +51,7 @@ use crate::{
pty::{get_default_shell, ClientTabIndexOrPaneId, PtyInstruction, VteBytes},
tab::{SuppressedPanes, Tab},
thread_bus::Bus,
- ui::{
- loading_indication::LoadingIndication,
- overlay::{Overlay, OverlayWindow},
- },
+ ui::loading_indication::LoadingIndication,
ClientId, ServerInstruction,
};
use zellij_utils::{
@@ -218,6 +215,7 @@ pub enum ScreenInstruction {
ToggleActiveTerminalFullscreen(ClientId, Option<NotificationEnd>),
TogglePaneFrames(Option<NotificationEnd>),
SetSelectable(PaneId, bool),
+ ShowPluginCursor(u32, ClientId, Option<(usize, usize)>),
ClosePane(
PaneId,
Option<ClientId>,
@@ -235,6 +233,8 @@ pub enum ScreenInstruction {
Vec<FloatingPaneLayout>,
Option<String>,
(Vec<SwapTiledLayout>, Vec<SwapFloatingLayout>), // swap layouts
+ Option<Vec<CommandOrPlugin>>, // initial_panes
+ bool, // block_on_first_terminal
bool, // should_change_focus_to_new_tab
(ClientId, bool), // bool -> is_web_client
Option<NotificationEnd>, // completion signal
@@ -245,10 +245,11 @@ pub enum ScreenInstruction {
Vec<(u32, HoldForCommand)>, // new pane pids
Vec<(u32, HoldForCommand)>, // new floating pane pids
HashMap<RunPluginOrAlias, Vec<u32>>,
- usize, // tab_index
- bool, // should change focus to new tab
- (ClientId, bool), // bool -> is_web_client
- Option<NotificationEnd>, // completion signal
+ usize, // tab_index
+ bool, // should change focus to new tab
+ (ClientId, bool), // bool -> is_web_client
+ Option<NotificationEnd>, // regular completion signal
+ Option<(u32, NotificationEnd)>, // blocking_terminal (terminal_id, completion_tx)
),
SwitchTabNext(ClientId, Option<NotificationEnd>),
SwitchTabPrev(ClientId, Option<NotificationEnd>),
@@ -284,10 +285,6 @@ pub enum ScreenInstruction {
Option<(u32, bool)>, // (pane_id, is_plugin) => pane_id to focus
),
RemoveClient(ClientId),
- AddOverlay(Overlay, ClientId),
- RemoveOverlay(ClientId),
- ConfirmPrompt(ClientId, Option<NotificationEnd>),
- DenyPrompt(ClientId, Option<NotificationEnd>),
UpdateSearch(Vec<u8>, ClientId, Option<NotificationEnd>),
SearchDown(ClientId, Option<NotificationEnd>),
SearchUp(ClientId, Option<NotificationEnd>),
@@ -368,8 +365,12 @@ pub enum ScreenInstruction {
ClientId,
Option<NotificationEnd>,
), // bools are: should_float, should_open_in_place Option<PaneId> is the pane id to replace, Option<PathBuf> is an optional cwd, bool after is skip_cache
- SuppressPane(PaneId, ClientId), // bool is should_float
- FocusPaneWithId(PaneId, bool, ClientId, Option<NotificationEnd>), // bool is should_float
+ SuppressPane(PaneId, ClientId),
+ UnsuppressPane(PaneId, bool), // bool -> should float if hidden
+ UnsuppressOrExpandPane(PaneId, bool), // bool -> should float if hidden
+ FocusPaneWithId(PaneId, bool, bool, ClientId, Option<NotificationEnd>), // bools:
+ // should_float_if_hidden,
+ // should_be_in_place_if_hidden
RenamePane(PaneId, Vec<u8>, Option<NotificationEnd>),
RenameTab(usize, Vec<u8>, Option<NotificationEnd>),
RequestPluginPermissions(
@@ -422,6 +423,7 @@ pub enum ScreenInstruction {
ResizePaneWithId(ResizeStrategy, PaneId),
EditScrollbackForPaneWithId(PaneId, Option<NotificationEnd>),
WriteToPaneId(Vec<u8>, PaneId),
+ CopyTextToClipboard(String, u32), // String - text to copy, u32 - plugin_id
MovePaneWithPaneId(PaneId),
MovePaneWithPaneIdInDirection(PaneId, Direction),
ClearScreenForPaneId(PaneId),
@@ -467,7 +469,7 @@ pub enum ScreenInstruction {
SetMouseSelectionSupport(PaneId, bool),
InterceptKeyPresses(PluginId, ClientId),
ClearKeyPressesIntercepts(ClientId),
- ReplacePaneWithExistingPane(PaneId, PaneId),
+ ReplacePaneWithExistingPane(PaneId, PaneId, bool), // bool -> suppress_replaced_pane
AddWatcherClient(ClientId, Size),
RemoveWatcherClient(ClientId),
SetFollowedClient(ClientId),
@@ -553,6 +555,7 @@ impl From<&ScreenInstruction> for ScreenContext {
},
ScreenInstruction::TogglePaneFrames(..) => ScreenContext::TogglePaneFrames,
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
+ ScreenInstruction::ShowPluginCursor(..) => ScreenContext::ShowPluginCursor,
ScreenInstruction::ClosePane(..) => ScreenContext::ClosePane,
ScreenInstruction::HoldPane(..) => ScreenContext::HoldPane,
ScreenInstruction::UpdatePaneName(..) => ScreenContext::UpdatePaneName,
@@ -591,10 +594,6 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::ToggleTab(..) => ScreenContext::ToggleTab,
ScreenInstruction::AddClient(..) => ScreenContext::AddClient,
ScreenInstruction::RemoveClient(..) => ScreenContext::RemoveClient,
- ScreenInstruction::AddOverlay(..) => ScreenContext::AddOverlay,
- ScreenInstruction::RemoveOverlay(..) => ScreenContext::RemoveOverlay,
- ScreenInstruction::ConfirmPrompt(..) => ScreenContext::ConfirmPrompt,
- ScreenInstruction::DenyPrompt(..) => ScreenContext::DenyPrompt,
ScreenInstruction::UpdateSearch(..) => ScreenContext::UpdateSearch,
ScreenInstruction::SearchDown(..) => ScreenContext::SearchDown,
ScreenInstruction::SearchUp(..) => ScreenContext::SearchUp,
@@ -633,6 +632,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::LaunchOrFocusPlugin(..) => ScreenContext::LaunchOrFocusPlugin,
ScreenInstruction::LaunchPlugin(..) => ScreenContext::LaunchPlugin,
ScreenInstruction::SuppressPane(..) => ScreenContext::SuppressPane,
+ ScreenInstruction::UnsuppressPane(..) => ScreenContext::UnsuppressPane,
+ ScreenInstruction::UnsuppressOrExpandPane(..) => ScreenContext::UnsuppressOrExpandPane,
ScreenInstruction::FocusPaneWithId(..) => ScreenContext::FocusPaneWithId,
ScreenInstruction::RenamePane(..) => ScreenContext::RenamePane,
ScreenInstruction::RenameTab(..) => ScreenContext::RenameTab,
@@ -657,6 +658,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenContext::EditScrollbackForPaneWithId
},
ScreenInstruction::WriteToPaneId(..) => ScreenContext::WriteToPaneId,
+ ScreenInstruction::CopyTextToClipboard(..) => ScreenContext::CopyTextToClipboard,
ScreenInstruction::MovePaneWithPaneId(..) => ScreenContext::MovePaneWithPaneId,
ScreenInstruction::MovePaneWithPaneIdInDirection(..) => {
ScreenContext::MovePaneWithPaneIdInDirection
@@ -848,14 +850,13 @@ pub(crate) struct Screen {
character_cell_size: Rc<RefCell<Option<SizeInPixels>>>,
stacked_resize: Rc<RefCell<bool>>,
sixel_image_store: Rc<RefCell<SixelImageStore>>,
- /// The overlay that is drawn on top of [`Pane`]'s', [`Tab`]'s and the [`Screen`]
- overlay: OverlayWindow,
terminal_emulator_colors: Rc<RefCell<Palette>>,
terminal_emulator_color_codes: Rc<RefCell<HashMap<usize, String>>>,
connected_clients: Rc<RefCell<HashMap<ClientId, bool>>>, // bool -> is_web_client
/// The indices of this [`Screen`]'s active [`Tab`]s.
active_tab_indices: BTreeMap<ClientId, usize>,
tab_history: BTreeMap<ClientId, Vec<usize>>,
+ pane_history: BTreeMap<ClientId, Vec<PaneId>>,
mode_info: BTreeMap<ClientId, ModeInfo>,
default_mode_info: ModeInfo, // TODO: restructure ModeInfo to prevent this duplication
style: Style,
@@ -942,10 +943,10 @@ impl Screen {
connected_clients: Rc::new(RefCell::new(HashMap::new())),
active_tab_indices: BTreeMap::new(),
tabs: BTreeMap::new(),
- overlay: OverlayWindow::default(),
terminal_emulator_colors: Rc::new(RefCell::new(Palette::default())),
terminal_emulator_color_codes: Rc::new(RefCell::new(HashMap::new())),
tab_history: BTreeMap::new(),
+ pane_history: BTreeMap::new(),
mode_info: BTreeMap::new(),
default_mode_info: mode_info,
draw_pane_frames,
@@ -1644,11 +1645,6 @@ impl Screen {
}
}
- /// Returns a mutable reference to this [`Screen`]'s active [`Overlays`].
- pub fn get_active_overlays_mut(&mut self) -> &mut Vec<Overlay> {
- &mut self.overlay.overlay_stack
- }
-
/// Returns a mutable reference to this [`Screen`]'s indexed [`Tab`].
pub fn get_indexed_tab_mut(&mut self, tab_index: usize) -> Option<&mut Tab> {
self.get_tabs_mut().get_mut(&tab_index)
@@ -1733,6 +1729,7 @@ impl Screen {
tab_index: usize,
should_change_client_focus: bool,
client_id_and_is_web_client: (ClientId, bool),
+ blocking_terminal: Option<(u32, NotificationEnd)>,
) -> Result<()> {
if self.tabs.get(&tab_index).is_none() {
// TODO: we should prevent this situation with a UI - eg. cannot close tabs with a
@@ -1817,6 +1814,7 @@ impl Screen {
new_floating_terminal_ids,
new_plugin_ids,
client_id,
+ blocking_terminal,
)?;
tab.update_input_modes()?;
@@ -1949,18 +1947,6 @@ impl Screen {
}
}
- // Optional: getter for debugging/monitoring
- pub fn get_watcher_size(&self, client_id: &ClientId) -> Option<Size> {
- self.watcher_clients
- .get(client_id)
- .map(|state| state.size())
- }
-
- // Optional: get all watcher sizes
- pub fn get_all_watcher_sizes(&self) -> &HashMap<ClientId, WatcherState> {
- &self.watcher_clients
- }
-
pub fn generate_and_report_tab_state(&mut self) -> Result<Vec<TabInfo>> {
let mut plugin_updates = vec![];
let mut tab_infos_for_screen_state = BTreeMap::new();
@@ -2063,6 +2049,8 @@ impl Screen {
}
fn log_and_report_session_state(&mut self) -> Result<()> {
let err_context = || format!("Failed to log and report session state");
+
+ self.update_active_pane_ids();
// generate own session info
let pane_manifest = self.generate_and_report_pane_state()?;
let tab_infos = self.generate_and_report_tab_state()?;
@@ -2090,6 +2078,11 @@ impl Screen {
.count(),
plugins: Default::default(), // these are filled in by the wasm thread
tab_history: self.tab_history.clone(),
+ pane_history: self
+ .pane_history
+ .iter()
+ .map(|(k, v)| (*k, v.iter().map(|v| (*v).into()).collect()))
+ .collect(),
};
self.bus
.senders
@@ -2496,6 +2489,7 @@ impl Screen {
run_plugin: &RunPluginOrAlias,
should_float: bool,
move_to_focused_tab: bool,
+ should_be_in_place: bool,
client_id: ClientId,
) -> Result<bool> {
// true => found and focused, false => not
@@ -2526,6 +2520,7 @@ impl Screen {
None,
true,
)?;
+ // TODO: also should_be_in_place
} else {
new_active_tab.hide_floating_panes();
new_active_tab.add_tiled_pane(
@@ -2542,7 +2537,7 @@ impl Screen {
self.tabs
.get_mut(&tab_index)
.with_context(err_context)?
- .focus_pane_with_id(plugin_pane_id, should_float, client_id)
+ .focus_pane_with_id(plugin_pane_id, should_float, should_be_in_place, client_id)
.context("failed to focus plugin pane")?;
self.log_and_report_session_state()
.with_context(err_context)?;
@@ -2556,6 +2551,7 @@ impl Screen {
&mut self,
pane_id: PaneId,
should_float_if_hidden: bool,
+ should_open_in_place: bool,
client_id: ClientId,
) -> Result<()> {
let err_context = || format!("failed to focus_plugin_pane");
@@ -2570,7 +2566,14 @@ impl Screen {
self.tabs
.iter_mut()
.find(|(_, t)| t.position == tab_index)
- .map(|(_, t)| t.focus_pane_with_id(pane_id, should_float_if_hidden, client_id))
+ .map(|(_, t)| {
+ t.focus_pane_with_id(
+ pane_id,
+ should_float_if_hidden,
+ should_open_in_place,
+ client_id,
+ )
+ })
.with_context(err_context)
.non_fatal();
},
@@ -2666,6 +2669,8 @@ impl Screen {
Some(tiled_panes_layout),
floating_panes_layout,
tab_index,
+ None, // initial_panes
+ false, // block_on_first_terminal
should_change_focus_to_new_tab,
(client_id, is_web_client),
None,
@@ -2742,6 +2747,8 @@ impl Screen {
Some(tiled_panes_layout),
floating_panes_layout,
tab_index,
+ None, // initial_panes
+ false, // block_on_first_terminal
should_change_focus_to_new_tab,
(client_id, is_web_client),
None,
@@ -2942,6 +2949,7 @@ impl Screen {
&mut self,
pane_id_to_replace: PaneId,
pane_id_of_existing_pane: PaneId,
+ suppress_replaced_pane: bool,
) {
let Some(tab_index_of_pane_id_to_replace) = self
.tabs
@@ -2965,7 +2973,7 @@ impl Screen {
.tabs
.iter_mut()
.find(|(_, t)| t.position == tab_index_of_existing_pane)
- .and_then(|(_, t)| t.extract_pane(pane_id_of_existing_pane, false))
+ .and_then(|(_, t)| t.extract_pane(pane_id_of_existing_pane, true))
else {
log::error!("Failed to find pane");
return;
@@ -2975,11 +2983,19 @@ impl Screen {
.iter_mut()
.find(|(_, t)| t.position == tab_index_of_pane_id_to_replace)
{
- tab.1.close_pane_and_replace_with_other_pane(
- pane_id_to_replace,
- extracted_pane_from_other_tab,
- None,
- );
+ if suppress_replaced_pane {
+ tab.1.suppress_pane_and_replace_with_other_pane(
+ pane_id_to_replace,
+ extracted_pane_from_other_tab,
+ None,
+ );
+ } else {
+ tab.1.close_pane_and_replace_with_other_pane(
+ pane_id_to_replace,
+ extracted_pane_from_other_tab,
+ None,
+ );
+ }
}
let _ = self.log_and_report_session_state();
}
@@ -3505,6 +3521,18 @@ impl Screen {
}
}
}
+ fn update_active_pane_ids(&mut self) {
+ let connected_clients: Vec<ClientId> =
+ self.connected_clients.borrow().keys().copied().collect();
+ for client_id in connected_clients {
+ if let Some(active_pane_id) = self.get_active_pane_id(&client_id) {
+ let active_pane_id: PaneId = active_pane_id.into();
+ let history = self.pane_history.entry(client_id).or_insert_with(|| vec![]);
+ history.retain(|e| e != &active_pane_id);
+ history.push(active_pane_id.into());
+ }
+ }
+ }
}
#[cfg(not(test))]
@@ -3692,9 +3720,11 @@ pub(crate) fn screen_thread_main(
new_pane_placement,
start_suppressed,
client_or_tab_index,
- completion_tx,
+ mut completion_tx,
set_blocking,
) => {
+ completion_tx.as_mut().map(|c| c.set_affected_pane_id(pid));
+
let blocking_notification = if set_blocking { completion_tx } else { None };
match client_or_tab_index {
@@ -3747,6 +3777,7 @@ pub(crate) fn screen_thread_main(
ClientTabIndexOrPaneId::PaneId(pane_id) => {
let mut found = false;
let all_tabs = screen.get_tabs_mut();
+ let should_focus_pane = false;
for tab in all_tabs.values_mut() {
if tab.has_pane_with_pid(&pane_id) {
tab.new_pane(
@@ -3754,7 +3785,7 @@ pub(crate) fn screen_thread_main(
initial_pane_title,
invoked_with,
start_suppressed,
- true,
+ should_focus_pane,
new_pane_placement,
None,
blocking_notification, // TODO: is this correct?
@@ -4426,6 +4457,26 @@ pub(crate) fn screen_thread_main(
screen.render(None)?;
screen.log_and_report_session_state()?;
},
+ ScreenInstruction::ShowPluginCursor(pid, client_id, cursor_position) => {
+ let all_tabs = screen.get_tabs_mut();
+ let mut found_plugin = false;
+ for tab in all_tabs.values_mut() {
+ if tab.has_plugin(pid) {
+ tab.show_plugin_cursor(pid, client_id, cursor_position);
+ found_plugin = true;
+ break;
+ }
+ }
+ if !found_plugin {
+ pending_events_waiting_for_tab.push(ScreenInstruction::ShowPluginCursor(
+ pid,
+ client_id,
+ cursor_position,
+ ));
+ }
+ screen.render(None)?;
+ screen.log_and_report_session_state()?;
+ },
ScreenInstruction::SetMouseSelectionSupport(pid, selection_support) => {
let all_tabs = screen.get_tabs_mut();
let mut found_plugin = false;
@@ -4564,6 +4615,8 @@ pub(crate) fn screen_thread_main(
floating_panes_layout,
tab_name,
swap_layouts,
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
(client_id, is_web_client),
completion_tx,
@@ -4590,6 +4643,8 @@ pub(crate) fn screen_thread_main(
layout,
floating_panes_layout,
tab_index,
+ initial_panes,
+ block_on_first_terminal,
should_change_focus_to_new_tab,
(client_id, is_web_client),
completion_tx,
@@ -4604,9 +4659,14 @@ pub(crate) fn screen_thread_main(
tab_index,
should_change_focus_to_new_tab,
(client_id, is_web_client),
- _completion_tx, // the action ends here, dropping this will release anything
- // waiting for it
+ mut completion_tx,
+ blocking_terminal,
) => {
+ if let Some(first_terminal_pane) = new_pane_pids.iter().next() {
+ completion_tx
+ .as_mut()
+ .map(|c| c.set_affected_pane_id(PaneId::Terminal(first_terminal_pane.0)));
+ }
screen.apply_layout(
layout,
floating_panes_layout,
@@ -4616,6 +4676,7 @@ pub(crate) fn screen_thread_main(
tab_index,
should_change_focus_to_new_tab,
(client_id, is_web_client),
+ blocking_terminal,
)?;
pending_tab_ids.remove(&tab_index);
if pending_tab_ids.is_empty() {
@@ -4749,6 +4810,8 @@ pub(crate) fn screen_thread_main(
None,
vec![],
tab_index,
+ None, // initial_panes
+ false, // block_on_first_terminal
should_change_focus_to_new_tab,
(client_id, is_web_client),
completion_tx,
@@ -4885,7 +4948,7 @@ pub(crate) fn screen_thread_main(
}
});
if let Some(pane_id) = pane_id {
- screen.focus_pane_with_id(pane_id, true, client_id)?;
+ screen.focus_pane_with_id(pane_id, true, false, client_id)?;
} else if let Some(tab_position_to_focus) = tab_position_to_focus {
screen.go_to_tab(tab_position_to_focus, client_id)?;
}
@@ -4915,37 +4978,6 @@ pub(crate) fn screen_thread_main(
screen.log_and_report_session_state()?;
screen.render(None)?;
},
- ScreenInstruction::AddOverlay(overlay, _client_id) => {
- screen.get_active_overlays_mut().pop();
- screen.get_active_overlays_mut().push(overlay);
- },
- ScreenInstruction::RemoveOverlay(_client_id) => {
- screen.get_active_overlays_mut().pop();
- screen.render(None)?;
- },
- ScreenInstruction::ConfirmPrompt(
- _client_id,
- _completion_tx, // the action ends here, dropping this will release anything
- // waiting for it
- ) => {
- let overlay = screen.get_active_overlays_mut().pop();
- let instruction = overlay.and_then(|o| o.prompt_confirm());
- if let Some(instruction) = instruction {
- screen
- .bus
- .senders
- .send_to_server(*instruction)
- .context("failed to confirm prompt")?;
- }
- },
- ScreenInstruction::DenyPrompt(
- _client_id,
- _completion_tx, // the action ends here, dropping this will release anything
- // waiting for it
- ) => {
- screen.get_active_overlays_mut().pop();
- screen.render(None)?;
- },
ScreenInstruction::UpdateSearch(
c,
client_id,
@@ -5441,6 +5473,7 @@ pub(crate) fn screen_thread_main(
&run_plugin,
should_float,
move_to_focused_tab,
+ should_open_in_place,
client_id,
)? {
screen.render(None)?;
@@ -5560,14 +5593,42 @@ pub(crate) fn screen_thread_main(
}
screen.log_and_report_session_state()?;
},
+ ScreenInstruction::UnsuppressPane(pane_id, should_float_if_hidden) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.unsuppress_pane(pane_id, should_float_if_hidden);
+ drop(screen.render(None));
+ break;
+ }
+ }
+ screen.log_and_report_session_state()?;
+ },
+ ScreenInstruction::UnsuppressOrExpandPane(pane_id, should_float_if_hidden) => {
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&pane_id) {
+ tab.unsuppress_or_expand_pane(pane_id, should_float_if_hidden);
+ drop(screen.render(None));
+ break;
+ }
+ }
+ screen.log_and_report_session_state()?;
+ },
ScreenInstruction::FocusPaneWithId(
pane_id,
should_float_if_hidden,
+ should_be_in_place_if_hidden,
client_id,
_completion_tx, // the action ends here, dropping this will release anything
// waiting for it
) => {
- screen.focus_pane_with_id(pane_id, should_float_if_hidden, client_id)?;
+ screen.focus_pane_with_id(
+ pane_id,
+ should_float_if_hidden,
+ should_be_in_place_if_hidden,
+ client_id,
+ )?;
screen.log_and_report_session_state()?;
},
ScreenInstruction::RenamePane(
@@ -5655,9 +5716,11 @@ pub(crate) fn screen_thread_main(
invoked_with,
close_replaced_pane,
client_id_tab_index_or_pane_id,
- _completion_tx, // the action ends here, dropping this will release anything
- // waiting for it
+ mut completion_tx,
) => {
+ completion_tx
+ .as_mut()
+ .map(|c| c.set_affected_pane_id(new_pane_id));
screen.replace_pane(
new_pane_id,
hold_for_command,
@@ -5822,6 +5885,24 @@ pub(crate) fn screen_thread_main(
}
screen.render(None)?;
},
+ ScreenInstruction::CopyTextToClipboard(text, plugin_id) => {
+ let plugin_pane_id = PaneId::Plugin(plugin_id);
+ let all_tabs = screen.get_tabs_mut();
+ for tab in all_tabs.values_mut() {
+ if tab.has_pane_with_pid(&plugin_pane_id) {
+ tab.copy_text_to_clipboard(&text)
+ .with_context(|| {
+ format!(
+ "failed to copy text to clipboard from plugin {}",
+ plugin_id
+ )
+ })
+ .non_fatal();
+ break;
+ }
+ }
+ screen.render(None)?;
+ },
ScreenInstruction::MovePaneWithPaneId(pane_id) => {
let all_tabs = screen.get_tabs_mut();
for tab in all_tabs.values_mut() {
@@ -6053,7 +6134,7 @@ pub(crate) fn screen_thread_main(
// waiting for it
) => {
if let Some(last_pane_id) = screen.stack_panes(pane_ids_to_stack) {
- let _ = screen.focus_pane_with_id(last_pane_id, false, client_id);
+ let _ = screen.focus_pane_with_id(last_pane_id, false, false, client_id);
let _ = screen.render(None);
let pane_group = screen.get_client_pane_group(&client_id);
if !pane_group.is_empty() {
@@ -6213,9 +6294,15 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::ClearKeyPressesIntercepts(client_id) => {
keybind_intercepts.remove(&client_id);
},
- ScreenInstruction::ReplacePaneWithExistingPane(old_pane_id, new_pane_id) => {
- screen.replace_pane_with_existing_pane(old_pane_id, new_pane_id)
- },
+ ScreenInstruction::ReplacePaneWithExistingPane(
+ old_pane_id,
+ new_pane_id,
+ suppress_replaced_pane,
+ ) => screen.replace_pane_with_existing_pane(
+ old_pane_id,
+ new_pane_id,
+ suppress_replaced_pane,
+ ),
ScreenInstruction::AddWatcherClient(client_id, size) => {
screen
.add_watcher_client(client_id)
diff --git a/zellij-server/src/tab/layout_applier.rs b/zellij-server/src/tab/layout_applier.rs
index 5e52888a..559dc5db 100644
--- a/zellij-server/src/tab/layout_applier.rs
+++ b/zellij-server/src/tab/layout_applier.rs
@@ -11,7 +11,7 @@ use crate::{
plugins::PluginInstruction,
pty::PtyInstruction,
thread_bus::ThreadSenders,
- ClientId,
+ ClientId, NotificationEnd,
};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
@@ -42,6 +42,7 @@ pub struct LayoutApplier<'a> {
arrow_fonts: bool,
styled_underlines: bool,
explicitly_disable_kitty_keyboard_protocol: bool,
+ blocking_terminal: Option<(u32, NotificationEnd)>,
}
impl<'a> LayoutApplier<'a> {
@@ -65,6 +66,7 @@ impl<'a> LayoutApplier<'a> {
arrow_fonts: bool,
styled_underlines: bool,
explicitly_disable_kitty_keyboard_protocol: bool,
+ blocking_terminal: Option<(u32, NotificationEnd)>,
) -> Self {
let viewport = viewport.clone();
let senders = senders.clone();
@@ -97,6 +99,7 @@ impl<'a> LayoutApplier<'a> {
arrow_fonts,
styled_underlines,
explicitly_disable_kitty_keyboard_protocol,
+ blocking_terminal,
}
}
pub fn apply_layout(
@@ -496,6 +499,18 @@ impl<'a> LayoutApplier<'a> {
Some(Run::Command(run_command)) => Some(run_command.to_string()),
_ => None,
};
+
+ // Check if this terminal should receive the blocking completion_tx
+ let notification_end = if let Some((blocking_pid, _)) = &self.blocking_terminal {
+ if *blocking_pid == pid {
+ self.blocking_terminal.take().map(|(_, tx)| tx)
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
let mut new_pane = TerminalPane::new(
pid,
*position_and_size,
@@ -513,7 +528,7 @@ impl<'a> LayoutApplier<'a> {
self.arrow_fonts,
self.styled_underlines,
self.explicitly_disable_kitty_keyboard_protocol,
- None,
+ notification_end,
);
if let Some(pane_initial_contents) = &layout.pane_initial_contents {
new_pane.handle_pty_bytes(pane_initial_contents.as_bytes().into());
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index cd87b817..7e441a0e 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -7,7 +7,6 @@ mod layout_applier;
mod swap_layouts;
use copy_command::CopyCommand;
-use serde;
use std::env::temp_dir;
use std::net::IpAddr;
use std::path::PathBuf;
@@ -46,7 +45,6 @@ use crate::{
thread_bus::ThreadSenders,
ClientId, ServerInstruction,
};
-use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Instant;
@@ -279,16 +277,6 @@ pub(crate) struct Tab {
web_server_port: u16,
}
-#[derive(Clone, Debug, Default, Serialize, Deserialize)]
-#[serde(crate = "self::serde")]
-pub(crate) struct TabData {
- pub position: usize,
- pub name: String,
- pub active: bool,
- pub mode_info: ModeInfo,
- pub colors: Styling,
-}
-
// FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication
pub trait Pane {
fn x(&self) -> usize;
@@ -304,7 +292,8 @@ pub trait Pane {
fn set_geom_override(&mut self, pane_geom: PaneGeom);
fn handle_pty_bytes(&mut self, _bytes: VteBytes) {}
fn handle_plugin_bytes(&mut self, _client_id: ClientId, _bytes: VteBytes) {}
- fn cursor_coordinates(&self) -> Option<(usize, usize)>;
+ fn show_cursor(&mut self, _client_id: ClientId, _cursor_position: Option<(usize, usize)>) {}
+ fn cursor_coordinates(&self, _client_id: Option<ClientId>) -> Option<(usize, usize)>;
fn is_mid_frame(&self) -> bool {
false
}
@@ -818,6 +807,7 @@ impl Tab {
new_floating_terminal_ids: Vec<(u32, HoldForCommand)>,
new_plugin_ids: HashMap<RunPluginOrAlias, Vec<u32>>,
client_id: ClientId,
+ blocking_terminal: Option<(u32, NotificationEnd)>,
) -> Result<()> {
self.swap_layouts
.set_base_layout((layout.clone(), floating_panes_layout.clone()));
@@ -841,6 +831,7 @@ impl Tab {
self.arrow_fonts,
self.styled_underlines,
self.explicitly_disable_kitty_keyboard_protocol,
+ blocking_terminal,
)
.apply_layout(
layout,
@@ -911,6 +902,7 @@ impl Tab {
self.arrow_fonts,
self.styled_underlines,
self.explicitly_disable_kitty_keyboard_protocol,
+ None,
)
.apply_floating_panes_layout_to_existing_panes(&layout_candidate)
.non_fatal();
@@ -949,6 +941,7 @@ impl Tab {
self.arrow_fonts,
self.styled_underlines,
self.explicitly_disable_kitty_keyboard_protocol,
+ None,
)
.apply_tiled_panes_layout_to_existing_panes(&layout_candidate);
if application_res.is_err() {
@@ -2080,6 +2073,26 @@ impl Tab {
drop(replaced_pane);
}
}
+ pub fn suppress_pane_and_replace_with_other_pane(
+ &mut self,
+ pane_id_to_replace: PaneId,
+ pane_to_replace_with: Box<dyn Pane>,
+ _completion_tx: Option<NotificationEnd>,
+ ) {
+ let mut replaced_pane = if self.floating_panes.panes_contain(&pane_id_to_replace) {
+ self.floating_panes
+ .replace_pane(pane_id_to_replace, pane_to_replace_with)
+ .ok()
+ } else {
+ self.tiled_panes
+ .replace_pane(pane_id_to_replace, pane_to_replace_with)
+ };
+
+ if let Some(replaced_pane) = replaced_pane.take() {
+ let is_scrollback_editor = false;
+ self.insert_suppressed_pane(replaced_pane.pid(), (is_scrollback_editor, replaced_pane));
+ }
+ }
pub fn horizontal_split(
&mut self,
pid: PaneId,
@@ -2683,7 +2696,7 @@ impl Tab {
.get(&active_pane_id)
.or_else(|| self.tiled_panes.get_pane(active_pane_id))?;
active_terminal
- .cursor_coordinates()
+ .cursor_coordinates(Some(client_id))
.map(|(x_in_terminal, y_in_terminal)| {
let x = active_terminal.x() + x_in_terminal;
let y = active_terminal.y() + y_in_terminal;
@@ -3381,21 +3394,16 @@ impl Tab {
if let Some(pane) = self.tiled_panes.get_pane_mut(id) {
pane.set_selectable(selectable);
if !selectable {
- // there are some edge cases in which this causes a hard crash when there are no
- // other selectable panes - ideally this should never happen unless it's a
- // configuration error - but this *does* sometimes happen with the default
- // configuration as well since we set this at run time. I left this here because
- // this should very rarely happen and I hope in my heart that we will stop setting
- // this at runtime in the default configuration at some point
- //
- // If however this is not the case and we find this does cause crashes, we can
- // solve it by adding a "dangling_clients" struct to Tab which we would fill with
- // the relevant client ids in this case and drain as soon as a new selectable pane
- // is opened
self.tiled_panes.move_clients_out_of_pane(id);
}
} else if let Some(pane) = self.floating_panes.get_pane_mut(id) {
pane.set_selectable(selectable);
+ if !selectable {
+ self.floating_panes.move_clients_out_of_pane(id);
+ if !self.floating_panes.has_selectable_panes() {
+ self.hide_floating_panes();
+ }
+ }
}
// we do this here because if there is a non-selectable pane on the edge, we consider it
// outside the viewport (a ui-pane, eg. the status-bar and tab-bar) and need to adjust for it
@@ -3539,8 +3547,15 @@ impl Tab {
closed_pane.reset_logical_position();
}
closed_pane
- } else if self.suppressed_panes.contains_key(&id) {
- self.suppressed_panes.remove(&id).map(|s_p| s_p.1)
+ } else if let Some(suppressed_key_of_pane) = self
+ .suppressed_panes
+ .iter()
+ .find_map(|(key, (_, pane))| if &pane.pid() == &id { Some(*key) } else { None })
+ {
+ // TODO: test this (from the path in screen.rs focus_plugin_pane ~line 2519
+ self.suppressed_panes
+ .remove(&suppressed_key_of_pane)
+ .map(|s_p| s_p.1)
} else {
None
}
@@ -4674,6 +4689,19 @@ impl Tab {
}
Ok(())
}
+ pub fn copy_text_to_clipboard(&self, text: &str) -> Result<()> {
+ self.write_selection_to_clipboard(text)
+ .with_context(|| format!("failed to write text to clipboard"))?;
+ self.senders
+ .send_to_plugin(PluginInstruction::Update(vec![(
+ None,
+ None,
+ Event::CopyToClipboard(self.clipboard_provider.as_copy_destination()),
+ )]))
+ .with_context(|| "failed to inform plugins about clipboard copy")
+ .non_fatal();
+ Ok(())
+ }
fn write_selection_to_clipboard(&self, selection: &str) -> Result<()> {
let err_context = || format!("failed to write selection to clipboard: '{}'", selection);
@@ -4950,6 +4978,26 @@ impl Tab {
plugin_pane.update_loading_indication(loading_indication);
}
}
+ pub fn show_plugin_cursor(
+ &mut self,
+ pid: u32,
+ client_id: ClientId,
+ cursor_position: Option<(usize, usize)>,
+ ) {
+ if let Some(plugin_pane) = self
+ .tiled_panes
+ .get_pane_mut(PaneId::Plugin(pid))
+ .or_else(|| self.floating_panes.get_pane_mut(PaneId::Plugin(pid)))
+ .or_else(|| {
+ self.suppressed_panes
+ .values_mut()
+ .find(|s_p| s_p.1.pid() == PaneId::Plugin(pid))
+ .map(|s_p| &mut s_p.1)
+ })
+ {
+ plugin_pane.show_cursor(client_id, cursor_position);
+ }
+ }
pub fn start_plugin_loading_indication(
&mut self,
pid: u32,
@@ -5009,7 +5057,7 @@ impl Tab {
.find(|(_id, (_, pane))| {
run_plugin_or_alias.is_equivalent_to_run(pane.invoked_with())
})
- .map(|(id, _)| *id)
+ .map(|(_, (_, pane))| pane.pid()) // TODO: does this break things????
})
}
@@ -5017,6 +5065,7 @@ impl Tab {
&mut self,
pane_id: PaneId,
should_float: bool,
+ should_be_in_place: bool,
client_id: ClientId,
) -> Result<()> {
// TODO: should error if pane is not selectable
@@ -5031,18 +5080,45 @@ impl Tab {
};
focused_floating_pane
})
- .or_else(|_| match self.suppressed_panes.remove(&pane_id) {
- Some(mut pane) => {
- pane.1.set_selectable(true);
- if should_float {
- self.show_floating_panes();
- self.add_floating_pane(pane.1, pane_id, None, true)
- } else {
- self.hide_floating_panes();
- self.add_tiled_pane(pane.1, pane_id, Some(client_id))
- }
- },
- None => Ok(()),
+ // TODO: change suppressed_panes to be a proper struct with methods that make sense rather
+ // than doing this dance every time
+ .or_else(|_| {
+ match self
+ .suppressed_panes
+ .extract_if(|_key, (_, pane)| pane.pid() == pane_id)
+ .next()
+ .map(|(_key, (_, pane))| pane)
+ {
+ Some(mut pane) => {
+ pane.set_selectable(true);
+ if should_float {
+ self.show_floating_panes();
+ self.add_floating_pane(pane, pane_id, None, true)
+ } else if should_be_in_place {
+ let replaced_pane = if self.are_floating_panes_visible() {
+ self.floating_panes
+ .replace_active_pane(pane, client_id)
+ .ok()
+ } else {
+ self.tiled_panes.replace_active_pane(pane, client_id)
+ };
+ if let Some(replaced_pane) = replaced_pane {
+ let is_scrollback_editor = false;
+ self.insert_suppressed_pane(
+ pane_id,
+ (is_scrollback_editor, replaced_pane),
+ );
+ } else {
+ log::error!("Could not find pane to replace, aborting.");
+ }
+ Ok(())
+ } else {
+ self.hide_floating_panes();
+ self.add_tiled_pane(pane, pane_id, Some(client_id))
+ }
+ },
+ None => Ok(()),
+ }
})
}
pub fn focus_suppressed_pane_for_all_clients(&mut self, pane_id: PaneId) {
@@ -5064,11 +5140,101 @@ impl Tab {
// scrollback editor), but it has to take itself out on its own (eg. a plugin using the
// show_self() method)
if let Some(pane) = self.extract_pane(pane_id, true) {
- let is_scrollback_editor = false;
- self.suppressed_panes
- .insert(pane_id, (is_scrollback_editor, pane));
+ self.insert_suppressed_pane(pane_id, (false, pane));
}
}
+ pub fn unsuppress_pane(&mut self, pane_id: PaneId, should_float_if_hidden: bool) {
+ // removes a pane from being suppressed (hidden) but does not focus it
+ match self
+ .suppressed_panes
+ .extract_if(|_key, (_, pane)| pane.pid() == pane_id)
+ .next()
+ .map(|(_key, (_, pane))| pane)
+ {
+ Some(pane) => {
+ if should_float_if_hidden {
+ self.add_floating_pane(pane, pane_id, None, true)
+ .non_fatal();
+ } else {
+ self.add_tiled_pane(pane, pane_id, None).non_fatal();
+ }
+ },
+ None => {
+ log::error!("Could not find suppressed pane with id: {:?}", pane_id);
+ },
+ }
+ }
+ pub fn unsuppress_or_expand_pane(&mut self, pane_id: PaneId, should_float_if_hidden: bool) {
+ // removes a pane from being suppressed (hidden) but does not focus it
+ match self
+ .suppressed_panes
+ .extract_if(|_key, (_, pane)| pane.pid() == pane_id)
+ .next()
+ .map(|(_key, (_, pane))| pane)
+ {
+ Some(pane) => {
+ if should_float_if_hidden {
+ self.add_floating_pane(pane, pane_id, None, true)
+ .non_fatal();
+ } else {
+ self.add_tiled_pane(pane, pane_id, None).non_fatal();
+ }
+ },
+ None => {
+ let expand_panes_success = self.tiled_panes.expand_pane_in_stack(pane_id).len() > 0;
+ if !expand_panes_success {
+ log::error!(
+ "Could not find suppressed or stacked pane with id: {:?}",
+ pane_id
+ );
+ }
+ },
+ }
+ }
+ fn insert_suppressed_pane(
+ &mut self,
+ suppressing_pane_id: PaneId,
+ pane_to_suppress: (bool, Box<dyn Pane>),
+ ) {
+ // bool -> is_scrollback_editor
+ // this method is intended to insert an existing provided pane into suppressed_panes, while
+ // making sure any existing panes already in suppressed_panes still remain there
+ // if there's a key collision (eg. the suppressing_pane_id already suppresses another
+ // pane), we fix the colliding pane so that it now becomes suppressed by its own id (i.e.
+ // needs to explicitly be removed from suppressed_panes by eg. a plugin action rather than
+ // being conditionally removed when its suppressing pane is closed or itself suppressed)
+ //
+ // - suppressing_pane_id: the id of the pane suppressing this pane - the pane that if
+ // closed, should trigger this pane being unsuppressed
+ // - pane_to_suppress: the pane itself that we want to suppress
+
+ // this closure removes and returns an existing pane from the map if it exists under this key
+ // and inserts the given pane into the map under this key
+ let mut insert_and_return_existing_value =
+ |key: PaneId, value: (bool, Box<dyn Pane>)| -> Option<(bool, Box<dyn Pane>)> {
+ let existing = self.suppressed_panes.remove(&key);
+ self.suppressed_panes.insert(key, value);
+ existing
+ };
+
+ // here we try to insert a pane into the suppressed panes map while making sure not to drop
+ // (close) any existing panes in the map. We repeat the action of remapping existing panes
+ // under their own keys until all keys either reference the suppressing_pane_id (only one
+ // can do this) or themselves
+ let mut key_to_insert = suppressing_pane_id;
+ let mut pane_to_insert = Some(pane_to_suppress);
+ loop {
+ pane_to_insert = pane_to_insert
+ .take()
+ .and_then(|p| insert_and_return_existing_value(key_to_insert, p));
+ if let Some(pane_to_insert) = pane_to_insert.as_ref() {
+ key_to_insert = pane_to_insert.1.pid();
+ } else {
+ break;
+ }
+ }
+ }
+
pub fn pane_infos(&self) -> Vec<PaneInfo> {
let mut pane_info = vec![];
let current_pane_group = { self.current_pane_group.borrow().clone_inner() };
@@ -5076,9 +5242,11 @@ impl Tab {
let mut floating_pane_info = self.floating_panes.pane_info(&current_pane_group);
pane_info.append(&mut tiled_pane_info);
pane_info.append(&mut floating_pane_info);
- for (pane_id, (_is_scrollback_editor, pane)) in self.suppressed_panes.iter() {
+ for (_pane_id_of_suppressing_pane, (_is_scrollback_editor, pane)) in
+ self.suppressed_panes.iter()
+ {
let mut pane_info_for_suppressed_pane =
- pane_info_for_pane(pane_id, pane, &current_pane_group);
+ pane_info_for_pane(&pane.pid(), pane, &current_pane_group);
pane_info_for_suppressed_pane.is_floating = false;
pane_info_for_suppressed_pane.is_suppressed = true;
pane_info_for_suppressed_pane.is_focused = false;
@@ -5364,7 +5532,7 @@ impl Tab {
}
pub fn add_suppressed_panes(&mut self, mut suppressed_panes: SuppressedPanes) {
for (pane_id, suppressed_pane_entry) in suppressed_panes.drain() {
- self.suppressed_panes.insert(pane_id, suppressed_pane_entry);
+ self.insert_suppressed_pane(pane_id, suppressed_pane_entry);
}
}
pub fn toggle_pane_pinned(&mut self, client_id: ClientId) {
@@ -5531,7 +5699,7 @@ pub fn pane_info_for_pane(
pane_info.pane_content_rows = pane.get_content_rows();
pane_info.pane_columns = pane.cols();
pane_info.pane_content_columns = pane.get_content_columns();
- pane_info.cursor_coordinates_in_pane = pane.cursor_coordinates();
+ pane_info.cursor_coordinates_in_pane = pane.cursor_coordinates(None);
pane_info.is_selectable = pane.selectable();
pane_info.title = pane.current_title();
pane_info.exited = pane.exited();
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs
index 029df49d..6eb39eae 100644
--- a/zellij-server/src/tab/unit/tab_integration_tests.rs
+++ b/zellij-server/src/tab/unit/tab_integration_tests.rs
@@ -138,6 +138,9 @@ impl ServerOsApi for FakeInputOutput {
fn clear_terminal_id(&self, _terminal_id: u32) -> Result<()> {
unimplemented!()
}
+ fn send_sigint(&self, pid: Pid) -> Result<()> {
+ unimplemented!()
+ }
}
struct MockPtyInstructionBus {
@@ -280,6 +283,7 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -361,6 +365,7 @@ fn create_new_tab_without_pane_frames(size: Size, default_mode: ModeInfo) -> Tab
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -469,6 +474,7 @@ fn create_new_tab_with_swap_layouts(
new_floating_terminal_ids,
new_plugin_ids,
client_id,
+ None,
)
.unwrap();
tab
@@ -554,6 +560,7 @@ fn create_new_tab_with_os_api(
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -648,6 +655,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
floating_pane_ids,
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -734,6 +742,7 @@ fn create_new_tab_with_mock_pty_writer(
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -822,6 +831,7 @@ fn create_new_tab_with_sixel_support(
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -1191,7 +1201,7 @@ fn render_stacks_without_pane_frames() {
)
.unwrap();
}
- tab.focus_pane_with_id(PaneId::Terminal(1), false, client_id);
+ tab.focus_pane_with_id(PaneId::Terminal(1), false, false, client_id);
for i in 7..9 {
let new_pane_id_1 = PaneId::Terminal(i);
tab.new_pane(
@@ -1206,7 +1216,7 @@ fn render_stacks_without_pane_frames() {
)
.unwrap();
}
- let _ = tab.focus_pane_with_id(PaneId::Terminal(1), false, client_id);
+ let _ = tab.focus_pane_with_id(PaneId::Terminal(1), false, false, client_id);
for i in 9..11 {
let new_pane_id_1 = PaneId::Terminal(i);
tab.new_pane(
@@ -1226,8 +1236,8 @@ fn render_stacks_without_pane_frames() {
ResizeStrategy::new(Resize::Increase, Some(Direction::Right)),
)
.unwrap();
- let _ = tab.focus_pane_with_id(PaneId::Terminal(7), false, client_id);
- let _ = tab.focus_pane_with_id(PaneId::Terminal(5), false, client_id);
+ let _ = tab.focus_pane_with_id(PaneId::Terminal(7), false, false, client_id);
+ let _ = tab.focus_pane_with_id(PaneId::Terminal(5), false, false, client_id);
tab.render(&mut output, None).unwrap();
let snapshot = take_snapshot(
diff --git a/zellij-server/src/tab/unit/tab_tests.rs b/zellij-server/src/tab/unit/tab_tests.rs
index f5284b88..085f8211 100644
--- a/zellij-server/src/tab/unit/tab_tests.rs
+++ b/zellij-server/src/tab/unit/tab_tests.rs
@@ -106,6 +106,9 @@ impl ServerOsApi for FakeInputOutput {
fn clear_terminal_id(&self, _terminal_id: u32) -> Result<()> {
unimplemented!()
}
+ fn send_sigint(&self, pid: Pid) -> Result<()> {
+ unimplemented!()
+ }
}
fn tab_resize_increase(tab: &mut Tab, id: ClientId) {
@@ -220,6 +223,7 @@ fn create_new_tab(size: Size, stacked_resize: bool) -> Tab {
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -304,6 +308,7 @@ fn create_new_tab_with_layout(size: Size, layout: TiledPaneLayout) -> Tab {
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
@@ -386,6 +391,7 @@ fn create_new_tab_with_cell_size(
vec![],
HashMap::new(),
client_id,
+ None,
)
.unwrap();
tab
diff --git a/zellij-server/src/ui/components/nested_list.rs b/zellij-server/src/ui/components/nested_list.rs
index c2aa2860..b4b76b9b 100644
--- a/zellij-server/src/ui/components/nested_list.rs
+++ b/zellij-server/src/ui/components/nested_list.rs
@@ -49,6 +49,7 @@ pub fn nested_list(
Some(padding + bulletin.len()),
&coordinates,
&style_declaration,
+ &style.colors,
text_style.bold(Some(AnsiCode::On)),
);
text = pad_line(text, max_width, padding, text_width);
diff --git a/zellij-server/src/ui/components/ribbon.rs b/zellij-server/src/ui/components/ribbon.rs
index 98b681a5..adfa3a79 100644
--- a/zellij-server/src/ui/components/ribbon.rs
+++ b/zellij-server/src/ui/components/ribbon.rs
@@ -34,6 +34,7 @@ pub fn ribbon(
padding,
&component_coordinates,
&declaration,
+ &colors,
text_style,
);
let mut stringified = component_coordinates
diff --git a/zellij-server/src/ui/components/table.rs b/zellij-server/src/ui/components/table.rs
index 978b741e..80ab2a37 100644
--- a/zellij-server/src/ui/components/table.rs
+++ b/zellij-server/src/ui/components/table.rs
@@ -22,7 +22,8 @@ pub fn table(
if is_too_high(row_index + 1, &coordinates) {
break;
}
- for cell in row {
+ let cell_count = row.iter().len();
+ for (cell_index, cell) in row.into_iter().enumerate() {
let declaration = if is_title_row {
style.colors.table_title
} else {
@@ -38,16 +39,21 @@ pub fn table(
} else {
CharacterStyles::from(declaration)
};
+
+ // Default: bold
+ let text_style = text_style.bold(Some(AnsiCode::On));
+
// here we intentionally don't pass our coordinates even if we have them, because
// these cells have already been padded and truncated
- let (text, _text_width) = stringify_text(
- &cell,
- None,
- &None,
- &declaration,
- text_style.bold(Some(AnsiCode::On)),
- );
- stringified.push_str(&format!("{}{} {}", text_style, text, RESET_STYLES));
+ let (text, _text_width) =
+ stringify_text(&cell, None, &None, &declaration, &style.colors, text_style);
+ if cell_index == cell_count.saturating_sub(1) {
+ // do not add padding between columns for the last cell
+ stringified.push_str(&format!("{}{}{}", text_style, text, RESET_STYLES));
+ } else {
+ // add padding between columns
+ stringified.push_str(&format!("{}{} {}", text_style, text, RESET_STYLES));
+ }
}
let next_row_instruction = coordinates
.as_ref()
diff --git a/zellij-server/src/ui/components/text.rs b/zellij-server/src/ui/components/text.rs
index c0088e60..eb8667f8 100644
--- a/zellij-server/src/ui/components/text.rs
+++ b/zellij-server/src/ui/components/text.rs
@@ -15,6 +15,7 @@ pub fn text(content: Text, style: &Style, component_coordinates: Option<Coordina
style.colors.text_unselected
};
+ // Start with base style from declaration
let base_text_style = CharacterStyles::from(declaration).bold(Some(AnsiCode::On));
let (text, _text_width) = stringify_text(
@@ -22,6 +23,7 @@ pub fn text(content: Text, style: &Style, component_coordinates: Option<Coordina
None,
&component_coordinates,
&declaration,
+ &style.colors,
base_text_style,
);
match component_coordinates {
@@ -39,6 +41,7 @@ pub fn stringify_text(
left_padding: Option<usize>,
coordinates: &Option<Coordinates>,
style: &StyleDeclaration,
+ styling: &zellij_utils::data::Styling,
component_text_style: CharacterStyles,
) -> (String, usize) {
let mut text_width = 0;
@@ -48,6 +51,7 @@ pub fn stringify_text(
} else {
component_text_style
};
+ stringified.push_str(&format!("{}", base_text_style));
for (i, character) in text.text.chars().enumerate() {
let character_width = character.width().unwrap_or(0);
if is_too_wide(
@@ -67,7 +71,7 @@ pub fn stringify_text(
if !text.indices.is_empty() || text.selected || text.opaque {
let character_with_styling =
- color_index_character(character, i, &text, style, base_text_style);
+ color_index_character(character, i, &text, style, styling, base_text_style);
stringified.push_str(&character_with_styling);
} else {
stringified.push(character)
@@ -100,12 +104,32 @@ pub fn color_index_character(
index: usize,
text: &Text,
declaration: &StyleDeclaration,
+ styling: &zellij_utils::data::Styling,
base_text_style: CharacterStyles,
) -> String {
- let character_style = text
- .style_of_index(index, declaration)
+ let mut character_style = text
+ .style_of_index(index, declaration, styling)
.map(|foreground_style| base_text_style.foreground(Some(foreground_style.into())))
.unwrap_or(base_text_style);
+
+ // Apply dim and unbold per-character based on index levels 4 and 5
+ if text.is_unbold_at(index) {
+ // Remove bold for this character
+ character_style = character_style.bold(Some(AnsiCode::Reset));
+ } else if text.is_dimmed_at(index) {
+ // Apply dim for this character
+ character_style = character_style
+ .foreground(Some(AnsiCode::Reset)) // some terminals (eg. alacritty) do not support dimming non 16
+ // colors, so we have to defer to the terminal's default here
+ .dim(Some(AnsiCode::On));
+ } else {
+ character_style = character_style
+ .bold(Some(AnsiCode::On))
+ .dim(Some(AnsiCode::Reset)); // default, to reset any
+ // possible dim/bold values
+ // from previous indices
+ }
+
format!("{}{}{}", character_style, character, base_text_style)
}
@@ -141,7 +165,46 @@ impl Text {
}
}
- pub fn style_of_index(&self, index: usize, style: &StyleDeclaration) -> Option<PaletteColor> {
+ pub fn is_dimmed_at(&self, index: usize) -> bool {
+ const DIM_LEVEL: usize = 4;
+ self.indices
+ .get(DIM_LEVEL)
+ .map(|indices| indices.contains(&index))
+ .unwrap_or(false)
+ }
+
+ pub fn is_unbold_at(&self, index: usize) -> bool {
+ const UNBOLD_LEVEL: usize = 5;
+ self.indices
+ .get(UNBOLD_LEVEL)
+ .map(|indices| indices.contains(&index))
+ .unwrap_or(false)
+ }
+
+ pub fn style_of_index(
+ &self,
+ index: usize,
+ style: &StyleDeclaration,
+ styling: &zellij_utils::data::Styling,
+ ) -> Option<PaletteColor> {
+ const ERROR_COLOR_LEVEL: usize = 6;
+ const SUCCESS_COLOR_LEVEL: usize = 7;
+
+ // Check error color first (highest precedence)
+ if let Some(indices) = self.indices.get(ERROR_COLOR_LEVEL) {
+ if indices.contains(&index) {
+ return Some(styling.exit_code_error.base);
+ }
+ }
+
+ // Check success color (second highest precedence)
+ if let Some(indices) = self.indices.get(SUCCESS_COLOR_LEVEL) {
+ if indices.contains(&index) {
+ return Some(styling.exit_code_success.base);
+ }
+ }
+
+ // Check regular emphasis levels (existing code)
let index_variant_styles = [
style.emphasis_0,
style.emphasis_1,
diff --git a/zellij-server/src/ui/mod.rs b/zellij-server/src/ui/mod.rs
index d9d5e3a0..1a17e743 100644
--- a/zellij-server/src/ui/mod.rs
+++ b/zellij-server/src/ui/mod.rs
@@ -1,6 +1,5 @@
pub mod boundaries;
pub mod components;
pub mod loading_indication;
-pub mod overlay;
pub mod pane_boundaries_frame;
pub mod pane_contents_and_ui;
diff --git a/zellij-server/src/ui/overlay/mod.rs b/zellij-server/src/ui/overlay/mod.rs
deleted file mode 100644
index 7e3f821b..00000000
--- a/zellij-server/src/ui/overlay/mod.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! This module handles the overlay's over the [`Screen`]
-//!
-//! They consist of:
-//!
-//! prompt's:
-//!
-//! notification's:
-
-pub mod prompt;
-
-use crate::ServerInstruction;
-use zellij_utils::errors::prelude::*;
-use zellij_utils::pane_size::Size;
-
-#[derive(Clone, Debug)]
-pub struct Overlay {
- pub overlay_type: OverlayType,
-}
-
-pub trait Overlayable {
- /// Generates vte_output that can be passed into
- /// the `render()` function
- fn generate_overlay(&self, size: Size) -> Result<String>;
-}
-
-#[derive(Clone, Debug)]
-pub enum OverlayType {
- Prompt(prompt::Prompt),
-}
-
-impl Overlayable for OverlayType {
- fn generate_overlay(&self, size: Size) -> Result<String> {
- match &self {
- OverlayType::Prompt(prompt) => prompt
- .generate_overlay(size)
- .context("failed to generate VTE output from overlay type"),
- }
- }
-}
-
-/// Entrypoint from [`Screen`], which holds the context in which
-/// the overlays are being rendered.
-/// The most recent overlays draw over the previous overlays.
-#[derive(Clone, Debug, Default)]
-pub struct OverlayWindow {
- pub overlay_stack: Vec<Overlay>,
-}
-
-impl Overlayable for OverlayWindow {
- fn generate_overlay(&self, size: Size) -> Result<String> {
- let mut output = String::new();
- //let clear_display = "\u{1b}[2J";
- //output.push_str(&clear_display);
- for overlay in &self.overlay_stack {
- let vte_output = overlay
- .generate_overlay(size)
- .context("failed to generate VTE output from overlay window")?;
- output.push_str(&vte_output);
- }
- Ok(output)
- }
-}
-
-impl Overlay {
- pub fn prompt_confirm(self) -> Option<Box<ServerInstruction>> {
- match self.overlay_type {
- OverlayType::Prompt(p) => p.confirm(),
- }
- }
- pub fn prompt_deny(self) -> Option<Box<ServerInstruction>> {
- match self.overlay_type {
- OverlayType::Prompt(p) => p.deny(),
- }
- }
-}
-
-impl Overlayable for Overlay {
- fn generate_overlay(&self, size: Size) -> Result<String> {
- self.overlay_type
- .generate_overlay(size)
- .context("failed to generate VTE output from overlay")
- }
-}
-
-impl Overlay {
- pub fn new(overlay_type: OverlayType) -> Self {
- Self { overlay_type }
- }
-
- fn pad_cols(output: &mut String, cols: usize) {
- if let Some(padding) = cols.checked_sub(output.len()) {
- for _ in 0..padding {
- output.push(' ');
- }
- }
- }
-}
diff --git a/zellij-server/src/ui/overlay/prompt.rs b/zellij-server/src/ui/overlay/prompt.rs
deleted file mode 100644
index 0cbfe351..00000000
--- a/zellij-server/src/ui/overlay/prompt.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use zellij_utils::pane_size::Size;
-
-use super::{Overlay, OverlayType, Overlayable};
-use crate::{ClientId, ServerInstruction};
-use zellij_utils::errors::prelude::*;
-
-use std::fmt::Write;
-
-#[derive(Clone, Debug)]
-pub struct Prompt {
- pub message: String,
- on_confirm: Option<Box<ServerInstruction>>,
- on_deny: Option<Box<ServerInstruction>>,
-}
-
-impl Prompt {
- pub fn new(
- message: String,
- on_confirm: Option<Box<ServerInstruction>>,
- on_deny: Option<Box<ServerInstruction>>,
- ) -> Self {
- Self {
- message,
- on_confirm,
- on_deny,
- }
- }
- pub fn confirm(self) -> Option<Box<ServerInstruction>> {
- self.on_confirm
- }
- pub fn deny(self) -> Option<Box<ServerInstruction>> {
- self.on_deny
- }
-}
-
-impl Overlayable for Prompt {
- fn generate_overlay(&self, size: Size) -> Result<String> {
- let mut output = String::new();
- let rows = size.rows;
- let mut vte_output = self.message.clone();
- Overlay::pad_cols(&mut vte_output, size.cols);
- for (x, h) in vte_output.chars().enumerate() {
- write!(
- &mut output,
- "\u{1b}[{};{}H\u{1b}[48;5;238m{}",
- rows,
- x + 1,
- h,
- )
- .context("failed to generate VTE output from prompt")?;
- }
- Ok(output)
- }
-}
-
-pub fn _generate_quit_prompt(client_id: ClientId) -> Overlay {
- let prompt = Prompt::new(
- (" Do you want to quit zellij? [Y]es / [N]o").to_string(),
- Some(Box::new(ServerInstruction::ClientExit(client_id, None))),
- None,
- );
- Overlay {
- overlay_type: OverlayType::Prompt(prompt),
- }
-}
diff --git a/zellij-server/src/ui/pane_contents_and_ui.rs b/zellij-server/src/ui/pane_contents_and_ui.rs
index c17a9b66..326dd96f 100644
--- a/zellij-server/src/ui/pane_contents_and_ui.rs
+++ b/zellij-server/src/ui/pane_contents_and_ui.rs
@@ -159,7 +159,7 @@ impl<'a> PaneContentsAndUi<'a> {
) {
let cursor_is_visible = self
.pane
- .cursor_coordinates()
+ .cursor_coordinates(Some(*fake_cursor_client_id))
.map(|(x, y)| {
self.output
.cursor_is_visible(self.pane.x() + x, self.pane.y() + y)
@@ -279,6 +279,7 @@ impl<'a> PaneContentsAndUi<'a> {
.add_post_vte_instruction_to_client(client_id, &vte_output);
}
}
+
Ok(())
}
pub fn render_pane_boundaries(
diff --git a/zellij-server/src/unit/screen_tests.rs b/zellij-server/src/unit/screen_tests.rs
index 171096f7..6345d6b8 100644
--- a/zellij-server/src/unit/screen_tests.rs
+++ b/zellij-server/src/unit/screen_tests.rs
@@ -249,6 +249,9 @@ impl ServerOsApi for FakeInputOutput {
fn clear_terminal_id(&self, _terminal_id: u32) -> Result<()> {
unimplemented!()
}
+ fn send_sigint(&self, pid: Pid) -> Result<()> {
+ unimplemented!()
+ }
}
fn create_new_screen(size: Size, advanced_mouse_actions: bool) -> Screen {
@@ -402,6 +405,8 @@ impl MockScreen {
initial_floating_panes_layout.clone(),
tab_name,
(vec![], vec![]), // swap layouts
+ None, // initial_panes
+ false,
should_change_focus_to_new_tab,
(self.main_client_id, false),
None,
@@ -416,6 +421,7 @@ impl MockScreen {
true,
(self.main_client_id, false),
None,
+ None,
));
self.last_opened_tab_index = Some(tab_index);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for the async render
@@ -492,6 +498,8 @@ impl MockScreen {
initial_floating_panes_layout.clone(),
tab_name,
(vec![], vec![]), // swap layouts
+ None, // initial_panes
+ false,
should_change_focus_to_new_tab,
(self.main_client_id, false),
None,
@@ -506,6 +514,7 @@ impl MockScreen {
true,
(self.main_client_id, false),
None,
+ None,
));
self.last_opened_tab_index = Some(tab_index);
screen_thread
@@ -528,6 +537,8 @@ impl MockScreen {
vec![], // floating_panes_layout
tab_name,
(vec![], vec![]), // swap layouts
+ None, // initial_panes
+ false,
should_change_focus_to_new_tab,
(self.main_client_id, false),
None,
@@ -542,6 +553,7 @@ impl MockScreen {
true,
(self.main_client_id, false),
None,
+ None,
));
self.last_opened_tab_index = Some(tab_index);
}
@@ -756,6 +768,7 @@ fn new_tab(screen: &mut Screen, pid: u32, tab_index: usize) {
tab_index,
true,
(client_id, false),
+ None,
)
.expect("TEST");
}
@@ -2638,6 +2651,8 @@ pub fn send_cli_new_pane_action_with_default_parameters() {
pinned: None,
stacked: false,
blocking: false,
+ unblock_condition: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2684,6 +2699,8 @@ pub fn send_cli_new_pane_action_with_split_direction() {
pinned: None,
stacked: false,
blocking: false,
+ unblock_condition: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2730,6 +2747,8 @@ pub fn send_cli_new_pane_action_with_command_and_cwd() {
pinned: None,
stacked: false,
blocking: false,
+ unblock_condition: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2787,6 +2806,8 @@ pub fn send_cli_new_pane_action_with_floating_pane_and_coordinates() {
pinned: None,
stacked: false,
blocking: false,
+ unblock_condition: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_new_pane_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2826,6 +2847,7 @@ pub fn send_cli_edit_action_with_default_parameters() {
width: None,
height: None,
pinned: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2865,6 +2887,7 @@ pub fn send_cli_edit_action_with_line_number() {
width: None,
height: None,
pinned: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -2904,6 +2927,7 @@ pub fn send_cli_edit_action_with_split_direction() {
width: None,
height: None,
pinned: None,
+ near_current_pane: false,
};
send_cli_action_to_server(&session_metadata, cli_edit_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100)); // give time for actions to be
@@ -3089,6 +3113,13 @@ pub fn send_cli_new_tab_action_default_params() {
layout: None,
layout_dir: None,
cwd: None,
+ initial_command: vec![],
+ initial_plugin: None,
+ close_on_exit: Default::default(),
+ start_suspended: Default::default(),
+ block_until_exit: false,
+ block_until_exit_success: false,
+ block_until_exit_failure: false,
};
send_cli_action_to_server(&session_metadata, new_tab_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -3129,6 +3160,13 @@ pub fn send_cli_new_tab_action_with_name_and_layout() {
))),
layout_dir: None,
cwd: None,
+ initial_command: vec![],
+ initial_plugin: None,
+ close_on_exit: Default::default(),
+ start_suspended: Default::default(),
+ block_until_exit: false,
+ block_until_exit_success: false,
+ block_until_exit_failure: false,
};
send_cli_action_to_server(&session_metadata, new_tab_action, client_id);
std::thread::sleep(std::time::Duration::from_millis(100));
@@ -3699,6 +3737,7 @@ pub fn screen_can_break_pane_to_a_new_tab() {
true,
(1, false),
None,
+ None,
));
std::thread::sleep(std::time::Duration::from_millis(100));
// move back to make sure the other pane is in the previous tab
@@ -3806,6 +3845,7 @@ pub fn screen_can_break_floating_pane_to_a_new_tab() {
true,
(1, false),
None,
+ None,
));
std::thread::sleep(std::time::Duration::from_millis(200));
// move back to make sure the other pane is in the previous tab
@@ -3880,6 +3920,7 @@ pub fn screen_can_break_plugin_pane_to_a_new_tab() {
true,
(1, false),
None,
+ None,
));
std::thread::sleep(std::time::Duration::from_millis(100));
// move back to make sure the other pane is in the previous tab
@@ -3958,6 +3999,7 @@ pub fn screen_can_break_floating_plugin_pane_to_a_new_tab() {
true,
(1, false),
None,
+ None,
));
std::thread::sleep(std::time::Duration::from_millis(100));
// move back to make sure the other pane is in the previous tab
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap
index e36f78b8..d322d6c1 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_default_parameters.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
index 73fec393..b21357e7 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_line_number.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap
index c09ce65b..fc5e1af5 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_edit_action_with_split_direction.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(Some(Down)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), Tiled(Some(Down)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap
index ca824efe..2428bce0 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_launch_or_focus_plugin_action.snap
@@ -36,6 +36,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
index de7e1ed5..fcb5c135 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_command_and_cwd.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", new_pane_instruction)"
---
-Some(SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Tiled(Some(Right)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false))
+Some(SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Tiled(Some(Right)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false))
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap
index 75f460d5..57aaa650 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_default_parameters.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(None), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap
index 4d5b11c8..3c3d85e7 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_floating_pane_and_coordinates.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Floating(Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None })), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(Some(RunCommand(RunCommand { command: "htop", args: [], cwd: Some("/some/folder"), hold_on_close: true, hold_on_start: false, originating_plugin: None, use_terminal_title: false })), None, Floating(Some(FloatingPaneCoordinates { x: Some(Fixed(10)), y: None, width: Some(Percent(20)), height: None, pinned: None })), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap
index 7ac623b8..2bdd6975 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_pane_action_with_split_direction.snap
@@ -2,4 +2,4 @@
source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", *received_pty_instructions.lock().unwrap())"
---
-[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(Some(Right)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None }), false), Exit]
+[UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), SpawnTerminal(None, None, Tiled(Some(Right)), false, ClientId(10), Some(NotificationEnd { channel: None, exit_status: None, unblock_condition: None, affected_pane_id: None }), false), Exit]
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap
index 30640244..0bdf3e1d 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_default_params.snap
@@ -59,6 +59,8 @@ Some(
),
[],
0,
+ None,
+ false,
true,
(
1,
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap
index 1a0677e6..d61ae4d7 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_new_tab_action_with_name_and_layout.snap
@@ -86,6 +86,8 @@ NewTab(
),
[],
1,
+ None,
+ false,
true,
(
10,
@@ -95,6 +97,8 @@ NewTab(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
)
diff --git a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_query_tab_names_action.snap b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_query_tab_names_action.snap
index ed703453..5c157762 100644
--- a/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_query_tab_names_action.snap
+++ b/zellij-server/src/unit/snapshots/zellij_server__screen__screen_tests__send_cli_query_tab_names_action.snap
@@ -13,6 +13,8 @@ Some(
NotificationEnd {
channel: None,
exit_status: None,
+ unblock_condition: None,
+ affected_pane_id: None,
},
),
),