/*
 *   Copyright (c) 2023 R3BL LLC
 *   All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

//! # Why R3BL?
//!
//! <img src="https://raw.githubusercontent.com/r3bl-org/r3bl-open-core/main/r3bl-term.svg?raw=true" height="256px">
//!
//! <!-- R3BL TUI library & suite of apps focused on developer productivity -->
//!
//! <span style="color:#FD2F53">R</span><span style="color:#FC2C57">3</span><span style="color:#FB295B">B</span><span style="color:#FA265F">L</span><span style="color:#F92363">
//! </span><span style="color:#F82067">T</span><span style="color:#F61D6B">U</span><span style="color:#F51A6F">I</span><span style="color:#F31874">
//! </span><span style="color:#F11678">l</span><span style="color:#EF137C">i</span><span style="color:#ED1180">b</span><span style="color:#EB0F84">r</span><span style="color:#E90D89">a</span><span style="color:#E60B8D">r</span><span style="color:#E40A91">y</span><span style="color:#E10895">
//! </span><span style="color:#DE0799">&amp;</span><span style="color:#DB069E">
//! </span><span style="color:#D804A2">s</span><span style="color:#D503A6">u</span><span style="color:#D203AA">i</span><span style="color:#CF02AE">t</span><span style="color:#CB01B2">e</span><span style="color:#C801B6">
//! </span><span style="color:#C501B9">o</span><span style="color:#C101BD">f</span><span style="color:#BD01C1">
//! </span><span style="color:#BA01C4">a</span><span style="color:#B601C8">p</span><span style="color:#B201CB">p</span><span style="color:#AE02CF">s</span><span style="color:#AA03D2">
//! </span><span style="color:#A603D5">f</span><span style="color:#A204D8">o</span><span style="color:#9E06DB">c</span><span style="color:#9A07DE">u</span><span style="color:#9608E1">s</span><span style="color:#910AE3">e</span><span style="color:#8D0BE6">d</span><span style="color:#890DE8">
//! </span><span style="color:#850FEB">o</span><span style="color:#8111ED">n</span><span style="color:#7C13EF">
//! </span><span style="color:#7815F1">d</span><span style="color:#7418F3">e</span><span style="color:#701AF5">v</span><span style="color:#6B1DF6">e</span><span style="color:#6720F8">l</span><span style="color:#6322F9">o</span><span style="color:#5F25FA">p</span><span style="color:#5B28FB">e</span><span style="color:#572CFC">r</span><span style="color:#532FFD">
//! </span><span style="color:#4F32FD">p</span><span style="color:#4B36FE">r</span><span style="color:#4739FE">o</span><span style="color:#443DFE">d</span><span style="color:#4040FE">u</span><span style="color:#3C44FE">c</span><span style="color:#3948FE">t</span><span style="color:#354CFE">i</span><span style="color:#324FFD">v</span><span style="color:#2E53FD">i</span><span style="color:#2B57FC">t</span><span style="color:#285BFB">y</span>
//!
//! We are working on building command line apps in Rust which have rich text user interfaces (TUI).
//! We want to lean into the terminal as a place of productivity, and build all kinds of awesome
//! apps for it.
//!
//! 1. 🔮 Instead of just building one app, we are building a library to enable any kind of rich TUI
//!    development w/ a twist: taking concepts that work really well for the frontend mobile and web
//!    development world and re-imagining them for TUI & Rust.
//!
//!    - Taking inspiration from things like [React](https://react.dev/),
//!      [SolidJS](https://www.solidjs.com/),
//!      [Elm](https://guide.elm-lang.org/architecture/),
//!      [iced-rs](https://docs.rs/iced/latest/iced/), [Jetpack
//!      Compose](https://developer.android.com/compose),
//!      [JSX](https://ui.dev/imperative-vs-declarative-programming),
//!      [CSS](https://www.w3.org/TR/CSS/#css), but making everything async (so they can
//!      be run in parallel & concurrent via [Tokio](https://crates.io/crates/tokio)).
//!    - Even the thread running the main event loop doesn't block since it is async.
//!    - Using proc macros to create DSLs to implement something inspired by
//!      [CSS](https://www.w3.org/TR/CSS/#css) &
//!      [JSX](https://ui.dev/imperative-vs-declarative-programming).
//!
//! 2. 🌎 We are building apps to enhance developer productivity & workflows.
//!
//!    - The idea here is not to rebuild `tmux` in Rust (separate processes mux'd onto a
//!      single terminal window). Rather it is to build a set of integrated "apps" (or
//!      "tasks") that run in the same process that renders to one terminal window.
//!    - Inside of this terminal window, we can implement things like "app" switching,
//!      routing, tiling layout, stacking layout, etc. so that we can manage a lot of TUI
//!      apps (which are tightly integrated) that are running in the same process, in the
//!      same window. So you can imagine that all these "app"s have shared application
//!      state. Each "app" may also have its own local application state.
//!    - Here are some examples of the types of "app"s we plan to build (for which this
//!      infrastructure acts as the open source engine):
//!      1. Multi user text editors w/ syntax highlighting.
//!      2. Integrations w/ github issues.
//!      3. Integrations w/ calendar, email, contacts APIs.
//!
//! All the crates in the `r3bl-open-core`
//! [repo](https://github.com/r3bl-org/r3bl-open-core/) provide lots of useful
//! functionality to help you build TUI (text user interface) apps, along w/ general
//! niceties & ergonomics that all Rustaceans 🦀 can enjoy 🎉.
//!
//! # Table of contents
//!
//! <!-- TOC -->
//!
//! - [Introduction](#introduction)
//! - [Changelog](#changelog)
//! - [Learn how these crates are built, provide feedback](#learn-how-these-crates-are-built-provide-feedback)
//! - [How to use it as a library?](#how-to-use-it-as-a-library)
//! - [APIs](#apis)
//!     - [select_from_list](#select_from_list)
//!     - [select_from_list_with_multi_line_header](#select_from_list_with_multi_line_header)
//! - [How to use it as a binary?](#how-to-use-it-as-a-binary)
//!     - [Interactive user experience](#interactive-user-experience)
//!     - [Paths](#paths)
//! - [Style the components](#style-the-components)
//!     - [Choose one of the 3 built-in styles](#choose-one-of-the-3-built-in-styles)
//!     - [Create your style](#create-your-style)
//! - [Build, run, test tasks](#build-run-test-tasks)
//!     - [Prerequisites](#prerequisites)
//!     - [Nushell scripts to build, run, test, etc.](#nu-shell-scripts-to-build-run-test-etc)
//! - [References](#references)
//!
//! <!-- /TOC -->
//!
//! # Introduction
//!
//! `r3bl_tuify` is a Rust crate that allows you to add simple interactivity to your CLI app.
//!
//! `r3bl_tuify` crate can be used in two ways:
//!
//! 1. **As a library**. This is useful if you want to add simple interactivity to your CLI
//!    app written in Rust. You can see an example of this in the `examples` folder in the
//!    `main_interactive.rs` file. You can run it using `cargo run --example
//!    main_interactive`.
//!
//! 1. **As a binary**. This is useful if you want to use this crate as a command line tool.
//!    The binary target is called `rt`.
//!
//! # Changelog
//!
//! Please check out the
//! [changelog](https://github.com/r3bl-org/r3bl-open-core/blob/main/CHANGELOG.md#r3bl_tuify) to
//! see how the library has evolved over time.
//!
//! # Learn how these crates are built, provide feedback
//!
//! To learn how we built this crate, please take a look at the following resources.
//! - If you like consuming video content, here's our [YT channel](https://www.youtube.com/@developerlifecom). Please consider [subscribing](https://www.youtube.com/channel/CHANNEL_ID?sub_confirmation=1).
//! - If you like consuming written content, here's our developer [site](https://developerlife.com/). Please consider subscribing to our [newsletter](https://developerlife.com/subscribe.html).
//! - If you have questions, please join our [discord server](https://discord.gg/8M2ePAevaM).
//!
//! # How to use it as a library?
//!
//! Here's a demo of the library target of this crate in action.
//!
//! <video width="100%" controls>
//!   <source src="https://github.com/r3bl-org/r3bl-open-core/assets/22040032/46850043-4973-49fa-9824-58f32f21e96e" type="video/mp4"/>
//! </video>
//!
//! To install the crate as a library, add the following to your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! r3bl_tuify = "*" # Get the latest version at the time you get this.
//! r3bl_core = "*" # Get the latest version at the time you get this.
//! ```
//!
//! The following example illustrates how you can use this as a library. The function that
//! does the work of rendering the UI is called
//! [`select_from_list`]. It takes a list of items and returns
//! the selected item or items (depending on the selection mode). If the user does not
//! select anything, it returns `None`. The function also takes the maximum height and
//! width of the display, and the selection mode (single select or multiple select).
//!
//! It works on macOS, Linux, and Windows. And is aware
//! of the terminal color output limitations of each. For eg, it uses Windows API on Windows for
//! keyboard input. And on macOS Terminal.app it restricts color output to a 256 color palette.
//!
//! ```rust
//! use r3bl_core::*;
//! use r3bl_tuify::*;
//! use std::io::Result;
//!
//! fn main() -> Result<()> {
//!     // Get display size.
//!     let max_width_col_count: usize = get_size().map(|it| it.col_count).unwrap_or(ch!(80)).into();
//!     let max_height_row_count: usize = 5;
//!
//!     let user_input = select_from_list(
//!         "Select an item".to_string(),
//!         [
//!             "item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7", "item 8",
//!             "item 9", "item 10",
//!         ]
//!         .iter()
//!         .map(|it| it.to_string())
//!         .collect(),
//!         max_height_row_count,
//!         max_width_col_count,
//!         SelectionMode::Single,
//!         StyleSheet::default(),
//!     );
//!
//!     match &user_input {
//!         Some(it) => {
//!             println!("User selected: {:?}", it);
//!         }
//!         None => println!("User did not select anything"),
//!     }
//!
//!     Ok(())
//! }
//! ```
//!
//! # APIs
//!
//! We provide 2 APIs:
//!
//! - [`select_from_list`]: Use this API if you want to display a list of items with a single line header.
//! - [`select_from_list_with_multi_line_header`]: Use this API if you want to display a list of items
//!   with a multi line header.
//!
//! ## select_from_list
//!
//! Use this API if you want to display a list of items with a single line header.
//!
//! ![image](https://github.com/r3bl-org/r3bl-open-core/assets/22040032/0ae722bb-8cd1-47b1-a293-1a96e84d24d0)
//!
//! [select_from_list] code example:
//!
//! ```rust
//! use r3bl_core::*;
//! use r3bl_tuify::*;
//! use std::io::Result;
//!
//! fn main() -> Result<()> {
//!     // Get display size.
//!     let max_height_row_count: usize = 5;
//!     let user_input = select_from_list(
//!         "Select an item".to_string(),
//!         [
//!             "item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7", "item 8",
//!             "item 9", "item 10",
//!         ]
//!         .iter()
//!         .map(|it| it.to_string())
//!         .collect(),
//!         max_height_row_count,
//!         0,
//!         SelectionMode::Single,
//!         StyleSheet::default(),
//!     );
//!
//!     match &user_input {
//!         Some(it) => {
//!             println!("User selected: {:?}", it);
//!         }
//!         None => println!("User did not select anything"),
//!     }
//!
//!     Ok(())
//! }
//! ```
//!
//! ## select_from_list_with_multi_line_header
//!
//! Use the `select_from_list_with_multi_line_header` API if you want to display a list of items with a
//! multi line header. The first 5 lines are all part of the multi line header.
//!
//! ![image](https://github.com/r3bl-org/r3bl-open-core/assets/22040032/2f82a42c-f720-4bcb-925d-0d5ad0b0a3c9)
//!
//! [select_from_list_with_multi_line_header] code example:
//!
//! ```rust
//! use std::{io::Result, vec};
//!
//! use r3bl_ansi_color::{AnsiStyledText, Color, Style as RStyle};
//! use r3bl_core::*;
//! use r3bl_tuify::{
//!     components::style::StyleSheet,
//!     select_from_list_with_multi_line_header,
//!     SelectionMode,
//! };
//!
//! fn multi_select_instructions() -> Vec<Vec<AnsiStyledText<'static>>> {
//!     let up_and_down = AnsiStyledText {
//!         text: " Up or down:",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(9, 238, 211)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!     let navigate = AnsiStyledText {
//!         text: "     navigate",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(94, 103, 111)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!
//!     let line_1 = vec![up_and_down, navigate];
//!
//!     let space = AnsiStyledText {
//!         text: " Space:",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(255, 216, 9)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!     let select = AnsiStyledText {
//!         text: "          select or deselect item",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(94, 103, 111)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!
//!     let line_2 = vec![space, select];
//!
//!     let esc = AnsiStyledText {
//!         text: " Esc or Ctrl+C:",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(255, 132, 18)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!     let exit = AnsiStyledText {
//!         text: "  exit program",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(94, 103, 111)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!
//!     let line_3 = vec![esc, exit];
//!     let return_key = AnsiStyledText {
//!         text: " Return:",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(234, 0, 196)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!     let confirm = AnsiStyledText {
//!         text: "         confirm selection",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(94, 103, 111)),
//!             RStyle::Background(Color::Rgb(14, 17, 23)),
//!         ],
//!     };
//!     let line_4 = vec![return_key, confirm];
//!     vec![line_1, line_2, line_3, line_4]
//! }
//!
//! fn main() -> Result<()> {
//!    let header = AnsiStyledText {
//!         text: " Please select one or more items. This is a really long heading that just keeps going and if your terminal viewport is small enough, this heading will be clipped",
//!         style: &[
//!             RStyle::Foreground(Color::Rgb(171, 204, 242)),
//!             RStyle::Background(Color::Rgb(31, 36, 46)),
//!         ],
//!     };
//!
//!     let mut instructions_and_header: Vec<Vec<AnsiStyledText>> = multi_select_instructions();
//!     instructions_and_header.push(vec![header]);
//!
//!     let user_input = select_from_list_with_multi_line_header(
//!         instructions_and_header,
//!         [
//!             "item 1 of 13",
//!             "item 2 of 13",
//!             "item 3 of 13",
//!             "item 4 of 13",
//!             "item 5 of 13",
//!             "item 6 of 13",
//!             "item 7 of 13",
//!             "item 8 of 13",
//!             "item 9 of 13",
//!             "item 10 of 13",
//!             "item 11 of 13",
//!             "item 12 of 13",
//!             "item 13 of 13",
//!         ]
//!         .iter()
//!         .map(|it| it.to_string())
//!         .collect(),
//!         Some(6),
//!         None,
//!         SelectionMode::Multiple,
//!         StyleSheet::default(),
//!     );
//!     match &user_input {
//!         Some(it) => {
//!             println!("User selected: {:?}", it);
//!         }
//!         None => println!("User did not select anything"),
//!     }
//!    Ok(())
//! }
//! ```
//!
//! # How to use it as a binary?
//!
//! Here's a demo of the binary target of this crate in action.
//!
//! <video width="100%" controls>
//!   <source src="https://github-production-user-asset-6210df.s3.amazonaws.com/2966499/267427392-2b42db72-cd62-4ea2-80ae-ccc01008190c.mp4" type="video/mp4"/>
//! </video>
//!
//! You can install the binary using `cargo install r3bl_tuify` (from crates.io). Or
//! `cargo install --path .` from source. `rt` is a command line tool that allows you to select one of the options from the list that is passed into it
//! via `stdin`. It supports both `stdin` and `stdout` piping.
//!
//! Here are the command line arguments that it accepts:
//! 1. `-s` or `--selection-mode` - Allows you to select the selection mode. There are two
//!    options: `single` and `multiple`.
//! 1. `-c` or `--command-to-run-with-selection` - Allows you to specify the command to
//!    run with the selected item. For example `"echo foo \'%\'"` simply prints each
//!    selected item.
//! 1. `-t` or `--tui-height` - Optionally allows you to set the height of the TUI. The
//!    default is 5.
//!
//! ## Interactive user experience
//!
//! Typically a CLI app is not interactive. You can pass commands, subcommands, options, and
//! arguments to it, but if you get something wrong, then you get an error and have to start
//! all over again. This "conversation" style interface might require a lot of trial and error
//! to get the desired result.
//!
//! The following is an example of using the binary with many subcommands, options, and arguments.
//!
//! ```shell
//! cat TODO.todo | cargo run -- select-from-list \
//!     --selection-mode single \
//!     --command-to-run-with-each-selection "echo %"
//! ```
//!
//! Here's a video of this in action.
//!
//! <!-- tuify-long-command -->
//! <video width="100%" controls>
//!   <source src="https://github.com/r3bl-org/r3bl-open-core/assets/2966499/c9b49bfb-b811-460e-a844-fe260eaa860a" type="video/mp4"/>
//! </video>
//!
//! What does this do?
//!
//! 1. `cat TODO.todo` - prints the contents of the `TODO.todo` file to `stdout`.
//! 1. `|` - pipes the output of the previous command to the next command, which is `rt` (ie,
//!    the binary target of this crate).
//! 1. `cargo run --` - runs the `rt` debug binary in the target folder.
//! 1. `select-from-list` - runs the `rt` binary with the `select-from-list`
//!    subcommand. This subcommand requires 2 arguments: `--selection-mode` and
//!    `--command-to-run-with-each-selection`. Whew! This is getting long!
//! 1. `--selection-mode single` - sets the selection mode to `single`. This means that the
//!     user can only select one item from the list. What list? The list that is piped in from
//!     the previous command (ie, `cat TODO.todo`).
//! 1. `--command-to-run-with-each-selection "echo %"` - sets the command to run with each
//!     selection. In this case, it is `echo %`. The `%` is a placeholder for the selected
//!     item. So if the user selects `item 1`, then the command that will be run is `echo item
//!     1`. The `echo` command simply prints the selected item to `stdout`.
//!
//! Now that is a lot to remember. It is helpful to use `clap` to provide nice command line help but
//! that are still quite a few things that you have to get right for this command to work.
//!
//! It doesn't have to be this way. The binary can be interactive along with
//! the use of `clap` to specify some of the subcommands, and arguments. It doesn't
//! have to be an all or nothing approach. We can have the best of both worlds. The following
//! videos illustrate what happens when:
//!
//! 1. `--selection-mode` and `--command-to-run-with-each-selection` are *not* passed in the
//!    command line.
//!    ```shell
//!    cat TODO.todo | cargo run -- select-from-list
//!    ```
//!
//!    Here are the 3 scenarios that can happen:
//!
//!    - The user first chooses `single` selection mode (using a list selection component),
//!      and then types in `echo %` in the terminal, as the command to run with each
//!      selection. This is an
//!      interactive scenario since the user has to provide 2 pieces of information:  the selection mode, and the command to run with each
//!      selection. They didn't provide this upfront when they ran the command.
//!      <!-- tuify-interactive-happy-path -->
//!      <video width="100%" controls>
//!        <source src="https://github.com/r3bl-org/r3bl-open-core/assets/2966499/51de8867-513b-429f-aff2-63dd25d71c82" type="video/mp4"/>
//!      </video>
//!
//!    - Another scenario is that the user does not provide the required information even when
//!      prompted interactively. In this scenario, the program exits with an error and help
//!      message.
//!
//!      Here they don't provide what `selection-mode` they want. And they don't provide what
//!      `command-to-run-with-each-selection` they want. Without this information the program
//!      can't continue, so it exits and provides some help message.
//!      <!-- tuify-interactive-unhappy-path -->
//!      <video width="100%" controls>
//!        <source src="https://github.com/r3bl-org/r3bl-open-core/assets/2966499/664d0367-90fd-4f0a-ad87-3f4745642ad0" type="video/mp4"/>
//!      </video>
//!
//! 1. `--selection-mode` is *not* passed in the command line. So it only interactively
//!    prompts the user for this piece of information. Similarly, if the user does not provide
//!    this information, the app exits and provides a help message.
//!    ```shell
//!    cat TODO.todo | cargo run -- select-from-list --command-to-run-with-each-selection "echo %"
//!    ```
//!    <!-- tuify-interactive-selection-mode-not-provided -->
//!      <video width="100%" controls>
//!        <source src="https://github.com/r3bl-org/r3bl-open-core/assets/2966499/be65d9b2-575b-47c0-8291-110340bd2fe7" type="video/mp4"/>
//!      </video>
//!
//! 1. `--command-to-run-with-each-selection` is *not* passed in the command line. So it only
//!    interactively prompts the user for this piece of information. Similarly, if the user
//!    does not provide this information, the app exits and provides a help message.
//!    ```shell
//!    cat TODO.todo | cargo run -- select-from-list --selection-mode single
//!    ```
//!    <!-- tuify-interactive-command-to-run-with-selection-not-provided -->
//!      <video width="100%" controls>
//!        <source src="https://github.com/r3bl-org/r3bl-open-core/assets/2966499/d8d7d419-c85e-4c10-bea5-345aa31a92a3" type="video/mp4"/>
//!      </video>
//!
//! ## Paths
//!
//! There are a lot of different execution paths that you can take with this relatively
//! simple program. Here is a list.
//!
//! - Happy paths:
//!   1. `rt` - prints help.
//!   1. `cat Cargo.toml | rt -s single -c "echo foo \'%\'"` - `stdin` is piped
//!      in, and it prints the user selected option to `stdout`.
//!   1. `cat Cargo.toml | rt -s multiple -c "echo foo \'%\'"` - `stdin` is piped
//!      in, and it prints the user selected option to `stdout`.
//!
//! - Unhappy paths (`stdin` is _not_ piped in and, or `stdout` _is_ piped out):
//!   1. `rt -s single` - expects `stdin` to be piped in, and prints help.
//!   1. `rt -s multiple` - expects `stdin` to be piped in, and prints help.
//!   1. `ls -la | rt -s single | xargs -0` - does not expect `stdout` to be piped out,
//!      and prints help.
//!   1. `ls -la | rt -s multiple | xargs -0` - does not expect `stdout` to be piped out,
//!      and prints help.
//!
//! > Due to how unix pipes are implemented, it is not possible to pipe the
//! > `stdout` of this command to anything else. Unix pipes are non-blocking. So there is no
//! > way to stop the pipe "midway". This is why `rt` displays an error when the `stdout` is
//! > piped out. It is not possible to pipe the `stdout` of `rt` to another command. Instead,
//! > the `rt` binary simply takes a command that will run after the user has made their
//! > selection. Using the selected item(s) and applying them to this command.
//!
//! # Style the components
//!
//! ## Choose one of the 3 built-in styles
//!
//! Built-in styles are called `default`, `sea_foam_style`, and `hot_pink_style`. You can find them in the `style.rs` file (tuify/src/components/style.rs).
//!
//! Default style:
//! ![image](https://github.com/r3bl-org/r3bl-open-core/assets/22040032/eaf990a4-1c33-4783-9f39-82af42568183)
//!
//! `sea_foam_style`:
//! ![image](https://github.com/r3bl-org/r3bl-open-core/assets/22040032/fc414f56-2f72-4d3a-86eb-bfd732b66bd1)
//!
//! `hot_pink_style`:
//! ![image](https://github.com/r3bl-org/r3bl-open-core/assets/22040032/06c155f9-11a9-416d-8056-cb4c741ac3d7)
//!
//! To use one of the built-in styles, simply pass it as an argument to the `select_from_list` function.
//!
//! ```rust
//! use r3bl_core::*;
//! use r3bl_tuify::*;
//! use std::io::Result;
//!
//! fn main() -> Result<()> {
//!     // 🎨 Uncomment the lines below to choose the other 2 built-in styles.
//!     // let default_style = StyleSheet::default();
//!     // let hot_pink_style = StyleSheet::hot_pink_style();
//!     let sea_foam_style = StyleSheet::sea_foam_style();
//!
//!     let max_width_col_count: usize = get_size().map(|it| it.col_count).unwrap_or(ch!(80)).into();
//!     let max_height_row_count: usize = 5;
//!
//!     let user_input = select_from_list(
//!         "Select an item".to_string(),
//!         [
//!             "item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7", "item 8",
//!             "item 9", "item 10",
//!         ]
//!         .iter()
//!         .map(|it| it.to_string())
//!         .collect(),
//!         max_height_row_count,
//!         max_width_col_count,
//!         SelectionMode::Single,
//!         sea_foam_style,  // 🖌️ or default_style or hot_pink_style
//!     );
//!
//!     match &user_input {
//!         Some(it) => {
//!             println!("User selected: {:?}", it);
//!         }
//!         None => println!("User did not select anything"),
//!     }
//!
//!     Ok(())
//! }
//! ```
//!
//! ## Create your style
//!
//! To create your style, you need to create a `StyleSheet` struct and pass it as an argument to the `select_from_list` function.
//!
//! ```rust
//! use std::io::Result;
//! use r3bl_ansi_color::{AnsiStyledText, Color};
//! use r3bl_tuify::{components::style::{Style, StyleSheet},
//!                 select_from_list,
//!                 SelectionMode};
//!
//! fn main() -> Result<()> {
//!    // This is how you can define your custom style.
//!    // For each Style struct, you can define different style overrides.
//!    // Please take a look at the Style struct to see what you can override.
//!    let my_custom_style = StyleSheet {
//!       focused_and_selected_style: Style {
//!             fg_color: Color::Rgb(255, 244, 0),
//!             bg_color: Color::Rgb(15, 32, 66),
//!             ..Style::default()
//!       },
//!       focused_style: Style {
//!             fg_color: Color::Rgb(255, 244, 0),
//!             ..Style::default()
//!       },
//!       unselected_style: Style { ..Style::default() },
//!       selected_style: Style {
//!             fg_color: Color::Rgb(203, 170, 250),
//!             bg_color: Color::Rgb(15, 32, 66),
//!             ..Style::default()
//!       },
//!       header_style: Style {
//!             fg_color: Color::Rgb(171, 204, 242),
//!             bg_color: Color::Rgb(31, 36, 46),
//!             ..Style::default()
//!       },
//!    };
//!
//!    // Then pass `my_custom_style` as the last argument to the `select_from_list` function.
//!    let user_input = select_from_list(
//!       "Multiple select".to_string(),
//!       ["item 1 of 3", "item 2 of 3", "item 3 of 3"]
//!          .iter()
//!          .map(|it| it.to_string())
//!          .collect(),
//!       6, // max_height_row_count
//!       80, // max_width_col_count
//!       SelectionMode::Multiple,
//!       my_custom_style,
//!    );
//!
//!    match &user_input {
//!       Some(it) => {
//!          println!("User selected: {:?}", it);
//!       }
//!       None => println!("User did not select anything"),
//!    }
//!    Ok(())
//! }
//! ```
//!
//! # Build, run, test tasks
//!
//! ## Prerequisites
//!
//! 🌠 For these to work you have to install the Rust toolchain, `nu`, `cargo-watch`,
//! `bat`, and `flamegraph` on your system. Here are the instructions:
//!
//! 1. Install the Rust toolchain using `rustup` by following the instructions
//!    [here](https://rustup.rs/).
//! 1. Install `cargo-watch` using `cargo install cargo-watch`.
//! 1. Install `flamegraph` using `cargo install flamegraph`.
//! 1. Install `bat` using `cargo install bat`.
//! 1. Install [`nu`](https://crates.io/crates/nu) shell on your system using `cargo install
//!    nu`. It is available for Linux, macOS, and Windows.
//!
//! ## Nushell scripts to build, run, test, etc.
//!
//! Go to the `tuify` folder and run the commands below. These commands are defined in the `./run` folder.
//!
//! | Command                                | Description                                |
//! | -------------------------------------- | ------------------------------------------ |
//! | `nu run examples`                      | Run examples in the `./examples` folder    |
//! | `nu run piped`                         | Run binary with piped input                |
//! | `nu run build`                         | Build                                      |
//! | `nu run clean`                         | Clean                                      |
//! | `nu run all`                           | All                                        |
//! | `nu run examples-with-flamegraph-profiling` | Run examples with flamegraph profiling |
//! | `nu run test`                          | Run tests                                  |
//! | `nu run clippy`                        | Run clippy                                 |
//! | `nu run docs`                          | Build docs                                 |
//! | `nu run serve-docs`                    | Serve docs over VSCode Remote SSH session. |
//! | `nu run upgrade-deps`                  | Upgrade deps                               |
//! | `nu run rustfmt`                       | Run rustfmt                                |
//!
//! The following commands will watch for changes in the source folder and re-run:
//!
//! | Command                                             | Description                        |
//! | --------------------------------------------------- | ---------------------------------- |
//! | `nu run watch-examples`                             | Watch run examples                 |
//! | `nu run watch-all-tests`                            | Watch all test                     |
//! | `nu run watch-one-test <test_name>`                 | Watch one test                     |
//! | `nu run watch-clippy`                               | Watch clippy                       |
//! | `nu run watch-macro-expansion-one-test <test_name>` | Watch macro expansion for one test |
//!
//! There's also a `run` script at the **top level folder** of the repo. It is intended to
//! be used in a CI/CD environment w/ all the required arguments supplied or in
//! interactive mode, where the user will be prompted for input.
//!
//! | Command                       | Description                        |
//! | ----------------------------- | ---------------------------------- |
//! | `nu run all`                  | Run all the tests, linting, formatting, etc. in one go. Used in CI/CD |
//! | `nu run build-full`           | This will build all the crates in the Rust workspace. It will install all the required pre-requisite tools needed to work with this crate (what `install-cargo-tools` does) and clear the cargo cache, cleaning, and then do a really clean build. |
//! | `nu run install-cargo-tools`  | This will install all the required pre-requisite tools needed to work with this crate (things like `cargo-deny`,and `flamegraph` will all be installed in one go) |
//! | `nu run check-licenses`       | Use `cargo-deny` to audit all licenses used in the Rust workspace |
//!
//! # References
//!
//! CLI UX guidelines:
//!
//! - [Handling Arguments](https://rust-cli-recommendations.sunshowers.io/handling-arguments.html)
//! - [Configuration](https://rust-cli-recommendations.sunshowers.io/configuration.html)
//! - [Hierarchical Config](https://rust-cli-recommendations.sunshowers.io/hierarchical-config.html)
//! - [Hierarchical Config](https://rust-cli-recommendations.sunshowers.io/hierarchical-config.html)
//! - [Clap Derive Overview](https://docs.rs/clap/latest/clap/_derive/#overview)
//! - [Command Line Interface Guidelines](https://clig.dev/#foreword)
//!
//! ANSI escape codes:
//!
//! - [ANSI Escape Codes Notes](https://notes.burke.libbey.me/ansi-escape-codes/)
//! - [ANSI Escape Code - Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code)
//! - [ASCII Table](https://www.asciitable.com/)
//! - [Xterm 256 Color Chart](https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg)
//! - [256 Colors Cheat Sheet](https://www.ditig.com/256-colors-cheat-sheet)
//! - [List of ANSI Color Escape Sequences - Stack Overflow](https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences)
//! - [Color Metric](https://www.compuphase.com/cmetric.htm)

// https://github.com/rust-lang/rust-clippy
// https://rust-lang.github.io/rust-clippy/master/index.html
#![warn(clippy::all)]
#![warn(clippy::unwrap_in_result)]
#![warn(rust_2018_idioms)]

pub mod components;
pub mod constants;
pub mod event_loop;
pub mod function_component;
pub mod keypress;
pub mod public_api;
pub mod scroll;
pub mod state;
pub mod test_utils;

pub use components::*;
pub use constants::*;
pub use event_loop::*;
pub use function_component::*;
pub use keypress::*;
pub use public_api::*;
pub use scroll::*;
pub use state::*;
pub use test_utils::*;

/// Enable file logging. You can use `tail -f log.txt` to watch the logs.
pub const DEVELOPMENT_MODE: bool = false;
