diff options
| author | Aram Drevekenin <aram@poor.dev> | 2026-02-17 15:43:28 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-17 15:43:28 +0100 |
| commit | 4e66a0beff5e41bda1dbde521fa1d2858c46fe7b (patch) | |
| tree | 380bee5eeac56bea2ebf341cccbe69615585e394 /default-plugins/sequence/src/ui/components.rs | |
| parent | Fix startup message and cache file creation (#4705) (diff) | |
| download | sl0ppij-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.rs | 115 |
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( |
