zellij_utils/
cli.rs

1use crate::data::{Direction, InputMode, Resize};
2use crate::setup::Setup;
3use crate::{
4    consts::{ZELLIJ_CONFIG_DIR_ENV, ZELLIJ_CONFIG_FILE_ENV},
5    input::{layout::PluginUserConfiguration, options::CliOptions},
6};
7use clap::{Args, Parser, Subcommand};
8use serde::{Deserialize, Serialize};
9use std::net::IpAddr;
10use std::path::PathBuf;
11use url::Url;
12
13fn validate_session(name: &str) -> Result<String, String> {
14    #[cfg(unix)]
15    {
16        use crate::consts::ZELLIJ_SOCK_MAX_LENGTH;
17
18        let mut socket_path = crate::consts::ZELLIJ_SOCK_DIR.clone();
19        socket_path.push(name);
20
21        if socket_path.as_os_str().len() >= ZELLIJ_SOCK_MAX_LENGTH {
22            // socket path must be less than 108 bytes
23            let available_length = ZELLIJ_SOCK_MAX_LENGTH
24                .saturating_sub(socket_path.as_os_str().len())
25                .saturating_sub(1);
26
27            return Err(format!(
28                "session name must be less than {} characters",
29                available_length
30            ));
31        };
32    };
33
34    Ok(name.to_owned())
35}
36
37#[derive(Parser, Default, Debug, Clone, Serialize, Deserialize)]
38#[clap(version, name = "zellij")]
39pub struct CliArgs {
40    /// Maximum panes on screen, caution: opening more panes will close old ones
41    #[clap(long, value_parser)]
42    pub max_panes: Option<usize>,
43
44    /// Change where zellij looks for plugins
45    #[clap(long, value_parser, overrides_with = "data_dir")]
46    pub data_dir: Option<PathBuf>,
47
48    /// Run server listening at the specified socket path
49    #[clap(long, value_parser, hide = true, overrides_with = "server")]
50    pub server: Option<PathBuf>,
51
52    /// Specify name of a new session
53    #[clap(long, short, overrides_with = "session", value_parser = validate_session)]
54    pub session: Option<String>,
55
56    /// Name of a predefined layout inside the layout directory or the path to a layout file
57    /// if inside a session (or using the --session flag) will be added to the session as a new tab
58    /// or tabs, otherwise will start a new session
59    #[clap(short, long, value_parser, overrides_with = "layout")]
60    pub layout: Option<PathBuf>,
61
62    /// Name of a predefined layout inside the layout directory or the path to a layout file
63    /// Will always start a new session, even if inside an existing session
64    #[clap(short, long, value_parser, overrides_with = "new_session_with_layout")]
65    pub new_session_with_layout: Option<PathBuf>,
66
67    /// Change where zellij looks for the configuration file
68    #[clap(short, long, overrides_with = "config", env = ZELLIJ_CONFIG_FILE_ENV, value_parser)]
69    pub config: Option<PathBuf>,
70
71    /// Change where zellij looks for the configuration directory
72    #[clap(long, overrides_with = "config_dir", env = ZELLIJ_CONFIG_DIR_ENV, value_parser)]
73    pub config_dir: Option<PathBuf>,
74
75    #[clap(subcommand)]
76    pub command: Option<Command>,
77
78    /// Specify emitting additional debug information
79    #[clap(short, long, value_parser)]
80    pub debug: bool,
81}
82
83#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
84pub enum Command {
85    /// Change the behaviour of zellij
86    #[clap(name = "options", value_parser)]
87    Options(CliOptions),
88
89    /// Setup zellij and check its configuration
90    #[clap(name = "setup", value_parser)]
91    Setup(Setup),
92
93    /// Run a web server to serve terminal sessions
94    #[clap(name = "web", value_parser)]
95    Web(WebCli),
96
97    /// Explore existing zellij sessions
98    #[clap(flatten)]
99    Sessions(Sessions),
100}
101
102#[derive(Debug, Clone, Args, Serialize, Deserialize)]
103pub struct WebCli {
104    /// Start the server (default unless other arguments are specified)
105    #[clap(long, value_parser, display_order = 1)]
106    pub start: bool,
107
108    /// Stop the server
109    #[clap(long, value_parser, exclusive(true), display_order = 2)]
110    pub stop: bool,
111
112    /// Get the server status
113    #[clap(long, value_parser, exclusive(true), display_order = 3)]
114    pub status: bool,
115
116    /// Run the server in the background
117    #[clap(
118        short,
119        long,
120        value_parser,
121        conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
122        display_order = 4
123    )]
124    pub daemonize: bool,
125    /// Create a login token for the web interface, will only be displayed once and cannot later be
126    /// retrieved. Returns the token name and the token.
127    #[clap(long, value_parser, exclusive(true), display_order = 5)]
128    pub create_token: bool,
129    /// Revoke a login token by its name
130    #[clap(
131        long,
132        value_parser,
133        exclusive(true),
134        value_name = "TOKEN NAME",
135        display_order = 6
136    )]
137    pub revoke_token: Option<String>,
138    /// Revoke all login tokens
139    #[clap(long, value_parser, exclusive(true), display_order = 7)]
140    pub revoke_all_tokens: bool,
141    /// List token names and their creation dates (cannot show actual tokens)
142    #[clap(long, value_parser, exclusive(true), display_order = 8)]
143    pub list_tokens: bool,
144    /// The ip address to listen on locally for connections (defaults to 127.0.0.1)
145    #[clap(
146        long,
147        value_parser,
148        conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
149        display_order = 9
150    )]
151    pub ip: Option<IpAddr>,
152    /// The port to listen on locally for connections (defaults to 8082)
153    #[clap(
154        long,
155        value_parser,
156        conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
157        display_order = 10
158    )]
159    pub port: Option<u16>,
160    /// The path to the SSL certificate (required if not listening on 127.0.0.1)
161    #[clap(
162        long,
163        value_parser,
164        conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
165        display_order = 11
166    )]
167    pub cert: Option<PathBuf>,
168    /// The path to the SSL key (required if not listening on 127.0.0.1)
169    #[clap(
170        long,
171        value_parser,
172        conflicts_with_all(&["stop", "status", "create-token", "revoke-token", "revoke-all-tokens"]),
173        display_order = 12
174    )]
175    pub key: Option<PathBuf>,
176}
177
178impl WebCli {
179    pub fn get_start(&self) -> bool {
180        self.start
181            || !(self.stop
182                || self.status
183                || self.create_token
184                || self.revoke_token.is_some()
185                || self.revoke_all_tokens
186                || self.list_tokens)
187    }
188}
189
190#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
191pub enum SessionCommand {
192    /// Change the behaviour of zellij
193    #[clap(name = "options")]
194    Options(CliOptions),
195}
196
197#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
198pub enum Sessions {
199    /// List active sessions
200    #[clap(visible_alias = "ls")]
201    ListSessions {
202        /// Do not add colors and formatting to the list (useful for parsing)
203        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
204        no_formatting: bool,
205
206        /// Print just the session name
207        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
208        short: bool,
209
210        /// List the sessions in reverse order (default is ascending order)
211        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
212        reverse: bool,
213    },
214    /// List existing plugin aliases
215    #[clap(visible_alias = "la")]
216    ListAliases,
217    /// Attach to a session
218    #[clap(visible_alias = "a")]
219    Attach {
220        /// Name of the session to attach to.
221        #[clap(value_parser)]
222        session_name: Option<String>,
223
224        /// Create a session if one does not exist.
225        #[clap(short, long, value_parser)]
226        create: bool,
227
228        /// Create a detached session in the background if one does not exist
229        #[clap(short('b'), long, value_parser)]
230        create_background: bool,
231
232        /// Number of the session index in the active sessions ordered creation date.
233        #[clap(long, value_parser)]
234        index: Option<usize>,
235
236        /// Change the behaviour of zellij
237        #[clap(subcommand, name = "options")]
238        options: Option<Box<SessionCommand>>,
239
240        /// If resurrecting a dead session, immediately run all its commands on startup
241        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
242        force_run_commands: bool,
243    },
244
245    /// Kill a specific session
246    #[clap(visible_alias = "k")]
247    KillSession {
248        /// Name of target session
249        #[clap(value_parser)]
250        target_session: Option<String>,
251    },
252
253    /// Delete a specific session
254    #[clap(visible_alias = "d")]
255    DeleteSession {
256        /// Name of target session
257        #[clap(value_parser)]
258        target_session: Option<String>,
259        /// Kill the session if it's running before deleting it
260        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
261        force: bool,
262    },
263
264    /// Kill all sessions
265    #[clap(visible_alias = "ka")]
266    KillAllSessions {
267        /// Automatic yes to prompts
268        #[clap(short, long, value_parser)]
269        yes: bool,
270    },
271
272    /// Delete all sessions
273    #[clap(visible_alias = "da")]
274    DeleteAllSessions {
275        /// Automatic yes to prompts
276        #[clap(short, long, value_parser)]
277        yes: bool,
278        /// Kill the sessions if they're running before deleting them
279        #[clap(short, long, value_parser, takes_value(false), default_value("false"))]
280        force: bool,
281    },
282
283    /// Send actions to a specific session
284    #[clap(visible_alias = "ac")]
285    #[clap(subcommand)]
286    Action(CliAction),
287    /// Run a command in a new pane
288    #[clap(visible_alias = "r")]
289    Run {
290        /// Command to run
291        #[clap(last(true), required(true))]
292        command: Vec<String>,
293
294        /// Direction to open the new pane in
295        #[clap(short, long, value_parser, conflicts_with("floating"))]
296        direction: Option<Direction>,
297
298        /// Change the working directory of the new pane
299        #[clap(long, value_parser)]
300        cwd: Option<PathBuf>,
301
302        /// Open the new pane in floating mode
303        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
304        floating: bool,
305
306        /// Open the new pane in place of the current pane, temporarily suspending it
307        #[clap(
308            short,
309            long,
310            value_parser,
311            default_value("false"),
312            takes_value(false),
313            conflicts_with("floating"),
314            conflicts_with("direction")
315        )]
316        in_place: bool,
317
318        /// Name of the new pane
319        #[clap(short, long, value_parser)]
320        name: Option<String>,
321
322        /// Close the pane immediately when its command exits
323        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
324        close_on_exit: bool,
325
326        /// Start the command suspended, only running after you first presses ENTER
327        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
328        start_suspended: bool,
329
330        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
331        #[clap(short, long, requires("floating"))]
332        x: Option<String>,
333        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
334        #[clap(short, long, requires("floating"))]
335        y: Option<String>,
336        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
337        #[clap(long, requires("floating"))]
338        width: Option<String>,
339        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
340        #[clap(long, requires("floating"))]
341        height: Option<String>,
342        /// Whether to pin a floating pane so that it is always on top
343        #[clap(long, requires("floating"))]
344        pinned: Option<bool>,
345        #[clap(
346            long,
347            conflicts_with("floating"),
348            conflicts_with("direction"),
349            value_parser,
350            default_value("false"),
351            takes_value(false)
352        )]
353        stacked: bool,
354    },
355    /// Load a plugin
356    #[clap(visible_alias = "p")]
357    Plugin {
358        /// Plugin URL, can either start with http(s), file: or zellij:
359        #[clap(last(true), required(true))]
360        url: String,
361
362        /// Plugin configuration
363        #[clap(short, long, value_parser)]
364        configuration: Option<PluginUserConfiguration>,
365
366        /// Open the new pane in floating mode
367        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
368        floating: bool,
369
370        /// Open the new pane in place of the current pane, temporarily suspending it
371        #[clap(
372            short,
373            long,
374            value_parser,
375            default_value("false"),
376            takes_value(false),
377            conflicts_with("floating")
378        )]
379        in_place: bool,
380
381        /// Skip the memory and HD cache and force recompile of the plugin (good for development)
382        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
383        skip_plugin_cache: bool,
384        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
385        #[clap(short, long, requires("floating"))]
386        x: Option<String>,
387        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
388        #[clap(short, long, requires("floating"))]
389        y: Option<String>,
390        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
391        #[clap(long, requires("floating"))]
392        width: Option<String>,
393        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
394        #[clap(long, requires("floating"))]
395        height: Option<String>,
396        /// Whether to pin a floating pane so that it is always on top
397        #[clap(long, requires("floating"))]
398        pinned: Option<bool>,
399    },
400    /// Edit file with default $EDITOR / $VISUAL
401    #[clap(visible_alias = "e")]
402    Edit {
403        file: PathBuf,
404
405        /// Open the file in the specified line number
406        #[clap(short, long, value_parser)]
407        line_number: Option<usize>,
408
409        /// Direction to open the new pane in
410        #[clap(short, long, value_parser, conflicts_with("floating"))]
411        direction: Option<Direction>,
412
413        /// Open the new pane in place of the current pane, temporarily suspending it
414        #[clap(
415            short,
416            long,
417            value_parser,
418            default_value("false"),
419            takes_value(false),
420            conflicts_with("floating"),
421            conflicts_with("direction")
422        )]
423        in_place: bool,
424
425        /// Open the new pane in floating mode
426        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
427        floating: bool,
428
429        /// Change the working directory of the editor
430        #[clap(long, value_parser)]
431        cwd: Option<PathBuf>,
432        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
433        #[clap(short, long, requires("floating"))]
434        x: Option<String>,
435        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
436        #[clap(short, long, requires("floating"))]
437        y: Option<String>,
438        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
439        #[clap(long, requires("floating"))]
440        width: Option<String>,
441        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
442        #[clap(long, requires("floating"))]
443        height: Option<String>,
444        /// Whether to pin a floating pane so that it is always on top
445        #[clap(long, requires("floating"))]
446        pinned: Option<bool>,
447    },
448    ConvertConfig {
449        old_config_file: PathBuf,
450    },
451    ConvertLayout {
452        old_layout_file: PathBuf,
453    },
454    ConvertTheme {
455        old_theme_file: PathBuf,
456    },
457    /// Send data to one or more plugins, launch them if they are not running.
458    #[clap(override_usage(
459r#"
460zellij pipe [OPTIONS] [--] <PAYLOAD>
461
462* Send data to a specific plugin:
463
464zellij pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
465
466* To all running plugins (that are listening):
467
468zellij pipe --name my_pipe_name -- my_arbitrary_data
469
470* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
471
472tail -f /tmp/my-live-logfile | zellij pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
473"#))]
474    Pipe {
475        /// The name of the pipe
476        #[clap(short, long, value_parser, display_order(1))]
477        name: Option<String>,
478        /// The data to send down this pipe (if blank, will listen to STDIN)
479        payload: Option<String>,
480
481        #[clap(short, long, value_parser, display_order(2))]
482        /// The args of the pipe
483        args: Option<PluginUserConfiguration>, // TODO: we might want to not re-use
484        // PluginUserConfiguration
485        /// The plugin url (eg. file:/tmp/my-plugin.wasm) to direct this pipe to, if not specified,
486        /// will be sent to all plugins, if specified and is not running, the plugin will be launched
487        #[clap(short, long, value_parser, display_order(3))]
488        plugin: Option<String>,
489        /// The plugin configuration (note: the same plugin with different configuration is
490        /// considered a different plugin for the purposes of determining the pipe destination)
491        #[clap(short('c'), long, value_parser, display_order(4))]
492        plugin_configuration: Option<PluginUserConfiguration>,
493    },
494}
495
496#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
497pub enum CliAction {
498    /// Write bytes to the terminal.
499    Write {
500        bytes: Vec<u8>,
501    },
502    /// Write characters to the terminal.
503    WriteChars {
504        chars: String,
505    },
506    /// [increase|decrease] the focused panes area at the [left|down|up|right] border.
507    Resize {
508        resize: Resize,
509        direction: Option<Direction>,
510    },
511    /// Change focus to the next pane
512    FocusNextPane,
513    /// Change focus to the previous pane
514    FocusPreviousPane,
515    /// Move the focused pane in the specified direction. [right|left|up|down]
516    MoveFocus {
517        direction: Direction,
518    },
519    /// Move focus to the pane or tab (if on screen edge) in the specified direction
520    /// [right|left|up|down]
521    MoveFocusOrTab {
522        direction: Direction,
523    },
524    /// Change the location of the focused pane in the specified direction or rotate forwrads
525    /// [right|left|up|down]
526    MovePane {
527        direction: Option<Direction>,
528    },
529    /// Rotate the location of the previous pane backwards
530    MovePaneBackwards,
531    /// Clear all buffers for a focused pane
532    Clear,
533    /// Dump the focused pane to a file
534    DumpScreen {
535        path: PathBuf,
536
537        /// Dump the pane with full scrollback
538        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
539        full: bool,
540    },
541    /// Dump current layout to stdout
542    DumpLayout,
543    /// Open the pane scrollback in your default editor
544    EditScrollback,
545    /// Scroll up in the focused pane
546    ScrollUp,
547    /// Scroll down in focus pane.
548    ScrollDown,
549    /// Scroll down to bottom in focus pane.
550    ScrollToBottom,
551    /// Scroll up to top in focus pane.
552    ScrollToTop,
553    /// Scroll up one page in focus pane.
554    PageScrollUp,
555    /// Scroll down one page in focus pane.
556    PageScrollDown,
557    /// Scroll up half page in focus pane.
558    HalfPageScrollUp,
559    /// Scroll down half page in focus pane.
560    HalfPageScrollDown,
561    /// Toggle between fullscreen focus pane and normal layout.
562    ToggleFullscreen,
563    /// Toggle frames around panes in the UI
564    TogglePaneFrames,
565    /// Toggle between sending text commands to all panes on the current tab and normal mode.
566    ToggleActiveSyncTab,
567    /// Open a new pane in the specified direction [right|down]
568    /// If no direction is specified, will try to use the biggest available space.
569    NewPane {
570        /// Direction to open the new pane in
571        #[clap(short, long, value_parser, conflicts_with("floating"))]
572        direction: Option<Direction>,
573
574        #[clap(last(true))]
575        command: Vec<String>,
576
577        #[clap(short, long, conflicts_with("command"), conflicts_with("direction"))]
578        plugin: Option<String>,
579
580        /// Change the working directory of the new pane
581        #[clap(long, value_parser)]
582        cwd: Option<PathBuf>,
583
584        /// Open the new pane in floating mode
585        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
586        floating: bool,
587
588        /// Open the new pane in place of the current pane, temporarily suspending it
589        #[clap(
590            short,
591            long,
592            value_parser,
593            default_value("false"),
594            takes_value(false),
595            conflicts_with("floating"),
596            conflicts_with("direction")
597        )]
598        in_place: bool,
599
600        /// Name of the new pane
601        #[clap(short, long, value_parser)]
602        name: Option<String>,
603
604        /// Close the pane immediately when its command exits
605        #[clap(
606            short,
607            long,
608            value_parser,
609            default_value("false"),
610            takes_value(false),
611            requires("command")
612        )]
613        close_on_exit: bool,
614        /// Start the command suspended, only running it after the you first press ENTER
615        #[clap(
616            short,
617            long,
618            value_parser,
619            default_value("false"),
620            takes_value(false),
621            requires("command")
622        )]
623        start_suspended: bool,
624        #[clap(long, value_parser)]
625        configuration: Option<PluginUserConfiguration>,
626        #[clap(long, value_parser)]
627        skip_plugin_cache: bool,
628        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
629        #[clap(short, long, requires("floating"))]
630        x: Option<String>,
631        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
632        #[clap(short, long, requires("floating"))]
633        y: Option<String>,
634        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
635        #[clap(long, requires("floating"))]
636        width: Option<String>,
637        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
638        #[clap(long, requires("floating"))]
639        height: Option<String>,
640        /// Whether to pin a floating pane so that it is always on top
641        #[clap(long, requires("floating"))]
642        pinned: Option<bool>,
643        #[clap(
644            long,
645            conflicts_with("floating"),
646            conflicts_with("direction"),
647            value_parser,
648            default_value("false"),
649            takes_value(false)
650        )]
651        stacked: bool,
652    },
653    /// Open the specified file in a new zellij pane with your default EDITOR
654    Edit {
655        file: PathBuf,
656
657        /// Direction to open the new pane in
658        #[clap(short, long, value_parser, conflicts_with("floating"))]
659        direction: Option<Direction>,
660
661        /// Open the file in the specified line number
662        #[clap(short, long, value_parser)]
663        line_number: Option<usize>,
664
665        /// Open the new pane in floating mode
666        #[clap(short, long, value_parser, default_value("false"), takes_value(false))]
667        floating: bool,
668
669        /// Open the new pane in place of the current pane, temporarily suspending it
670        #[clap(
671            short,
672            long,
673            value_parser,
674            default_value("false"),
675            takes_value(false),
676            conflicts_with("floating"),
677            conflicts_with("direction")
678        )]
679        in_place: bool,
680
681        /// Change the working directory of the editor
682        #[clap(long, value_parser)]
683        cwd: Option<PathBuf>,
684        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
685        #[clap(short, long, requires("floating"))]
686        x: Option<String>,
687        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
688        #[clap(short, long, requires("floating"))]
689        y: Option<String>,
690        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
691        #[clap(long, requires("floating"))]
692        width: Option<String>,
693        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
694        #[clap(long, requires("floating"))]
695        height: Option<String>,
696        /// Whether to pin a floating pane so that it is always on top
697        #[clap(long, requires("floating"))]
698        pinned: Option<bool>,
699    },
700    /// Switch input mode of all connected clients [locked|pane|tab|resize|move|search|session]
701    SwitchMode {
702        input_mode: InputMode,
703    },
704    /// Embed focused pane if floating or float focused pane if embedded
705    TogglePaneEmbedOrFloating,
706    /// Toggle the visibility of all floating panes in the current Tab, open one if none exist
707    ToggleFloatingPanes,
708    /// Close the focused pane.
709    ClosePane,
710    /// Renames the focused pane
711    RenamePane {
712        name: String,
713    },
714    /// Remove a previously set pane name
715    UndoRenamePane,
716    /// Go to the next tab.
717    GoToNextTab,
718    /// Go to the previous tab.
719    GoToPreviousTab,
720    /// Close the current tab.
721    CloseTab,
722    /// Go to tab with index [index]
723    GoToTab {
724        index: u32,
725    },
726    /// Go to tab with name [name]
727    GoToTabName {
728        name: String,
729        /// Create a tab if one does not exist.
730        #[clap(short, long, value_parser)]
731        create: bool,
732    },
733    /// Renames the focused pane
734    RenameTab {
735        name: String,
736    },
737    /// Remove a previously set tab name
738    UndoRenameTab,
739    /// Create a new tab, optionally with a specified tab layout and name
740    NewTab {
741        /// Layout to use for the new tab
742        #[clap(short, long, value_parser)]
743        layout: Option<PathBuf>,
744
745        /// Default folder to look for layouts
746        #[clap(long, value_parser, requires("layout"))]
747        layout_dir: Option<PathBuf>,
748
749        /// Name of the new tab
750        #[clap(short, long, value_parser)]
751        name: Option<String>,
752
753        /// Change the working directory of the new tab
754        #[clap(short, long, value_parser)]
755        cwd: Option<PathBuf>,
756    },
757    /// Move the focused tab in the specified direction. [right|left]
758    MoveTab {
759        direction: Direction,
760    },
761    PreviousSwapLayout,
762    NextSwapLayout,
763    /// Query all tab names
764    QueryTabNames,
765    StartOrReloadPlugin {
766        url: String,
767        #[clap(short, long, value_parser)]
768        configuration: Option<PluginUserConfiguration>,
769    },
770    LaunchOrFocusPlugin {
771        #[clap(short, long, value_parser)]
772        floating: bool,
773        #[clap(short, long, value_parser)]
774        in_place: bool,
775        #[clap(short, long, value_parser)]
776        move_to_focused_tab: bool,
777        url: String,
778        #[clap(short, long, value_parser)]
779        configuration: Option<PluginUserConfiguration>,
780        #[clap(short, long, value_parser)]
781        skip_plugin_cache: bool,
782    },
783    LaunchPlugin {
784        #[clap(short, long, value_parser)]
785        floating: bool,
786        #[clap(short, long, value_parser)]
787        in_place: bool,
788        url: Url,
789        #[clap(short, long, value_parser)]
790        configuration: Option<PluginUserConfiguration>,
791        #[clap(short, long, value_parser)]
792        skip_plugin_cache: bool,
793    },
794    RenameSession {
795        name: String,
796    },
797    /// Send data to one or more plugins, launch them if they are not running.
798    #[clap(override_usage(
799r#"
800zellij action pipe [OPTIONS] [--] <PAYLOAD>
801
802* Send data to a specific plugin:
803
804zellij action pipe --plugin file:/path/to/my/plugin.wasm --name my_pipe_name -- my_arbitrary_data
805
806* To all running plugins (that are listening):
807
808zellij action pipe --name my_pipe_name -- my_arbitrary_data
809
810* Pipe data into this command's STDIN and get output from the plugin on this command's STDOUT
811
812tail -f /tmp/my-live-logfile | zellij action pipe --name logs --plugin https://example.com/my-plugin.wasm | wc -l
813"#))]
814    Pipe {
815        /// The name of the pipe
816        #[clap(short, long, value_parser, display_order(1))]
817        name: Option<String>,
818        /// The data to send down this pipe (if blank, will listen to STDIN)
819        payload: Option<String>,
820
821        #[clap(short, long, value_parser, display_order(2))]
822        /// The args of the pipe
823        args: Option<PluginUserConfiguration>, // TODO: we might want to not re-use
824        // PluginUserConfiguration
825        /// The plugin url (eg. file:/tmp/my-plugin.wasm) to direct this pipe to, if not specified,
826        /// will be sent to all plugins, if specified and is not running, the plugin will be launched
827        #[clap(short, long, value_parser, display_order(3))]
828        plugin: Option<String>,
829        /// The plugin configuration (note: the same plugin with different configuration is
830        /// considered a different plugin for the purposes of determining the pipe destination)
831        #[clap(short('c'), long, value_parser, display_order(4))]
832        plugin_configuration: Option<PluginUserConfiguration>,
833        /// Launch a new plugin even if one is already running
834        #[clap(
835            short('l'),
836            long,
837            value_parser,
838            takes_value(false),
839            default_value("false"),
840            display_order(5)
841        )]
842        force_launch_plugin: bool,
843        /// If launching a new plugin, skip cache and force-compile the plugin
844        #[clap(
845            short('s'),
846            long,
847            value_parser,
848            takes_value(false),
849            default_value("false"),
850            display_order(6)
851        )]
852        skip_plugin_cache: bool,
853        /// If launching a plugin, should it be floating or not, defaults to floating
854        #[clap(short('f'), long, value_parser, display_order(7))]
855        floating_plugin: Option<bool>,
856        /// If launching a plugin, launch it in-place (on top of the current pane)
857        #[clap(
858            short('i'),
859            long,
860            value_parser,
861            conflicts_with("floating-plugin"),
862            display_order(8)
863        )]
864        in_place_plugin: Option<bool>,
865        /// If launching a plugin, specify its working directory
866        #[clap(short('w'), long, value_parser, display_order(9))]
867        plugin_cwd: Option<PathBuf>,
868        /// If launching a plugin, specify its pane title
869        #[clap(short('t'), long, value_parser, display_order(10))]
870        plugin_title: Option<String>,
871    },
872    ListClients,
873    TogglePanePinned,
874    /// Stack pane ids
875    /// Ids are a space separated list of pane ids.
876    /// They should either be in the form of `terminal_<int>` (eg. terminal_1), `plugin_<int>` (eg.
877    /// plugin_1) or bare integers in which case they'll be considered terminals (eg. 1 is
878    /// the equivalent of terminal_1)
879    ///
880    /// Example: zellij action stack-panes -- terminal_1 plugin_2 3
881    StackPanes {
882        #[clap(last(true), required(true))]
883        pane_ids: Vec<String>,
884    },
885    ChangeFloatingPaneCoordinates {
886        /// The pane_id of the floating pane, eg.  terminal_1, plugin_2 or 3 (equivalent to
887        /// terminal_3)
888        #[clap(short, long, value_parser)]
889        pane_id: String,
890        /// The x coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
891        #[clap(short, long)]
892        x: Option<String>,
893        /// The y coordinates if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
894        #[clap(short, long)]
895        y: Option<String>,
896        /// The width if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
897        #[clap(long)]
898        width: Option<String>,
899        /// The height if the pane is floating as a bare integer (eg. 1) or percent (eg. 10%)
900        #[clap(long)]
901        height: Option<String>,
902        /// Whether to pin a floating pane so that it is always on top
903        #[clap(long)]
904        pinned: Option<bool>,
905    },
906}