Yet Another CLi Spinner (for Go)
Package yacspin
provides yet another CLi spinner for Go, taking inspiration (and some utility code) from the https://github.com/briandowns/spinner project. Specifically yacspin
borrows the default character sets, and color mappings to github.com/fatih/color colors, from that project.
License
Because this package adopts the spinner character sets from https://github.com/briandowns/spinner, this package is released under the Apache 2.0 License.
Yet Another CLi Spinner?
The other Go spinner ties the ability to show updated information to the spinner's animation, meaning you can't always show all the information you want to the end user without changing the animation speed. In addition there were also some API design choices that have made it unsafe for concurrent use (while it's running), which presents challenges when trying to update the text in the spinner while it's animating in the first place
There was also an interest in the spinner being able to represent a task, and to indicate whether it failed or was successful. This would have further compounded the API issues mentioned above.
This project takes inspiration from that other project, and takes a new approach to address the challenges above.
Features
Provided Spinners
There are over 70 spinners available in the CharSets
package variable. They were borrowed from github.com/briandowns/spinner. There is a table with all of the spinners at the bottom of this README.
Dynamic Width of Animation
Because of how some spinners are animated, they may have different widths are different times in the animation. The spinner calculates the maximum width, and pads the animation to ensure the text's position on the screen doesn't change. This results in a smoother looking animation.
yacspin
other spinners
Success and Failure Results
The spinner has both a Stop()
and StopFail()
method, which allows the spinner to result in a success message or a failure message. The messages, colors, and even the character used to denote success or failure are customizable in either the initial config or via the methods.
By doing this you can use the spinner to display the status of a list of tasks being executed serially.
Stop
StopFail
Concurrency
The spinner is safe for concurrent use, so you can update any of its settings via methods whether the spinner is stopped or is currently running.
Live Updates
Most spinners tie the ability to show new messages with the animation of the spinner. So if the spinner animates every 200ms, you can only show updated information every 200ms. If you wanted more frequent updates, you'd need to tradeoff the asthetics of the animation to display more data.
This spinner updates the printed information of the spinner immediately on change, without the animation updating. This allows you to use an animation speed that looks astheticaly pleasing, while also knowing the data presented to the user will be updated live.
You can see this in action in the following gif, where the filenames being uploaded are rendered independent of the spinner being animated:
Pausing for Updates
Sometimes you want to change a few settings, and don't want the spinner to render your partially applied configuration. If your spinner is running, and you want to change a few configuration items via method calls, you can Pause()
the spinner first. After making the changes you can call Unpause()
, and it will continue rendering like normal with the newly applied configuration.
Usage
go get github.com/theckman/yacspin
Within the yacspin
package there are some default spinners stored in the yacspin.CharSets
variable, and you can also provide your own. There is also a list of known colors in the yacspin.ValidColors
variable.
cfg := yacspin.Config{
Frequency: 100 * time.Millisecond,
CharSet: yacspin.CharSets[59],
Suffix: " backing up database to S3",
SuffixAutoColon: true,
Message: "exporting data",
StopCharacter: "✓",
StopColors: []string{"fgGreen"},
}
spinner, err := yacspin.New(cfg)
// handle the error
spinner.Start()
// doing some work
time.Sleep(2 * time.Second)
spinner.Message("uploading data")
// upload...
time.Sleep(2 * time.Second)
spinner.Stop()