---
title: "Getting Started with climenu"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting Started with climenu}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE # Set to FALSE since interactive examples can't run in vignettes
)
```

```{r setup}
library(climenu)
```

## Introduction

`climenu` provides interactive command-line menus, inspired by popular CLI tools like [inquirer.js](https://github.com/SBoudrias/Inquirer.js), Python's [pick](https://github.com/aisk/pick), and Go's [survey](https://github.com/AlecAivazis/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

```{r install, eval=FALSE}
# Install from CRAN
install.packages("climenu")
```

## Basic Usage

### Single Selection Menu

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

```{r single-basic}
# 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:

```{r select-direct}
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:

```{r checkbox-basic}
# 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:

```{r preselect-value}
# 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
```

```{r preselect-index}
# 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:

```{r preselect-single}
# 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:

```{r return-index}
# 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:

```{r select-all}
# 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.

```{r named-choices}
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.

```{r descriptions}
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

```{r config-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

```{r data-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

```{r file-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`:

```{r handle-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.

```{r non-interactive}
# 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

```{r package-example}
#' 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](https://github.com/PetrCala/climenu)
- [Report Issues](https://github.com/PetrCala/climenu/issues)
- [Contributing Guidelines](https://github.com/PetrCala/climenu/blob/master/CONTRIBUTING.md)

## 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!
