25 releases (8 breaking)

new 0.9.0 Mar 31, 2025
0.7.0 Feb 19, 2025
0.6.0 Dec 15, 2024
0.5.1 Nov 30, 2024
0.1.1 Jun 26, 2022

#57 in Command-line interface

Download history 1084/week @ 2024-12-11 744/week @ 2024-12-18 320/week @ 2024-12-25 971/week @ 2025-01-01 764/week @ 2025-01-08 619/week @ 2025-01-15 686/week @ 2025-01-22 716/week @ 2025-01-29 783/week @ 2025-02-05 988/week @ 2025-02-12 692/week @ 2025-02-19 160/week @ 2025-02-26 83/week @ 2025-03-05 204/week @ 2025-03-12 213/week @ 2025-03-19 238/week @ 2025-03-26

759 downloads per month
Used in 11 crates

MIT license

245KB
5.5K SLoC

promkit

ci docs.rs

A toolkit for building your own interactive prompt in Rust.

Getting Started

Put the package in your Cargo.toml.

[dependencies]
promkit = "0.9.0"

Features

  • Cross-platform support for both UNIX and Windows utilizing crossterm
  • Modularized architecture
    • promkit-core
      • Core functionality for basic terminal operations and pane management
    • promkit-widgets
      • Various UI components (text, listbox, tree, etc.)
    • promkit
      • High-level presets and user interfaces
    • promkit-derive
      • A Derive macro that simplifies interactive form input
  • Rich preset components
    • Readline - Text input with auto-completion
    • Confirm - Yes/no confirmation prompt
    • Password - Password input with masking and validation
    • Form - Manage multiple text input fields
    • Listbox - Single selection interface from a list
    • QuerySelector - Searchable selection interface
    • Checkbox - Multiple selection checkbox interface
    • Tree - Tree display for hierarchical data like file systems
    • JSON - Parse and interactively display JSON data
    • Text - Static text display

Concept

See here.

Projects using promkit

Examples/Demos

promkit provides presets so that users can try prompts immediately without having to build complex components for specific use cases.

Show you commands, code, and actual demo screens for examples that can be executed immediately below.

Readline

Command
cargo run --bin readline --manifest-path examples/readline/Cargo.toml
Code
use promkit::{preset::readline::Readline, suggest::Suggest, Result};

fn main() -> Result {
    let mut p = Readline::default()
        .title("Hi!")
        .enable_suggest(Suggest::from_iter([
            "apple",
            "applet",
            "application",
            "banana",
        ]))
        .validator(
            |text| text.len() > 10,
            |text| format!("Length must be over 10 but got {}", text.len()),
        )
        .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Confirm

Command
cargo run --manifest-path examples/confirm/Cargo.toml
Code
use promkit::{preset::confirm::Confirm, Result};

fn main() -> Result {
    let mut p = Confirm::new("Do you have a pet?").prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Password

Command
cargo run --manifest-path examples/password/Cargo.toml
Code
use promkit::{preset::password::Password, Result};

fn main() -> Result {
    let mut p = Password::default()
        .title("Put your password")
        .validator(
            |text| 4 < text.len() && text.len() < 10,
            |text| format!("Length must be over 4 and within 10 but got {}", text.len()),
        )
        .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Form

Command
cargo run --manifest-path examples/form/Cargo.toml
Code
use promkit::{crossterm::style::Color, preset::form::Form, style::StyleBuilder, text_editor};

fn main() -> anyhow::Result<()> {
    let mut p = Form::new([
        text_editor::State {
            texteditor: Default::default(),
            history: Default::default(),
            prefix: String::from("❯❯ "),
            mask: Default::default(),
            prefix_style: StyleBuilder::new().fgc(Color::DarkRed).build(),
            active_char_style: StyleBuilder::new().bgc(Color::DarkCyan).build(),
            inactive_char_style: StyleBuilder::new().build(),
            edit_mode: Default::default(),
            word_break_chars: Default::default(),
            lines: Default::default(),
        },
        text_editor::State {
            texteditor: Default::default(),
            history: Default::default(),
            prefix: String::from("❯❯ "),
            mask: Default::default(),
            prefix_style: StyleBuilder::new().fgc(Color::DarkGreen).build(),
            active_char_style: StyleBuilder::new().bgc(Color::DarkCyan).build(),
            inactive_char_style: StyleBuilder::new().build(),
            edit_mode: Default::default(),
            word_break_chars: Default::default(),
            lines: Default::default(),
        },
        text_editor::State {
            texteditor: Default::default(),
            history: Default::default(),
            prefix: String::from("❯❯ "),
            mask: Default::default(),
            prefix_style: StyleBuilder::new().fgc(Color::DarkBlue).build(),
            active_char_style: StyleBuilder::new().bgc(Color::DarkCyan).build(),
            inactive_char_style: StyleBuilder::new().build(),
            edit_mode: Default::default(),
            word_break_chars: Default::default(),
            lines: Default::default(),
        },
    ])
    .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Listbox

Command
cargo run --manifest-path examples/listbox/Cargo.toml
Code
use promkit::{preset::listbox::Listbox, Result};

fn main() -> Result {
    let mut p = Listbox::new(0..100)
        .title("What number do you like?")
        .listbox_lines(5)
        .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

QuerySelector

Command
cargo run --manifest-path examples/query_selector/Cargo.toml
Code
use promkit::{preset::query_selector::QuerySelector, Result};

fn main() -> Result {
    let mut p = QuerySelector::new(0..100, |text, items| -> Vec<String> {
        text.parse::<usize>()
            .map(|query| {
                items
                    .iter()
                    .filter(|num| query <= num.parse::<usize>().unwrap_or_default())
                    .map(|num| num.to_string())
                    .collect::<Vec<String>>()
            })
            .unwrap_or(items.clone())
    })
    .title("What number do you like?")
    .listbox_lines(5)
    .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Checkbox

Command
cargo run --manifest-path examples/checkbox/Cargo.toml
Code
use promkit::{preset::checkbox::Checkbox, Result};

fn main() -> Result {
    let mut p = Checkbox::new(vec![
        "Apple",
        "Banana",
        "Orange",
        "Mango",
        "Strawberry",
        "Pineapple",
        "Grape",
        "Watermelon",
        "Kiwi",
        "Pear",
    ])
    .title("What are your favorite fruits?")
    .checkbox_lines(5)
    .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

Tree

Command
cargo run --manifest-path examples/tree/Cargo.toml
Code
use promkit::{preset::tree::Tree, tree::Node, Result};

fn main() -> Result {
    let mut p = Tree::new(Node::try_from(&std::env::current_dir()?.join("src"))?)
        .title("Select a directory or file")
        .tree_lines(10)
        .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

JSON

Command
cargo run --manifest-path examples/json/Cargo.toml
Code
use promkit::{json::JsonStream, preset::json::Json, serde_json::Deserializer, Result};

fn main() -> Result {
    let stream = JsonStream::new(
        Deserializer::from_str(
            r#"{
              "number": 9,
              "map": {
                "entry1": "first",
                "entry2": "second"
              },
              "list": [
                "abc",
                "def"
              ]
            }"#,
        )
        .into_iter::<serde_json::Value>()
        .filter_map(serde_json::Result::ok),
        None,
    );

    let mut p = Json::new(stream)
        .title("JSON viewer")
        .json_lines(5)
        .prompt()?;
    println!("result: {:?}", p.run()?);
    Ok(())
}

License

This project is licensed under the MIT License. See the LICENSE file for details.

Stargazers over time

Stargazers over time

Dependencies

~5–15MB
~204K SLoC