TTY::MenuTree

Interactive, hierarchical command-line menus built on tty-prompt.

You model each menu entry as a small action object. TTY::MenuTree::Menu runs them in a loop: leaf actions are invoked, groups open a submenu (with automatic "Back" navigation), and an exit action ends the loop. Banners and goodbyes are injectable hooks, so the menu itself stays free of application-specific content.

Installation

Add it to your Gemfile:

gem "tty-menu-tree"

Then run:

bundle install

Or install it directly:

gem install tty-menu-tree

Usage

An action is any object that responds to name, call, submenu, and exit?. Subclass TTY::MenuTree::Action to get sensible defaults.

require "tty/menu_tree"

class SayHello < TTY::MenuTree::Action
  def name = "Say hello"
  def call = puts("Hello! 👋")
end

class ShowTime < TTY::MenuTree::Action
  def name = "Show the time"
  def call = puts(Time.now)
end

actions = [
  TTY::MenuTree::Group.new(name: "Greetings", actions: [SayHello.new]),
  ShowTime.new,
  TTY::MenuTree::ExitAction.new,
]

TTY::MenuTree::Menu.new(
  actions: actions,
  title: "What would you like to do?",
  on_start: -> { puts "Welcome!" },
  on_exit: -> { puts "Goodbye!" },
).run

The action protocol

Method Returns Meaning
name String Label shown in the menu.
call anything Runs a leaf action (skipped when submenu is set).
submenu Array<#name>/nil Child actions to descend into, or nil for leaves.
exit? Boolean When true, ends the top-level loop.

You do not have to subclass anything — any duck-typed object works. The provided building blocks are just conveniences:

  • TTY::MenuTree::Action — base class with defaults and NotImplementedError guards.
  • TTY::MenuTree::Group — an action whose submenu is its child actions.
  • TTY::MenuTree::ExitAction — an action whose exit? is true.

Customizing the prompt

Menu only needs an object responding to select_action(title, actions, back:). The default TTY::MenuTree::Prompt wraps tty-prompt and appends a "Back" entry (returning nil) when back: true. Pass your own prompt to fully control rendering:

TTY::MenuTree::Menu.new(actions: actions, prompt: MyPrompt.new).run

You can also customize the default prompt's colors or Back label:

prompt = TTY::MenuTree::Prompt.new(
  tty_prompt: TTY::Prompt.new(active_color: :magenta),
  back_label: "⬅ Back",
)

Development

bin/setup       # bundle install
bundle exec rspec

License

Released under the MIT License.