Skip to content

yarlson/tap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

102 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tap

CI

A Go library for building beautiful, interactive command-line prompts and terminal UI components.

Features

  • Interactive Prompts: Text input, password, confirmation, single/multi-select, autocomplete, and multiline textarea
  • Progress Indicators: Spinners with customizable frames and progress bars with multiple styles
  • Output Utilities: Styled tables, boxed messages, and streaming output
  • Modern API: Context-aware, generic types for select options, functional options pattern
  • Cross-Platform: Unix and Windows terminal support
  • Multiline Input: Textarea supports multiline editing with Shift+Enter for new lines and Up/Down navigation

Installation

go get github.com/yarlson/tap

Requirements: Go 1.24+

Quick Start

Text Input

package main

import (
    "context"
    "fmt"

    "github.com/yarlson/tap"
)

func main() {
    name := tap.Text(context.Background(), tap.TextOptions{
        Message:     "Enter your name:",
        Placeholder: "Type something...",
    })
    fmt.Printf("Hello, %s!\n", name)
}

Select Menu

colors := []tap.SelectOption[string]{
    {Value: "red", Label: "Red", Hint: "The color of passion"},
    {Value: "blue", Label: "Blue", Hint: "The color of calm"},
    {Value: "green", Label: "Green", Hint: "The color of nature"},
}

result := tap.Select[string](context.Background(), tap.SelectOptions[string]{
    Message: "What's your favorite color?",
    Options: colors,
})
fmt.Printf("You selected: %s\n", result)

Spinner

spin := tap.NewSpinner(tap.SpinnerOptions{})
spin.Start("Connecting")
// ... do work ...
spin.Stop("Connected", 0)

// With hint (optional second line in gray)
spin.Stop("Installed 42 packages", 0, tap.StopOptions{
    Hint: "Run 'npm start' to begin",
})

Progress Bar

progress := tap.NewProgress(tap.ProgressOptions{
    Style: "heavy",
    Max:   100,
    Size:  40,
})

progress.Start("Downloading...")
for i := 0; i <= 100; i += 10 {
    progress.Advance(10, fmt.Sprintf("Downloading... %d%%", i))
}
progress.Stop("Download complete!", 0)

// With hint (optional second line in gray)
progress.Stop("Download complete!", 0, tap.StopOptions{
    Hint: "Saved to ~/Downloads/file.zip (128 MB)",
})

Textarea

res := tap.Textarea(context.Background(), tap.TextareaOptions{
    Message:     "Enter your commit message:",
    Placeholder: "Type something...",
    Validate: func(s string) error {
        if len(s) < 10 {
            return fmt.Errorf("at least 10 characters required")
        }
        return nil
    },
})
fmt.Println(res)

Use Shift+Enter to insert new lines. Pasted content is collapsed into [Text N] placeholders and expanded on submit.

Messages with Hints

// Message, Outro, and Cancel support optional hints
tap.Message("Task completed", tap.MessageOptions{
    Hint: "Processed 42 items in 1.2s",
})

tap.Outro("All done!", tap.MessageOptions{
    Hint: "Run 'app --help' for more options",
})

Keyboard Shortcuts

All Prompts

Key Action
Return (Enter) Submit/confirm selection
Escape or ^C Cancel and exit prompt
Left/Right Move cursor left/right
Backspace/Del Delete character

Textarea

Key Action
Shift+Return Insert new line (multiline input)
Up/Down Move to previous/next line
Home Move to start of current line
End Move to end of current line
Return Submit multiline text

API Reference

Interactive Prompts

Function Description Return Type
Text(ctx, TextOptions) Single-line text input string
Password(ctx, PasswordOptions) Masked password input string
Confirm(ctx, ConfirmOptions) Yes/No confirmation bool
Select[T](ctx, SelectOptions[T]) Single-choice selection T
MultiSelect[T](ctx, MultiSelectOptions[T]) Multiple-choice selection []T
Textarea(ctx, TextareaOptions) Multiline text input string
Autocomplete(ctx, AutocompleteOptions) Text input with suggestions string

Progress Components

Function Description
NewSpinner(SpinnerOptions) Animated spinner indicator
NewProgress(ProgressOptions) Progress bar with percentage
NewStream(StreamOptions) Streaming output with optional timer

Output Utilities

Function Description
Table(headers, rows, TableOptions) Render formatted tables
Box(message, title, BoxOptions) Render boxed messages
Intro(title, ...MessageOptions) Display intro message
Outro(message, ...MessageOptions) Display outro message
Message(message, ...MessageOptions) Display styled message

Options Structs

TextOptions

type TextOptions struct {
    Message      string
    Placeholder  string
    DefaultValue string
    InitialValue string
    Validate     func(string) error
    Input        Reader
    Output       Writer
}

TextareaOptions

type TextareaOptions struct {
    Message      string             // Prompt label displayed above the input area
    Placeholder  string             // Hint text shown when input is empty
    DefaultValue string             // Value returned when user submits empty input
    InitialValue string             // Pre-populated editable content on prompt start
    Validate     func(string) error // Validates the fully-resolved string on submit
    Input        Reader
    Output       Writer
}

Validate receives the resolved string with all paste placeholders expanded. Return an error to reject the input — the error message is displayed below the input and the user can continue editing.

SelectOptions

type SelectOptions[T any] struct {
    Message      string
    Options      []SelectOption[T]
    InitialValue *T
    MaxItems     *int
    Input        Reader
    Output       Writer
}

SpinnerOptions

type SpinnerOptions struct {
    Indicator string        // "dots" (default) or "timer"
    Frames    []string      // custom animation frames
    Delay     time.Duration // frame delay
}

ProgressOptions

type ProgressOptions struct {
    Style string  // "heavy", "light", or "block"
    Max   int     // maximum value
    Size  int     // bar width in characters
}

TableOptions

type TableOptions struct {
    ShowBorders      bool
    IncludePrefix    bool
    MaxWidth         int
    ColumnAlignments []TableAlignment
    HeaderStyle      TableStyle
    HeaderColor      TableColor
    FormatBorder     func(string) string
}

MessageOptions

type MessageOptions struct {
    Output Writer
    Hint   string // Optional second line displayed in gray
}

StopOptions

Used with Spinner.Stop() and Progress.Stop() to add an optional hint line:

type StopOptions struct {
    Hint string // Optional second line displayed in gray below the message
}

Examples

Run interactive examples:

go run ./examples/text/main.go
go run ./examples/textarea/main.go
go run ./examples/password/main.go
go run ./examples/select/main.go
go run ./examples/multiselect/main.go
go run ./examples/confirm/main.go
go run ./examples/autocomplete/main.go
go run ./examples/spinner/main.go
go run ./examples/progress/main.go
go run ./examples/messages/main.go
go run ./examples/table/main.go
go run ./examples/stream/main.go

Compatibility

Platform Support

Terminal signal handling differs between Unix and Windows. The library includes platform-specific implementations:

  • internal/terminal/terminal_unix.go - Unix signal handling
  • internal/terminal/terminal_windows.go - Windows signal handling

Textarea Terminal Requirements

Shift+Enter (multiline input) requires a terminal that reports modifier keys. Supported terminals include iTerm2, kitty, Alacritty, WezTerm, Ghostty, and Windows Terminal. Terminals that don't distinguish Shift+Enter from Enter (e.g., macOS Terminal.app) cannot insert newlines via keyboard — users can paste multiline content instead.

Bracketed paste mode enables paste detection and [Text N] placeholder collapsing. Supported by all major modern terminals. Terminals without bracketed paste support degrade to character-by-character input — functional but without placeholder collapsing.

Environment Variables

Variable Required Description
TERM No Terminal type for ANSI escape sequence support

Development

Running Tests

go test ./...

With race detection:

go test -race ./...

Linting

The project uses golangci-lint with configuration in .golangci.yml:

golangci-lint run

Testing Patterns

Override terminal I/O for deterministic tests:

in := tap.NewMockReadable()
out := tap.NewMockWritable()
tap.SetTermIO(in, out)
defer tap.SetTermIO(nil, nil)

// Simulate keypresses
go func() {
    in.EmitKeypress("h", tap.Key{Name: "h"})
    in.EmitKeypress("i", tap.Key{Name: "i"})
    in.EmitKeypress("", tap.Key{Name: "return"})
}()

result := tap.Text(ctx, tap.TextOptions{Message: "Enter:"})

Troubleshooting

Platform-specific signal handling

Symptom: Terminal signal handling differs between Unix and Windows

Solution: The library includes platform-specific implementations. Ensure you're building for the correct target platform. Check internal/terminal/terminal_unix.go and internal/terminal/terminal_windows.go for platform-specific behavior.

ANSI sequence handling for width calculations

Symptom: Text width calculations may produce unexpected results with styled text

Solution: The library handles ANSI escape sequences in width calculations via visibleWidth() and truncateToWidth() functions in ansi_utils.go. Ensure styled text is properly formatted with reset sequences.

Contributing

Contributions are welcome. When contributing:

  • Follow existing code patterns and naming conventions
  • Add unit tests using the mock I/O pattern
  • Add examples under examples/<feature>/main.go for new features
  • Run go test ./... and golangci-lint run before submitting

License

MIT License - see LICENSE for details.

About

Beautiful, interactive command-line prompts for Go

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors

Languages