ui

package module
v0.0.0-...-1433650 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2024 License: MIT Imports: 18 Imported by: 2

README

UI package

this package takes inspiration from bubbletea and lipgloss to power some reusable components for your non bubbletea cli application. This is useful when you have a cli application that you want to enhance but don't want to wrap all the logic in a bubbletea program. Also this package depends only on standards go package.

Provided components

This are the components included in the package

Confirm
confirm, err:= ui.NewConfirm("Please confirm", false).Run()
Select

This uses a multiSelect component under the hood, already parameterized to handle a single selection.

options := []ui.SelectOption[int]{
  SelectOption[int]{Value: 1, Label: "option 1"},
  SelectOption[int]{Value: 2, Label: "option 2"},
  SelectOption[int]{Value: 3, Label: "option 3"},
}
selection, err := ui.NewSelect("Please select an option", options).
  MaxVisibleOptions(3).
  Run()

selection2, err := ui.NewSelectStrings("Please select an option from a list of strings", []{"option 1", "option 2", "option 3"}).
  Run()
Select Options

Here's a demo using all possible options

selection, err := ui.NewSelectStrings("Please select a fruit", []string{"apple", "cherry", "orange", "banana"}).
  SelectedIndex(2).
  MaxVisibleOptions(3).
  WithCleanup(true).
  Run()
MultiSelect

This is a simple component to handle user selection it presents a menu to the user for selection it can handle multiple or single selection.

selection, err := ui.NewSelect("Choose an option", options).Run()

MultiSelect Options

Here's a demo using some possible options

selection, err := ui.NewSelect("Choose an option", options).
  SelectionMaxLen(2).              // default to 0 which means no limit
  SelectionMinLen(2).              // default to 1
  SelectionExactLen(2).            // this does exactly the same as the two lines above
  AllowEmptySelection().           // this does the same as SelectionMinLen(0)
  // following options are ignored in fallback mode
  SelectedIndexed(1,2).            // set a pre-selection of selected item
  MaxVisibleOptions(5).            // default value can be changed with ui.SetDefaultMaxVisibleOptions(5)
  WithCleanup(true)                // remove the selector from output when done
  Run()

InputText / InputPassword

A component to gather text input with current edition binding attached. It supports validation and auto-completion.

input, err := ui.NewInputText(msg)
InputText Options
inputStr, err := ui.NewInputText(msg).
	Inline(). // prompt will be appended to msg end without new line
	SetPrompt("👉 "). // set a fancy prompt
	SetMaxLen(100). // it will block input over the given number of character
	SetMaxWidth(10). // set the size of the input field
	WithValidator(func (val string) bool {
		// your validation logic goes here
	}).
	WithCleanUp(). // the msg and user input will be removed from display
	WithCompletion(func(startOfWord string, fullWord string) ([]string, error){
		// Receives the start of the word to complete and the full word which can be different
		// if cursor is placed within a word. 
		// Returns the list of available completions.
	}).
	Run()

Limitations
  • Input password may display the password when in fallback mode
  • If terminal width can't be detected it can lead to weird behavior, same goes if the terminal is resized during edition.
  • It doesn't support input of carriage return, line feed or tab, they all will be replaced by single space
  • Escapes sequences will be removed

How to create your own components

If you come from bubbletea, you will be familiar with most of what to do here. If not I encourage you to look at existing components for better understanding. Don't hesitate to contact me for more in depth knowledge.

The ComponentApi

your component should implement the ModelInterface which means it also has to implement a GetComponentApi() *ComponentApi method where component api looks like:

type ComponentApi struct {
	Done    bool // if true will stop to wait for user input
	Cleanup bool // if true will remove component from ouput when done
	InputReader inputReader // select user input type you want to listen to
}

manipulating this api from your model will tell the system when it's time to stop and if it should cleanup or refresh the view when done.

keybindings helpers

In order to ease the use of key bindings and automatically generate the help string you should defined keybindings in your model Init method like this:

func (m *model[T]) Init() ui.Cmd {
	m.bindings = NewKeyBindings[*model[T]]()
  // (only the first key specified will appear in the helper message)
	m.bindings.AddBinding("up,k", ui.Msgs["up"], func(m *model[T]) ui.Cmd {
		if m.focusedIndex > 0 {
			m.focusedIndex--
		}
		return nil
	})
	m.bindings.AddBinding("down,j", ui.Msgs["down"], func(m *model[T]) ui.Cmd {
		if m.focusedIndex < len(m.options)-1 {
			m.focusedIndex++
		}
		return nil
	})
  // if description is "" then the key binding won't appear in the helper message
	m.bindings.AddBinding("ctrl+c", "", func(m *model[T]) ui.Cmd {
    return ui.CmdKill
	})
	return nil
}

Reading this you should have noticed the use of the ui.Msgs instead of raw strings this is intended for consistency and to allow localization of components. To localize components it's up to you to detect language and replace strings in ui.Msgs to sweets your needs (at least for now this may change in the future). So you are encouraged to use predefined messages in your components when there's already one available for your use case.

Defining your keybindings this way will allow you to easily handle keys in your Update method and to generate an help message in your Render method like this:


func (m *model[T]) Update(msg ui.Msg) (ui.Model, ui.Cmd) {
	cmd := m.bindings.Handle(m, msg)
	if m.done {
		return m, ui.CmdQuit
	}
	return m, cmd
}

func (m *model[T]) Render() string {
	theme := GetTheme() // use theme defined method for consistency more on theme later
	var sb strings.Builder
	sb.WriteString(theme.Title(m.title) + "\n")
  // ... rest of your UI here ...//
  // add a help string (showing keyboard bindings)
  sb.WriteString(m.bindings.GetDescription())
  sb.WriteString("\n")
	return sb.String()
}
the FallBack method

In addition to Init, Update, and Render components in this package MUST define a FallBack method. The Fallback method will be used when NO_COLOR or ASSIST env variables are set or when ui.ToggleEnhenced(false) have been called

This should be a less appealing version of your component, with no formatting and simple readline for user input. When used the Update method will not be used, so your are on your own on the fallback method to handle inputs, and call the Fallback method again if you need to retry

here's a basic example:

func (m *model[T]) Fallback() (ui.Model, error) {
	var sb strings.Builder
	// reset selected
	sb.WriteString(fmt.Sprintf("%s\n", m.title))
	if m.errorMsg != "" {
		sb.WriteString(fmt.Sprintf("Error: %s\n", m.errorMsg))
		m.errorMsg = ""
	}
  // some methods are provided to assist you like: Readline, ReadInt, ReadInts
	ints, err := ReadInts(sb.String())+
	if err != nil {
		m.errorMsg = Msgs["notANumber"]
		return m.Fallback() // call the method again as we don't have a valid input
	}
	return m, nil
}

Limitations

This is not intended to be used in asynchronous environment. Reading user input should be a synchronous tasks, you can read an interesting article on that topic here

Documentation

Overview

Copyright © 2024 Jonathan Gotti <jgotti at jgotti dot org> SPDX-FileType: SOURCE SPDX-License-Identifier: MIT SPDX-FileCopyrightText: 2024 Jonathan Gotti <[email protected]>

Index

Constants

View Source
const (
	Black        colorANSI8 = 0
	Red          colorANSI8 = 1
	Green        colorANSI8 = 2
	Yellow       colorANSI8 = 3
	Blue         colorANSI8 = 4
	Magenta      colorANSI8 = 5
	Cyan         colorANSI8 = 6
	White        colorANSI8 = 7
	ResetColor   colorANSI8 = 9
	DefaultColor colorANSI8 = ResetColor

	BrightBlack   colorANSI256 = 8
	BrightRed     colorANSI256 = 9
	BrightGreen   colorANSI256 = 10
	BrightYellow  colorANSI256 = 11
	BrightBlue    colorANSI256 = 12
	BrightMagenta colorANSI256 = 13
	BrightCyan    colorANSI256 = 14
	BrightWhite   colorANSI256 = 15

	// grayscale 1-24 from dark to light
	Gray1  colorANSI256 = 232
	Gray2  colorANSI256 = 233
	Gray3  colorANSI256 = 234
	Gray4  colorANSI256 = 235
	Gray5  colorANSI256 = 236
	Gray6  colorANSI256 = 237
	Gray7  colorANSI256 = 238
	Gray8  colorANSI256 = 239
	Gray9  colorANSI256 = 240
	Gray10 colorANSI256 = 241
	Gray11 colorANSI256 = 242
	Gray12 colorANSI256 = 243
	Gray13 colorANSI256 = 244
	Gray14 colorANSI256 = 245
	Gray15 colorANSI256 = 246
	Gray16 colorANSI256 = 247
	Gray17 colorANSI256 = 248
	Gray18 colorANSI256 = 249
	Gray19 colorANSI256 = 250
	Gray20 colorANSI256 = 251
	Gray21 colorANSI256 = 252
	Gray22 colorANSI256 = 253
	Gray23 colorANSI256 = 254
	Gray24 colorANSI256 = 255

	// aliases
	DarkGrey  colorANSI256 = Gray2
	MidGrey   colorANSI256 = Gray13
	LightGrey colorANSI256 = Gray23
)
View Source
const (
	// this is the default reader it will send MsgKey
	KeyReader inputReader = iota
	// LineReader will send MsgLine (same as LineReaderFramed if terminal width is available else default to LineReaderUnbounded)
	LineReader
	// LineReaderUnbounded will send MsgLine (doesn't manage terminal width)
	// this is more a fallback mode when the terminal width is not available
	LineReaderUnbounded
	// LineReaderWrapped will send MsgLine
	// it will wrap the text if it's too long for the terminal width
	// will default to LineReaderUnbounded if the terminal width is not available
	//
	// suitable to edit long text at the end of display (doesn't support new lines or carriage returns)
	//
	// WARNING: this mode is still highly experimental and may not work as expected
	//          any help to improve it is welcome
	LineReaderWrapped

	// this will send MsgLine and will not manage any display of the value
	// suitable for password input
	PasswordReader
	// this will send MsgInt
	IntReader
	// this will send MsgInts
	IntsReader
)

Variables

View Source
var (
	ErrSIGINT       = fmt.Errorf("SIGINT")
	ErrReadKey      = fmt.Errorf("read key error")
	ErrPasteTimeout = fmt.Errorf("%w: paste timeout", ErrReadKey)
	ErrUnknownKey   = fmt.Errorf("%w: unknown key", ErrReadKey)
	ErrComp         = fmt.Errorf("completion error")
	ErrNaN          = fmt.Errorf("not a number")
)
View Source
var (
	ErrNOTERM    = fmt.Errorf("not a terminal")
	ErrTimeout   = fmt.Errorf("timeout")
	ErrBadFormat = fmt.Errorf("bad response format")
)
View Source
var ErrInputRead = fmt.Errorf("input read error")
View Source
var ErrRunComponent = fmt.Errorf("error while running component")
View Source
var ErrSelectEscaped = fmt.Errorf("selection escaped")
View Source
var KeyDescriptors = map[string]string{
	"left":      "←",
	"right":     "→",
	"up":        "↑",
	"down":      "↓",
	"enter":     "↵",
	" ":         "space",
	"esc":       "escape",
	"tab":       "⇥",
	"shift+tab": "⇤",
	"backspace": "⌫",
	"delete":    "⌦",
	"home":      "⇱",
	"end":       "⇲",
	"pgup":      "⇞",
	"pgdown":    "⇟",
}
View Source
var Msgs = map[string]string{
	"up":                             "Move up",
	"down":                           "Move down",
	"select":                         "Select",
	"confirm":                        "Confirm",
	"cancel":                         "Cancel",
	"submit":                         "Submit",
	"userCanceled":                   "Canceled by user",
	"limitReached":                   "Selection limit reached",
	"selectAllToggle":                "Select/Deselect all",
	"errorPrefix":                    "Error: ",
	"fallbackMultiSelectPrompt":      "Select items by entering corresponding numbers: (eg: 1 2 3) ",
	"fallbackSelectPrompt":           "Select one item entering corresponding number: ",
	"fallbackConfirmError":           "Invalid input, please enter 'y' or 'n'",
	"fallbackConfirmPromptTrue":      "[Y/n]: ",
	"fallbackConfirmPromptFalse":     "[y/N]: ",
	"fallbackConfirmHelpPromptTrue":  "type y/n then enter (default yes): ",
	"fallbackConfirmHelpPromptFalse": "type y/n then enter (default no): ",
	"notANumber":                     "Selection must only contain numbers",

	"outBoundMin": "You must select at least %d options",

	"outBoundMax": "You can not select more than %d options",

	"outOfRange": "Selection out of range please select numbers between 1 and %d",
}

Functions

func ApplyStyle

func ApplyStyle(s string, styles ...SGRParam) string

Directly apply styles to a string. If Enhanced is disabled, the string is returned as is. Sample usage: ui.ApplyStyle("This will be red and bold.", colors.Red, colors.Bold)

func Confirm

func Confirm(msg string, dflt bool) bool

Shorthand for NewConfirm(msg, dflt).Run() ignoring errors

func ConfirmInline

func ConfirmInline(msg string, dflt bool) bool

Shorthand for NewConfirm(msg, dflt).Inline().Run() ignoring errors

func ConfirmInlineNoHelp

func ConfirmInlineNoHelp(msg string, dflt bool) bool

Shorthand for NewConfirm(msg, dflt).Inline().WithoutHelp().Run() ignoring errors

func EnhancedEnabled

func EnhancedEnabled() bool

Returns whether enhanced rendering is enabled

func EraseNLines

func EraseNLines(n int)

func EraseText

func EraseText(text string)

func NewConfirm

func NewConfirm(msg string, dflt bool) *confirmModel

func NewMultiSelect

func NewMultiSelect[T comparable](title string, options []SelectOption[T]) *multiSelectModel[T]

NewMultiSelect creates a new multi-select model with the given title and options. The options should be a slice of MultiSelectOption, where each option is a value that implements the comparable interface.

Usage:

type MyOption struct {
    Name string
    Value int // can be any comparable type
}

options := []MultiSelectOption[MyOption]{
    MyOption{Name: "Option 1", Value: 1},
    MyOption{Name: "Option 2", Value: 2},
    MyOption{Name: "Option 3", Value: 3},
}

multiSelect := NewMultiSelect[MyOption]("Choose an option", options) selected := multiSelect.Run()

for _, option := range selected {
    fmt.Printf("Selected option: %s, value: %d\n", option.Name, option.Value)
}

func NewMultiSelectStrings

func NewMultiSelectStrings(title string, options []string) *multiSelectModel[string]

NewMultiSelectStrings creates a new multi-select model with the given title and options. The options should be a slice of strings. This is a shortcut for when you only need to select between strings without having to manually prepare a slice of SelectOption.

Usage:

options := []string{"Option 1", "Option 2", "Option 3"}

multiSelect := NewMultiSelectStrings("Choose an option", options) selected := multiSelect.Run()

for _, option := range selected {
    fmt.Printf("Selected option: %s\n", option)
}

func NewSelect

func NewSelect[T comparable](title string, options []SelectOption[T]) *selectModel[T]

func NewSelectStrings

func NewSelectStrings(title string, options []string) *selectModel[string]

func Println

func Println(s string, styles ...SGRParam)

utility function to print a string with given styles

func RunComponent

func RunComponent[M Model](m M) (M, error)

* helper function to run a component and return the result model

func SGREscapeSequence

func SGREscapeSequence(styles ...SGRParam) string

Return an ANSI SGR escape sequence from given SGR parameters. Sample usage: ui.SGREscapeSequence(colors.Red, colors.Bold)

func SGRResetSequence

func SGRResetSequence(styles ...SGRParam) string

Return the reset ANSI SGR escape sequence corresponding for given SGR parameters. this is more computationally expensive than simply use a reset all sequence \033[0m but it's more accurate as it will only reset the styles that were set (waring Faint/Bold use the same reset). thus allowing to embed styles in styles without having to worry about resetting them.

If no styles are given it will return a reset all sequence. If it can't determine the reset style for one of its arguments, it will return a reset all sequence.

func SetDefaultMaxVisibleOptions

func SetDefaultMaxVisibleOptions(max int)

set a number either 0 or between 3 and 15

func SetTerminal

func SetTerminal(t TermInterface)

this should be mocked in tests

func ToggleEnhanced

func ToggleEnhanced(enable bool)

Allow to turn off colouring for all Style methods Be careful: if you do string(colors.Red) + "a red string" + string(Reset) it will still be rendered in red as you use colors codes directly.

Types

type AdaptiveColor

type AdaptiveColor struct {
	Dark  ColorInterface
	Light ColorInterface
}

func (AdaptiveColor) Background

func (c AdaptiveColor) Background() SGRParam

func (AdaptiveColor) Color

func (c AdaptiveColor) Color() ColorInterface

func (AdaptiveColor) Foreground

func (c AdaptiveColor) Foreground() SGRParam

type Cmd

type Cmd func() Msg

type Color

type Color string

func (Color) Background

func (c Color) Background() SGRParam

func (Color) Foreground

func (c Color) Foreground() SGRParam

type ColorInterface

type ColorInterface interface {
	Foreground() SGRParam
	Background() SGRParam
}

type ComponentApi

type ComponentApi struct {
	Done        bool
	Cleanup     bool
	InputReader inputReader
}

type InputTextModel

type InputTextModel struct {
	// contains filtered or unexported fields
}

func NewInputPassword

func NewInputPassword(msg string) *InputTextModel

Beware that it will work as an inputText in fallback mode

func NewInputText

func NewInputText(msg string) *InputTextModel

func (*InputTextModel) Fallback

func (m *InputTextModel) Fallback() (Model, error)

func (*InputTextModel) GetComponentApi

func (m *InputTextModel) GetComponentApi() *ComponentApi

func (*InputTextModel) Init

func (m *InputTextModel) Init() Cmd

func (*InputTextModel) Inline

func (m *InputTextModel) Inline() *InputTextModel

func (*InputTextModel) ReadlineCompletion

func (m *InputTextModel) ReadlineCompletion(wordStart string, word string) ([]string, error)

ReadlineCompletion is called by the ReadlineEnhanced function to get completion suggestions

  • wordStart is the start of the word to complete (start of the word to cursor position)
  • word is the whole word under cursor (start to end of the word)

func (*InputTextModel) ReadlineConfig

func (m *InputTextModel) ReadlineConfig() LineEditorOptions

func (*InputTextModel) ReadlineKeyHandler

func (m *InputTextModel) ReadlineKeyHandler(key string) (Msg, error)

func (*InputTextModel) Render

func (m *InputTextModel) Render() string

func (*InputTextModel) Run

func (m *InputTextModel) Run() (string, error)

func (*InputTextModel) SetInitValue

func (m *InputTextModel) SetInitValue(value string) *InputTextModel

func (*InputTextModel) SetMaxLen

func (m *InputTextModel) SetMaxLen(n int) *InputTextModel

func (*InputTextModel) SetMaxWidth

func (m *InputTextModel) SetMaxWidth(n int) *InputTextModel

func (*InputTextModel) SetPrompt

func (m *InputTextModel) SetPrompt(prompt string) *InputTextModel

Set the prompt to display before the input (ignored in inline mode)

func (*InputTextModel) Update

func (m *InputTextModel) Update(msg Msg) Cmd

func (*InputTextModel) WithCleanup

func (m *InputTextModel) WithCleanup() *InputTextModel

Remove the input from the output when done

func (*InputTextModel) WithCompletion

func (m *InputTextModel) WithCompletion(compSuggester func(string, string) ([]string, error)) *InputTextModel

Allow to set a function to suggest completion for the input (only used in enhanced mode)

func (*InputTextModel) WithKeyHandler

func (m *InputTextModel) WithKeyHandler(keyHandler func(string) (Msg, error)) *InputTextModel

the keyHandler is called when a key is pressed in enhanced mode it should return a message to update the model or an error if nil,nil is returned then the normal input handling will be done if a non nil msg or a non nil error is returned then the input will be considered handled and enhanced readline will simply ignore the key (only used in enhanced mode)

func (*InputTextModel) WithValidator

func (m *InputTextModel) WithValidator(validator func(string) error) *InputTextModel

Allow to set a function to validate the input if the function return an error the input will be rejected and the error will be displayed the user will then be asked to enter a new value

type KeyBindings

type KeyBindings[T any] struct {
	Handlers    map[string]func(T) Cmd
	Description []string
	// contains filtered or unexported fields
}

func NewKeyBindings

func NewKeyBindings[T any]() *KeyBindings[T]

func (*KeyBindings[T]) AddBinding

func (k *KeyBindings[T]) AddBinding(keysToBind string, desc string, handler func(T) Cmd) *KeyBindings[T]

Add a key binding to the key bindings @param keysToBind multiple keys can be added separated by a comma

func (*KeyBindings[T]) AddToDescription

func (k *KeyBindings[T]) AddToDescription(desc ...string) *KeyBindings[T]

func (*KeyBindings[T]) GetDescription

func (k *KeyBindings[T]) GetDescription() string

Should be called from the View method of the model to display the key bindings

func (*KeyBindings[T]) Handle

func (k *KeyBindings[T]) Handle(m T, msg Msg) Cmd

this method should be called from the Update method of the model to handle key bindings

type LineEditorOptions

type LineEditorOptions lineEditor.LineEditorOptions

re-export the lineEditor.LineEditorOptions for convenience

type Model

type Model interface {
	// set initial state of the component
	// can return a Cmd to run some initialisation commands
	Init() Cmd
	// return a rendered view of the model to display on the terminal
	Render() string
	// update the model with a message and return an optional Cmd to run
	// if this method returns a command it will then be executed again with the
	// msg returned by the command until it returns nil or the done flag is set to true
	// then the Render method will be called again to display the updated view
	// beware that the model should not be updated concurrently
	Update(Msg) Cmd
	// This method is called when we fallback to the non-enhanced mode
	// In such case there's no Render/Update loop but only a single call to Fallback
	// For examples of fallback usage see the confirm.go file
	Fallback() (Model, error)
	// return the embeded component api
	GetComponentApi() *ComponentApi
}

type Msg

type Msg interface{}

func CmdKill

func CmdKill() Msg

func CmdQuit

func CmdQuit() Msg

func CmdUserAbort

func CmdUserAbort() Msg

func CmdUserCancel

func CmdUserCancel() Msg

func ReadKeyPressEvent

func ReadKeyPressEvent(terminal interface {
	TermIsTerminal
	TermWithRawMode
	TermWithExclusiveReader
	TTYFileDescriptor
}) (Msg, error)

func ReadLineEnhanced

func ReadLineEnhanced(terminal interface {
	TermIsTerminal
	TermWithRawMode
	TermWithExclusiveReader
	TermWithSize
	TTYFileDescriptor
}, visualMode lineEditor.VisualEditMode, provider any) (Msg, error)

This is an advanced version of ReadLine that allow to provide a value to edit. It requires a terminal which supports raw mode and is a tty to work. Most common keyboard shortcuts should be supported for navigation and editing.

The visualMode determines how the text will be displayed to the user. see [VModeHidden][VModeUnbounded][VModeWrappedLine][VModeFramedLine]

The provider can be nil or an object that implements one or more of the following interfaces:

  • readlineConfigProvider to provide some config to the line editor
  • readlineKeyHandlerProvider to provide custom key bindings
  • readlineCompletionProvider to provide completion suggestions

Providing a value to edit is possible by providing an object that implements interface{ReadlineConfig() LineEditorOptions}. If the given object does not implement this interface the value will be ignored. see LineEditorOptions for more details.

The provider can also implements interface{ReadlineKeyHandler(key string) (ui.Msg, error)} to provide custom key bindings. it will be called for each key press with the key as argument. The method should return a ui.Msg (can be any value) to update model or an error

  • if nil,nil is returned then the normal input handling will be done and editor will continue to wait for input (unless the key is a completion key)
  • if a non nil msg or a non nil error is returned then the input will be considered handled and the editor will simply ignore the key and return the ui.Msg and error stopping the editor.

It is also possible to provide auto-completion feature by implementing interface{ReadlineCompletion(wordStart, word string) ([]string, error)}. This method will be called when the user press the completion key (tab by default). It should return a list of suggestions for the word under the cursor. The wordStart is the start of the word to complete (start of the word to cursor position) and the word is the whole word under cursor (start to end of the word).

Note: this function is made public for advanced usage only. You shouldn't need to use it directly in most cases but simply define a model implementing the ComponentApi with InputReader: ui.LineReader.

func ReadPassword

This is an advanced version of ReadLine which don't print to output. It requires a terminal which supports raw mode and is a tty to work. Most common keyboard shortcuts should be supported for navigation and editing.

Note: this function is made public for advanced usage only. You shouldn't need to use it directly in most cases but simply define a model implementing the ComponentApi with InputReader: ui.PasswordReader.

type MsgInt

type MsgInt struct {
	Value int
}

func ReadInt

func ReadInt(prompt string) (MsgInt, error)

type MsgInts

type MsgInts struct {
	Value []int
}

func ParseInts

func ParseInts(str string) (MsgInts, error)

* Parse a string of space separated integers into a list of integers

func ReadInts

func ReadInts(prompt string) (MsgInts, error)

type MsgKey

type MsgKey struct {
	Value   string
	Unknown bool
	IsSeq   bool
	ByteSeq []byte
}

MsgKey is a message sent by the KeyReader it can be a known key press event or an unknown sequence if it's an unknown sequence the Value will be the raw sequence and the Unknown field will be set to true if it's a known sequence the Value will be the name of the key and the IsSeq field will be set to true if it's a paste event the IsSeq will be set to true and the Value will be "paste" and the pasted value will be returned by the IsPasteEvent method

func (MsgKey) IsPasteEvent

func (msg MsgKey) IsPasteEvent() (bool, []rune)

test if the key press event is a paste event if it is a paste event it will return true and the pasted value as []rune

func (MsgKey) String

func (msg MsgKey) String() string

type MsgKill

type MsgKill struct{}

MsgKill is a message to kill the program

type MsgLine

type MsgLine interface {
	// Value returns the value of the line
	Value() string
	// return a string representation of the line as displayed while editing
	Sprint() (string, error)
}

func Readline

func Readline(prompt string) (MsgLine, error)

* read a line from stdin

type MsgLineEnhanced

type MsgLineEnhanced struct {
	// contains filtered or unexported fields
}

func (MsgLineEnhanced) Sprint

func (msg MsgLineEnhanced) Sprint() (string, error)

func (MsgLineEnhanced) Value

func (msg MsgLineEnhanced) Value() string

type MsgLineSimple

type MsgLineSimple struct {
	// contains filtered or unexported fields
}

func (MsgLineSimple) Sprint

func (msg MsgLineSimple) Sprint() (string, error)

func (MsgLineSimple) Value

func (msg MsgLineSimple) Value() string

type MsgQuit

type MsgQuit struct{}

MsgQuit is a message to mark the component as done

type RGB

type RGB [3]int

type SGRParam

type SGRParam string
const (
	Bold      SGRParam = "1"
	Faint     SGRParam = "2"
	Italic    SGRParam = "3"
	Underline SGRParam = "4"
	Blink     SGRParam = "5"
	Reversed  SGRParam = "7"
	Strike    SGRParam = "9"

	ResetBold       SGRParam = "22" // 21m is not specified this is not a mistake
	ResetFaint      SGRParam = "22" // so 22 reset both Bold and Faint
	ResetItalic     SGRParam = "23"
	ResetUnderline  SGRParam = "24"
	ResetBlink      SGRParam = "25"
	ResetReversed   SGRParam = "27"
	ResetStrike     SGRParam = "29"
	ResetForeground SGRParam = "39"
	ResetBackground SGRParam = "49"
	ResetAll        SGRParam = "0"
)

func BackgroundANSI256

func BackgroundANSI256(c int) SGRParam

func BackgroundHex

func BackgroundHex(hex string) SGRParam

func BackgroundRGB

func BackgroundRGB(rgb RGB) SGRParam

func ForegroundANSI256

func ForegroundANSI256(c int) SGRParam

func ForegroundHex

func ForegroundHex(hex string) SGRParam

func ForegroundRGB

func ForegroundRGB(rgb RGB) SGRParam

func SGRCombine

func SGRCombine(styles ...SGRParam) SGRParam

Combine multiple ANSI SGR params.

type SelectOption

type SelectOption[T comparable] struct {
	Value T
	Label string
}

type Styler

type Styler = func(s ...string) string

func NewStyler

func NewStyler(styles ...SGRParam) Styler

Returns a function that will apply given styles to the received String The returned function will return an unstyled string if ui.ToggleEnhanced(false) has been called (or set to false on init by the env var NO_COLOR, ACCESSIBLE or TERM=dumb) Sample usage: ui.NewStyler(colors.Red, colors.Bold)("This will be red and bold.")

type TTYFileDescriptor

type TTYFileDescriptor interface {
	Tty() *os.File
}

type TermIsTerminal

type TermIsTerminal interface {
	IsTerminal() bool
}

type TermState

type TermState state

type TermWithBackground

type TermWithBackground interface {
	// WARNING: you should have checked that the terminal is a terminal before calling this function.
	// you can do it like this term.IsTerminal(int(os.stdin.Fd()))
	// Try to use OSC query to get the background color.
	// it is the most reliable method to get the background color when available,
	// but it is not supported in all terminals.
	// If it fails, it will default to the COLORFGBG environment variable.
	// but it is not reliable for example Konsole keep reporting the wrong
	// background color after a change.
	HasDarkBackground() (bool, error)
}

type TermWithExclusiveReader

type TermWithExclusiveReader interface {
	// Returns a mutex locked reader on the terminal input and a function to unlock it when done
	// this is useful to avoid concurrent access to the reader.
	// You should have checked that the terminal is a terminal before calling this function.
	ExclusiveReader() (*bufio.Reader, func())
}

type TermWithRawMode

type TermWithRawMode interface {
	MakeRaw() (*TermState, error)
	Restore(*TermState) error

	// put terminal in raw mode and return a restore function and an error if any
	//
	// Usage:
	//
	//	restore, err := handleTerminalState(terminal)
	//	defer restore()
	HandleState(hideCursor bool) (func(), error)
}

type TermWithSize

type TermWithSize interface {
	// GetSize returns the number of columns and rows in the terminal.
	GetSize() (int, int, error)
	// DeviceStatusReport returns the current cursor position in the terminal.
	DeviceStatusReport() (row, col int, err error)
}

type Terminal

type Terminal struct {
	// contains filtered or unexported fields
}

func NewTerminal

func NewTerminal(tty *os.File) (*Terminal, error)

func (*Terminal) AltScreenBuffer

func (t *Terminal) AltScreenBuffer() error

func (*Terminal) DeviceStatusReport

func (t *Terminal) DeviceStatusReport() (row, col int, err error)

func (*Terminal) ExclusiveReader

func (t *Terminal) ExclusiveReader() (*bufio.Reader, func())

func (*Terminal) GetSize

func (t *Terminal) GetSize() (int, int, error)

func (*Terminal) HandleState

func (t *Terminal) HandleState(hideCursor bool) (func(), error)

func (*Terminal) HasDarkBackground

func (t *Terminal) HasDarkBackground() (bool, error)

func (*Terminal) IsTerminal

func (t *Terminal) IsTerminal() bool

func (*Terminal) MainScreenBuffer

func (t *Terminal) MainScreenBuffer() error

func (*Terminal) MakeRaw

func (t *Terminal) MakeRaw() (*TermState, error)

func (*Terminal) Restore

func (t *Terminal) Restore(state *TermState) error

func (*Terminal) SetBracketedPasteMode

func (t *Terminal) SetBracketedPasteMode(on bool) error

func (*Terminal) Tty

func (t *Terminal) Tty() *os.File

type Theme

type Theme struct {
	Config ThemeConfig
	// contains filtered or unexported fields
}

func GetTheme

func GetTheme() *Theme

Get the global theme to use in the whole application if theme is not set it will set theme to the default theme

func NewTheme

func NewTheme(themeInitializer ThemeInitializer) *Theme

func SetTheme

func SetTheme(theme ThemeInitializer) *Theme

Set the global theme to use in the whole application if theme is nil it will use the default theme

func (*Theme) Accentuated

func (t *Theme) Accentuated(s ...string) string
func (t *Theme) Blink(s ...string) string

func (*Theme) Bold

func (t *Theme) Bold(s ...string) string

beware that Bold also reset the Faint style

func (*Theme) Button

func (t *Theme) Button(s ...string) string

func (*Theme) ButtonAccentuated

func (t *Theme) ButtonAccentuated(s ...string) string

func (*Theme) ButtonError

func (t *Theme) ButtonError(s ...string) string

func (*Theme) ButtonSuccess

func (t *Theme) ButtonSuccess(s ...string) string

func (*Theme) ConditionalFocusIndicator

func (t *Theme) ConditionalFocusIndicator(focus bool) string

func (*Theme) ConditionalSelectedIndicator

func (t *Theme) ConditionalSelectedIndicator(selected bool) string

func (*Theme) Copy

func (t *Theme) Copy() Theme

func (*Theme) EmphasedError

func (t *Theme) EmphasedError(s ...string) string

func (*Theme) EmphasedInfo

func (t *Theme) EmphasedInfo(s ...string) string

func (*Theme) EmphasedSuccess

func (t *Theme) EmphasedSuccess(s ...string) string

func (*Theme) EmphasedWarning

func (t *Theme) EmphasedWarning(s ...string) string

func (*Theme) Error

func (t *Theme) Error(s ...string) string

func (*Theme) FailureIndicator

func (t *Theme) FailureIndicator() string

func (*Theme) Faint

func (t *Theme) Faint(s ...string) string

beware that Faint also reset the Bold style

func (*Theme) FocusItemIndicator

func (t *Theme) FocusItemIndicator() string

func (*Theme) Info

func (t *Theme) Info(s ...string) string

func (*Theme) Italic

func (t *Theme) Italic(s ...string) string

func (*Theme) KeyBindingSeparator

func (t *Theme) KeyBindingSeparator() string

func (*Theme) KeySeparator

func (t *Theme) KeySeparator() string

func (*Theme) MoreDownIndicator

func (t *Theme) MoreDownIndicator() string

func (*Theme) MoreUpIndicator

func (t *Theme) MoreUpIndicator() string

func (*Theme) ReverseAccentuated

func (t *Theme) ReverseAccentuated(s ...string) string

func (*Theme) Reversed

func (t *Theme) Reversed(s ...string) string

func (*Theme) SelectedIndicator

func (t *Theme) SelectedIndicator() string

func (*Theme) Strike

func (t *Theme) Strike(s ...string) string

func (*Theme) Success

func (t *Theme) Success(s ...string) string

func (*Theme) SuccessIndicator

func (t *Theme) SuccessIndicator() string

func (*Theme) Title

func (t *Theme) Title(s ...string) string

func (*Theme) UnFocusItemIndicator

func (t *Theme) UnFocusItemIndicator() string

func (*Theme) UnSelectedIndicator

func (t *Theme) UnSelectedIndicator() string

func (*Theme) Underline

func (t *Theme) Underline(s ...string) string

func (*Theme) Warning

func (t *Theme) Warning(s ...string) string

type ThemeConfig

type ThemeConfig struct {
	AccentColor ColorInterface
	/** color to use for text on top of accent color */
	AccentText   ColorInterface
	ErrorColor   ColorInterface
	ErrorText    ColorInterface
	SuccessColor ColorInterface
	SuccessText  ColorInterface
	WarningColor ColorInterface
	WarningText  ColorInterface
	InfoColor    ColorInterface
	InfoText     ColorInterface

	SelectedIndicatorString    string
	UnSelectedIndicatorString  string
	FocusItemIndicatorString   string
	UnFocusItemIndicatorString string
	MoreUpIndicatorString      string
	MoreDownIndicatorString    string
	// key bindings separators should not be styled
	KeySeparator string
	// key bindings separators should not be styled
	KeyBindingSeparator string

	ButtonStyler            func(...string) string
	ButtonAccentuatedStyler func(...string) string
	// contains filtered or unexported fields
}

func ThemeDefault

func ThemeDefault() ThemeConfig

func ThemeMonoSpace

func ThemeMonoSpace() ThemeConfig

func (*ThemeConfig) Copy

func (t *ThemeConfig) Copy() ThemeConfig

type ThemeInitializer

type ThemeInitializer func() ThemeConfig

Directories

Path Synopsis
examples
pkg
tools

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳