# Getting Started with climenu

    library(climenu)

## Introduction

`climenu` provides interactive command-line menus, inspired by popular CLI tools like inquirer.js, Python’s pick, and Go’s survey. It offers an intuitive interface for users to make selections directly in the R console, making your scripts and packages more interactive and user-friendly.

### Key Features

- Single selection menus - Choose one item from a list
- Multiple selection menus - Select multiple items with checkboxes
- Keyboard navigation - Arrow keys or vi-style (j/k) navigation
- Labels vs values - Named choices display the names and return the values
- Dim descriptions - Optional aligned description column per choice
- Pre-selection support - Start with items already selected
- Flexible returns - Get back values or indices
- Graceful fallback - Works in non-interactive environments

## Installation

    # Install from CRAN
    install.packages("climenu")

## Basic Usage

### Single Selection Menu

The simplest way to create a menu is with the `menu()` function:

    # Basic single selection
    colors <- c("Red", "Green", "Blue", "Yellow", "Purple")
    choice <- menu(colors, prompt = "Choose your favorite color:")

    # The user navigates with arrow keys (↑/↓) or vi keys (j/k)
    # Press Enter to select
    # Returns: "Blue" (for example)

You can also use the `select()` function directly:

    choice <- select(
      choices = c("Yes", "No", "Maybe"),
      prompt = "Do you agree?"
    )

### Multiple Selection Menu

For selecting multiple items, use `type = "checkbox"` or call `checkbox()` directly:

    # Using menu() with checkbox type
    toppings <- c("Pepperoni", "Mushrooms", "Olives", "Onions", "Extra Cheese")
    selected <- menu(
      toppings,
      type = "checkbox",
      prompt = "Select pizza toppings:"
    )

    # Or use checkbox() directly
    selected <- checkbox(
      choices = toppings,
      prompt = "Select pizza toppings:"
    )

    # User navigates with arrow keys, toggles with Space, confirms with Enter
    # Returns: c("Pepperoni", "Extra Cheese", "Mushrooms")

## Advanced Features

### Pre-selection

You can pre-select items by value or by index:

    # Pre-select by value
    selected <- checkbox(
      choices = c("Option A", "Option B", "Option C", "Option D"),
      selected = c("Option A", "Option C"),
      prompt = "Modify your selection:"
    )
    # Options A and C will be checked initially

    # Pre-select by index
    selected <- checkbox(
      choices = c("Option A", "Option B", "Option C", "Option D"),
      selected = c(1, 3), # First and third items
      prompt = "Modify your selection:"
    )

For single-selection menus, pre-selection sets the cursor position:

    # Start with cursor on the second option
    choice <- select(
      choices = c("Small", "Medium", "Large"),
      selected = 2, # or "Medium"
      prompt = "Select size:"
    )

### Returning Indices Instead of Values

Sometimes you need the position of the selected item rather than its value:

    # Single selection - returns integer
    index <- select(
      choices = c("First", "Second", "Third"),
      return_index = TRUE
    )
    # Returns: 2 (if user selected "Second")

    # Multiple selection - returns integer vector
    indices <- checkbox(
      choices = c("Alpha", "Beta", "Gamma", "Delta"),
      return_index = TRUE
    )
    # Returns: c(1, 3, 4) (if user selected Alpha, Gamma, Delta)

This is particularly useful when you need to map selections back to data structures or when the choice labels differ from the underlying values you want to work with.

### Select All / Deselect All

For checkbox menus with many options, you can add a “Select all” toggle at the top of the list:

    # Enable the "Select all" option
    methods <- checkbox(
      choices = c("method_a", "method_b", "method_c", "method_d"),
      allow_select_all = TRUE,
      prompt = "Select methods to run:"
    )
    # The first item in the menu will be "Select all" / "Deselect all"
    # It toggles all items at once and is never included in the result

This is handy when users are likely to want most or all items and only need to deselect a few.

### Labels, Values, and Descriptions

Named choices separate what the user sees from what your code receives: the names are the labels shown in the menu, and the (unnamed) values are what gets returned. This removes the need to map a returned label back to an action key.

    action <- select(
      c("Run methods" = "run", "Adjust options" = "options", "Quit" = "quit"),
      prompt = "What next?"
    )
    # The menu displays "Run methods", "Adjust options", "Quit"
    # Returns: "run", "options", or "quit"

Each choice can also carry a description, rendered dim after the label as an aligned second column. Descriptions are display-only: they are never echoed after the selection and never returned.

    screen <- select(
      c("Overview", "Columns", "Studies"),
      descriptions = c(
        "size and missingness summary",
        "role and type per column",
        "per-study estimate counts"
      ),
      prompt = "Preview data"
    )

The confirmation line after a completed selection can be suppressed with `echo = FALSE` when the calling code prints its own summary.

## Keyboard Controls

Key | Action
--- | ---
↑ or k | Move cursor up
↓ or j | Move cursor down
Space | Toggle selection (checkbox only)
Enter | Confirm selection
Esc or q | Cancel (returns NULL)

On terminals that don’t support single-key input (e.g. RStudio or RGui on Windows), `climenu` falls back to a numbered-prompt mode — type the number of your choice (or comma-separated numbers for checkboxes) and press Enter.

## Practical Examples

### Configuration Wizard

    # Ask user to configure application settings
    cat("\n=== Application Configuration ===\n")

    # Choose environment
    env <- select(
      choices = c("Development", "Staging", "Production"),
      prompt = "Select environment:"
    )

    # Enable features
    features <- checkbox(
      choices = c("Logging", "Caching", "Analytics", "Debug Mode"),
      selected = c("Logging", "Caching"), # Sensible defaults
      prompt = "Enable features:"
    )

    # Choose log level
    log_level <- select(
      choices = c("ERROR", "WARN", "INFO", "DEBUG", "TRACE"),
      selected = "INFO",
      prompt = "Select log level:"
    )

    # Build configuration
    config <- list(
      environment = env,
      features = features,
      log_level = log_level
    )

    cat("\nConfiguration complete!\n")
    print(config)

### Data Processing Pipeline

    # Select datasets to process
    available_datasets <- c(
      "sales_2023.csv",
      "sales_2024.csv",
      "customers.csv",
      "products.csv",
      "inventory.csv"
    )

    datasets <- checkbox(
      choices = available_datasets,
      prompt = "Select datasets to process:"
    )

    if (is.null(datasets)) {
      cat("Processing cancelled.\n")
    } else {
      # Choose processing method
      method <- select(
        choices = c("Fast (approximate)", "Standard", "Thorough (slow)"),
        prompt = "Select processing method:"
      )

      cat("\nProcessing", length(datasets), "datasets using", method, "method...\n")
      # ... processing logic here ...
    }

### File Selection with Indices

    # Get list of files
    files <- list.files(pattern = "\\.csv$")

    if (length(files) == 0) {
      cat("No CSV files found.\n")
    } else {
      # Let user select files, get indices
      indices <- checkbox(
        choices = files,
        return_index = TRUE,
        prompt = "Select files to delete:"
      )

      if (!is.null(indices) && length(indices) > 0) {
        files_to_delete <- files[indices]

        # Confirm deletion
        confirm <- select(
          choices = c("Yes, delete them", "No, cancel"),
          prompt = paste("Delete", length(files_to_delete), "file(s)?")
        )

        if (confirm == "Yes, delete them") {
          file.remove(files_to_delete)
          cat("Deleted", length(files_to_delete), "file(s).\n")
        }
      }
    }

## Handling User Cancellation

When a user cancels a selection (by pressing Esc or q), the menu functions return `NULL`:

    choice <- select(c("Continue", "Skip", "Abort"))

    if (is.null(choice)) {
      cat("User cancelled the operation.\n")
      # Handle cancellation appropriately
      stop("Operation cancelled by user")
    }

    # Safe to proceed with choice
    cat("User selected:", choice, "\n")

Always check for `NULL` returns when user input is critical to your workflow.

## Non-Interactive Environments

When `climenu` detects it’s not running in an interactive session (e.g., in Rscript, automated testing, or R CMD check), it:

- Issues a warning
- Returns sensible defaults:
  - `select()`: returns the first choice (or pre-selected item if specified)
  - `checkbox()`: returns pre-selected items (or empty vector if none)
- Does not block execution

This ensures your code can run in both interactive and automated contexts.

    # This will work in both interactive and non-interactive modes
    choice <- select(
      choices = c("Option 1", "Option 2", "Option 3"),
      selected = 1 # Fallback for non-interactive
    )

    # In non-interactive mode: issues warning and returns "Option 1"
    # In interactive mode: shows menu and waits for user input

## Integration with Packages

If you’re building an R package and want to use `climenu`:

### DESCRIPTION File

Add `climenu` to your Imports:

    Imports:
        climenu

### Using in Package Functions

    #' Interactive configuration function
    #' @export
    configure_analysis <- function() {
      # Check if interactive
      if (!interactive()) {
        cli::cli_inform("Using default configuration (non-interactive mode)")
        return(get_default_config())
      }

      # Show interactive menu
      method <- climenu::select(
        choices = c("Linear Model", "Random Forest", "Neural Network"),
        prompt = "Select analysis method:"
      )

      if (is.null(method)) {
        cli::cli_abort("Configuration cancelled by user")
      }

      # ... rest of configuration logic ...

      return(config)
    }

## Tips and Best Practices

1. Keep choices concise - Long choice text may not display well in narrow terminals

2. Provide clear prompts - Tell users what they’re selecting and what will happen

3. Use pre-selection wisely - Pre-select sensible defaults to improve UX

4. Handle cancellation - Always check for `NULL` returns

5. Test non-interactive behavior - Ensure your code works in automated contexts

6. Consider sort order - Present choices in a logical order (alphabetical, by frequency, by importance)

## API Reference

### `menu(choices, prompt, type, selected, return_index)`

Main entry point for creating menus.

Arguments:

- `choices`: Character vector of options to display
- `prompt`: Message to display (default: “Select an item:”)
- `type`: `"select"` for single selection, `"checkbox"` for multiple (default: `"select"`)
- `selected`: Pre-selected items as indices or values (optional)
- `return_index`: Logical; return indices instead of values (default: `FALSE`)

Returns: Selected value(s) as character vector, or indices as integer vector, or `NULL` if cancelled

### `select(choices, prompt, selected, return_index, max_visible)`

Single selection menu. Same parameters as `menu()` (without `type`), plus:

- `max_visible`: Maximum items to display at once (default: `10`). Set to `NULL` to show all.

### `checkbox(choices, prompt, selected, return_index, max_visible, allow_select_all)`

Multiple selection menu. Same parameters as `menu()` (without `type`), plus:

- `max_visible`: Maximum items to display at once (default: `10`). Set to `NULL` to show all.
- `allow_select_all`: Add a “Select all” / “Deselect all” toggle at the top (default: `FALSE`).

## Troubleshooting

### Arrow keys not working

Live menus require a real terminal (e.g. macOS Terminal, iTerm2, Linux terminals, Windows Terminal, Rterm) with both single-key input and ANSI escape support. In RStudio or RGui on Windows, `keypress::keypress()` is not supported, and some consoles cannot process ANSI escapes — `climenu` detects both cases automatically and switches to the numbered-prompt fallback described above. Run from a supported terminal if you want live arrow-key navigation.

### Menu doesn’t display properly

- Ensure your terminal supports ANSI escape codes
- Check terminal width - very narrow terminals may cause display issues
- Try running in RStudio console or a standard terminal

### Returns NULL unexpectedly

- Check if running in non-interactive mode (`interactive()` returns `FALSE`)
- Verify user didn’t press Esc/q to cancel
- Check for empty choice vectors

## Further Reading

- GitHub Repository
- Report Issues
- Contributing Guidelines

## Conclusion

`climenu` makes it easy to add interactive menus to your R scripts and packages. Whether you’re building a data analysis wizard, configuration tool, or just need better user input, `climenu` provides an intuitive and robust solution.

Happy menu-making!