cliclack

repository·main·Indexed 18 days ago

https://github.com/fadeevab/cliclack

A Rust library for building beautiful, minimal, and opinionated command-line interfaces inspired by the @clack/prompts npm package. It provides high-level primitives for input prompts with validation and autocomplete, password and confirm prompts, single and multi-selection lists with fuzzy filtering, and progress indicators including spinners and multi-progress bars. It also includes a theme trait for UI customization and a log module for non-interactive styled messages.

Tokens
10.4K
Snippets
42
Records
53
Agent score
63%

What's inside cliclack

  1. MultiSelect interaction controls

    main

    When running a MultiSelect prompt, the following keyboard interactions are supported:

    KeyAction
    Up / kMove cursor up
    Down / jMove cursor down
    Left / hMove cursor left (used in filter mode)
    Right / lMove cursor right (used in filter mode)
    SpaceToggle selection of the current item
    EnterSubmit the current selection
    TypingFilters the list (if filter_mode() was enabled)
  2. Understand the Prompt State machine

    main

    The State<T> enum manages the lifecycle of a prompt interaction. The interaction loop continues as long as the state is Active or Error.

    • Active: The prompt is currently being displayed and waiting for input.
    • Submit(T): The user has successfully completed the interaction. The value of type T is returned as the result of the interaction.
    • Cancel: The user aborted the interaction (e.g., by pressing Esc or Ctrl-C). This results in an io::ErrorKind::Interrupted error.
    • Error(String): An error occurred during the interaction process.
    pub enum State<T> {
        Active,
        Submit(T),
        Cancel,
        Error(String),
    }
  3. How FilteredView works in selection prompts

    main

    A FilteredView is an internal mechanism used within selection prompts to manage and filter a list of items based on user input. When enabled, it tracks a StringCursor representing the user's typed input and maintains a subset of items (items) that match that input.

    Key behaviors:

    • Enabling: Filtering is inactive by default. It must be explicitly enabled via .enable().
    • Input Handling: The .on() method processes keyboard events. When a character is typed, it uses a Suggest implementation to refresh the visible items list based on the current input string.
    • State Management: The .on() method returns a State indicating whether the prompt should remain Active, return an Error (e.g., "No items" when pressing Enter on an empty list), or pass control back to the caller (returning None).
  4. How to provide custom validation logic to Input::validate

    main

    The validate method on Input<T> accepts any closure that follows the signature Fn(&T) -> Result<(), E>. This is made possible by the Validate<T> trait, which provides a blanket implementation for standard Rust closures.

    To validate input, simply pass a closure that takes a reference to your input type and returns Ok(()) if valid, or an Err(E) containing your error details if invalid.

    // Example of passing a closure to a validation method
    input.validate(|val: &String| {
        if val.is_empty() {
            Err("Input cannot be empty")
        } else {
            Ok(())
        }
    })?
  5. How fuzzy suggestion matching works

    main

    When using the built-in implementations for Vec<String> or Vec<Rc<RefCell<T>>>, cliclack uses a fuzzy matching algorithm to rank results:

    • Empty Input: If the input is empty or contains only whitespace, all items are returned in their original order.
    • Similarity Algorithm: It uses the Jaro-Winkler similarity algorithm.
    • Word Bonus: A bonus is added to the score if all whitespace-separated words from the input are present within the item's label.
    • Filtering: Items with a total score of 0.6 or lower are filtered out.
    • Ranking: Results are sorted by their similarity score in descending order.
  6. Set up a prompt session with intro and outro

    main

    To create a polished CLI experience, use intro to print a header at the start of your session and outro (or outro_cancel for cancellations) to print a footer when finished. This helps frame the interaction for the user.

    use cliclack::{intro, outro};
    
    intro("create-my-app")?;
    // Do stuff
    outro("You're all set!")?;
    # Ok::<(), std::io::Error>(())
  7. Customizing CLI output with the Theme trait

    main

    To change the visual appearance of cliclack prompts (colors, symbols, and formatting), implement the Theme trait. The default theme is an implementation of the original @clack/prompts style.

    When implementing Theme, many methods accept a ThemeState argument, allowing you to return different styles depending on whether the prompt is Active, Canceled, Submitted, or in an Error state.

    To apply your custom theme globally, use the set_theme function.

    use console::Style;
    use cliclack::*;
    
    struct MagentaTheme;
    
    impl Theme for MagentaTheme {
        fn state_symbol_color(&self, _state: &ThemeState) -> Style {
            Style::new().magenta()
        }
    }
    
    // Apply the theme globally
    set_theme(MagentaTheme);
  8. Use the input prompt with validation

    main

    The input function allows you to capture a single line of text and parse it into a target type. You can enhance the input experience using several methods:

    • .placeholder(text): Sets a placeholder string.
    • .validate(|input| Result<(), Error>): A closure to validate the user input. Return Ok(()) if valid, or Err("error message") to reject the input.
    • .multiline(): Enables multiline input.
    • .autocomplete(): Enables autocompletion (requires cliclack = "0.5.0" or higher).
    use cliclack::input;
    
    let path: String = input("Where should we create your project?")
        .placeholder("./sparkling-solid")
        .validate(|input: &String| {
            if input.is_empty() {
                Err("Please enter a path.")
            } else if !input.starts_with("./") {
                Err("Please enter a relative path")
            } else {
                Ok(())
            }
        })
        .interact()?;
  9. Initialize and end a prompt session with intro and outro

    main

    Use intro to print a starting message for your CLI session and outro (or outro_cancel) to print a concluding message. These functions manage the visual lifecycle of your prompt session.

    use cliclack::{intro, outro};
    
    intro("create-my-app")?;
    // Do stuff
    outro("You're all set!")?;