aboutsummaryrefslogtreecommitdiffstats
path: root/default-plugins/sequence/src/ui/components.rs
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-02-17 15:43:28 +0100
committerGitHub <noreply@github.com>2026-02-17 15:43:28 +0100
commit4e66a0beff5e41bda1dbde521fa1d2858c46fe7b (patch)
tree380bee5eeac56bea2ebf341cccbe69615585e394 /default-plugins/sequence/src/ui/components.rs
parentFix startup message and cache file creation (#4705) (diff)
downloadsl0ppij-4e66a0beff5e41bda1dbde521fa1d2858c46fe7b.tar.gz
sl0ppij-4e66a0beff5e41bda1dbde521fa1d2858c46fe7b.tar.xz
sl0ppij-4e66a0beff5e41bda1dbde521fa1d2858c46fe7b.zip
Revamp the sequence plugin (#4713)
* initial pipe interface * initial open_x_pane_in_new_tab plugin api implementation * fix: send CommandPaneExited for command panes opened as initial_terminals in new tabs from plugins * open panes in place of specific pane id plugin commands - initial implementation * sequence and plugin api ux * focus self when starting * combine ui and pipe paths * highlight selected pane * initial sequence removal of editing capabilities * fix: translate plugin paths in api * properly trim commands when loaded from pipe or pasted text * minor ux fixes * get ctrl c to work properly * behavior fixes * more ui fixes * fix tests * close tab and end sequence properly * default to AND * zellij sequence alias * add alias * consolidate paths * rustfmt * fix ui padding * add pr * fix: reset scrolling when resetting terminal state
Diffstat (limited to 'default-plugins/sequence/src/ui/components.rs')
-rw-r--r--default-plugins/sequence/src/ui/components.rs115
1 files changed, 43 insertions, 72 deletions
diff --git a/default-plugins/sequence/src/ui/components.rs b/default-plugins/sequence/src/ui/components.rs
index 3fe2ae1a..bc319567 100644
--- a/default-plugins/sequence/src/ui/components.rs
+++ b/default-plugins/sequence/src/ui/components.rs
@@ -6,6 +6,11 @@ use zellij_tile::prelude::*;
const SPINNER_FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧"];
+const INTRO_PART1: &str = "Paste a series of commands to run in sequence";
+const INTRO_PART2: &str = "or <e> to edit in your editor";
+const INTRO_FULL: &str =
+ "Paste a series of commands to run in sequence or <e> to edit in your editor";
+
const HELP_RUNNING_WITH_SELECTION: &str = "<Ctrl c> - interrupt, <Enter> - run from selected";
const HELP_RUNNING_NO_SELECTION: &str = "<Ctrl c> - interrupt, <↓↑> - navigate";
const HELP_STOPPED_WITH_SELECTION: &str =
@@ -13,15 +18,7 @@ const HELP_STOPPED_WITH_SELECTION: &str =
const HELP_STOPPED_NO_SELECTION: &str =
"<Ctrl w> - close all, <↓↑> - navigate, <Enter> - run from first";
-const HELP_ONE_PENDING_COMMAND: &str = "<Enter> - run, <Ctrl Enter> - add command";
-
-const HELP_ALL_COMMANDS_PENDING: &str =
- "<Enter> - run, <Ctrl Enter> - add command, <↓↑> - navigate";
-const HELP_ALL_COMMANDS_PENDING_WITH_SELECTION: &str =
- "<Enter> - run, <Ctrl Enter> - add command, <↓↑> - navigate, <e> - edit selected";
-
-const HELP_EDITING_FIRST_LINE: &str =
- "<Enter> - accept, <Ctrl Enter> - add command, <↓↑> - navigate";
+const HELP_PENDING: &str = "<e> - edit, <Enter> - run";
fn select_help_text(sequence: &State) -> &'static str {
let is_running = sequence
@@ -35,18 +32,9 @@ fn select_help_text(sequence: &State) -> &'static str {
.iter()
.all(|command| matches!(command.get_status(), CommandStatus::Pending));
let has_selection = sequence.selection.current_selected_command_index.is_some();
- let is_editing = sequence.editing.editing_input.is_some();
if all_pending && !sequence.execution.is_running {
- if sequence.execution.all_commands.len() == 1 {
- HELP_ONE_PENDING_COMMAND
- } else if has_selection && !is_editing {
- HELP_ALL_COMMANDS_PENDING_WITH_SELECTION
- } else if is_editing {
- HELP_EDITING_FIRST_LINE
- } else {
- HELP_ALL_COMMANDS_PENDING
- }
+ HELP_PENDING
} else if is_running {
if has_selection {
HELP_RUNNING_WITH_SELECTION
@@ -54,10 +42,8 @@ fn select_help_text(sequence: &State) -> &'static str {
HELP_RUNNING_NO_SELECTION
}
} else {
- if has_selection && !is_editing {
+ if has_selection {
HELP_STOPPED_WITH_SELECTION
- } else if is_editing {
- HELP_EDITING_FIRST_LINE
} else {
HELP_STOPPED_NO_SELECTION
}
@@ -71,7 +57,6 @@ fn style_help_text(text: &str) -> Text {
.color_substring(3, "<↓↑>")
.color_substring(3, "<Esc>")
.color_substring(3, "<Enter>")
- .color_substring(3, "<Ctrl Enter>")
.color_substring(3, "<e>")
}
@@ -79,33 +64,6 @@ pub fn render_help_lines(
sequence: &State,
max_width: Option<usize>,
) -> (Text, usize, Option<(Text, usize)>) {
- let is_editing = sequence.editing.editing_input.is_some();
-
- if is_editing {
- let help_text = select_help_text(sequence);
- let (truncated_text, help_len) = if let Some(width) = max_width {
- truncate_help_line(help_text, width)
- } else {
- (help_text.to_string(), help_text.chars().count())
- };
- let first_line = (style_help_text(&truncated_text).unbold_all(), help_len);
-
- let editing_help_text = "Navigate with cd, chain with ||, &&, ; ";
- let editing_help = Text::new(editing_help_text)
- .color_substring(3, "cd")
- .color_substring(3, "||")
- .color_substring(3, "&&")
- .color_substring(3, ";")
- .unbold_all();
- let editing_len = editing_help_text.len();
-
- return (
- first_line.0,
- first_line.1,
- Some((editing_help, editing_len)),
- );
- }
-
let help_text = select_help_text(sequence);
if let Some(width) = max_width {
@@ -139,6 +97,38 @@ pub fn render_help_lines(
}
}
+/// Returns `(text, x, y)` tuples for the main screen intro hint.
+/// The text is centered horizontally; if it does not fit on one line, two lines
+/// are returned separated by one blank line (1-line padding).
+pub fn render_intro_hint(rows: usize, cols: usize) -> Vec<(Text, usize, usize)> {
+ let full_len = INTRO_FULL.chars().count();
+
+ if full_len <= cols {
+ let x = cols.saturating_sub(full_len) / 2;
+ let y = rows / 2;
+ vec![(Text::new(INTRO_FULL).color_substring(3, "<e>"), x, y)]
+ } else {
+ let p1_len = INTRO_PART1.chars().count();
+ let p2_len = INTRO_PART2.chars().count();
+ let x1 = cols.saturating_sub(p1_len) / 2;
+ let x2 = cols.saturating_sub(p2_len) / 2;
+ // 3 display rows: line1, padding, line2 — center the group vertically
+ let start_y = rows.saturating_sub(3) / 2;
+ vec![
+ (
+ Text::new(INTRO_PART1).color_substring(3, "<e>"),
+ x1,
+ start_y,
+ ),
+ (
+ Text::new(INTRO_PART2).color_substring(3, "<e>"),
+ x2,
+ start_y + 2,
+ ),
+ ]
+ }
+}
+
fn split_help_line(help_text: &str, max_width: usize) -> Option<(String, String)> {
let text_width = help_text.chars().count();
@@ -200,7 +190,7 @@ fn chain_cell(chain_type: &crate::state::ChainType) -> Text {
}
}
-fn get_spinner_frame(frame: usize) -> &'static str {
+pub fn get_spinner_frame(frame: usize) -> &'static str {
SPINNER_FRAMES[frame % SPINNER_FRAMES.len()]
}
@@ -371,7 +361,8 @@ pub fn add_command_row(
None => return table,
};
- let cmd_text = &command.get_text();
+ let cmd_text = command.get_text();
+ let cmd_text = cmd_text.trim();
let chain_type = &command.get_chain_type();
let status = &command.get_status();
let command_cwd = command.get_cwd().or_else(|| state.cwd.clone());
@@ -398,26 +389,6 @@ pub fn add_command_row(
return table;
};
- if let Some(text_input) = &state.editing.editing_input {
- if state.selection.current_selected_command_index == Some(index) {
- let (truncated_editing_text, _) = truncate_middle(
- text_input.get_text(),
- available_cmd_width,
- Some(text_input.cursor_position()),
- );
- let (row, _) = command_sequence_row(
- state,
- &command_cwd,
- &truncated_editing_text,
- chain_type,
- &CommandStatus::Pending,
- false,
- overflow_indicator,
- );
- return table.add_styled_row(row);
- }
- }
-
let truncated_cmd_text = truncate_middle(cmd_text, available_cmd_width, None).0;
let (row, _) = command_sequence_row(