275 lines
20 KiB
Markdown
275 lines
20 KiB
Markdown
# Yet Another CLi Spinner (for Go)
|
|
[](https://github.com/theckman/yacspin/blob/master/LICENSE)
|
|
[](https://godoc.org/github.com/theckman/yacspin)
|
|
[](https://github.com/theckman/yacspin/releases)
|
|
[](https://github.com/theckman/yacspin/actions/workflows/tests.yaml)
|
|
[](https://goreportcard.com/report/github.com/theckman/yacspin)
|
|
[](https://codecov.io/gh/theckman/yacspin)
|
|
|
|
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?
|
|
This project was created after it was realized that the most popular spinner
|
|
library for Go had some limitations, that couldn't be fixed without a massive
|
|
overhaul of the API.
|
|
|
|
The other spinner ties the ability to show updated messages 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. This means you need to trade off
|
|
animation aesthetics to show "realtime" information. It was a goal to avoid this
|
|
problem.
|
|
|
|
In addition, there were also some API design choices that have made it unsafe
|
|
for concurrent use, which presents challenges when trying to update the text in
|
|
the spinner while it's animating. This could result in undefined behavior due to
|
|
data races.
|
|
|
|
There were also some variable-width spinners in that other project that did
|
|
not render correctly. Because the width of the spinner animation would change,
|
|
so would the position of the message on the screen. `yacspin` uses a dynamic
|
|
width when animating, so your message should appear static relative to the
|
|
animating spinner.
|
|
|
|
Finally, there was 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 changes needed above to support in an intuitive way.
|
|
|
|
This project takes inspiration from that other project, and takes a new approach
|
|
to address the challenges above.
|
|
|
|
## Features
|
|
#### Provided Spinners
|
|
There are over 90 spinners available in the `CharSets` package variable. They
|
|
were borrowed from [github.com/briandowns/spinner](https://github.com/briandowns/spinner).
|
|
There is a table with most of the spinners [at the bottom of this README](#Spinners).
|
|
|
|
#### Dynamic Width of Animation
|
|
Because of how some spinners are animated, they may have different widths are
|
|
different times in the animation. `yacspin` 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 spinner's methods.
|
|
|
|
By doing this you can use a single `yacspin` spinner to display the status of a
|
|
list of tasks being executed serially.
|
|
|
|
##### Stop
|
|

|
|
|
|
##### StopFail
|
|

|
|
|
|
#### Animation At End of Line
|
|
The `SpinnerAtEnd` field of the `Config` struct allows you to specify whether
|
|
the spinner is rendered at the end of the line instead of the beginning. The
|
|
default value (`false`) results in the spinner being rendered at the beginning
|
|
of the line.
|
|
|
|
#### 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 animating.
|
|
|
|
#### 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.
|
|
|
|
#### Supporting Non-Interactive (TTY) Output Targets
|
|
`yacspin` also has native support for non-interactive (TTY) output targets. By
|
|
default this is detected in the constructor, or can be overriden via the
|
|
`TerminalMode` `Config` struct field. When detecting the application is not
|
|
running withn a TTY session, the behavior of the spinner is different.
|
|
|
|
Specifically, when this is automatically detected the spinner no longer uses
|
|
colors, disables the automatic spinner animation, and instead only animates the
|
|
spinner when updating the message. In addition, each animation is rendered on a
|
|
new line instead of overwriting the current line.
|
|
|
|
This should result in human-readable output without any changes needed by
|
|
consumers, even when the system is writing to a non-TTY destination.
|
|
|
|
#### Manually Stepping Animation
|
|
If you'd like to manually animate the spinner, you can do so by setting the
|
|
`TerminalMode` to `ForceNoTTYMode | ForceSmartTerminalMode`. In this mode the
|
|
spinner will still use colors and other text stylings, but the animation only
|
|
happens when data is updated and on individual lines. You can accomplish this by
|
|
calling the `Message()` method with the same used previously.
|
|
|
|
## 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.
|
|
|
|
### Example
|
|
|
|
There are runnable examples in the [examples/](https://github.com/theckman/yacspin/tree/master/examples)
|
|
directory, with one simple example and one more advanced one. Here is a quick
|
|
snippet showing usage from a very high level, with error handling omitted:
|
|
|
|
```Go
|
|
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
|
|
|
|
err = spinner.Start()
|
|
|
|
// doing some work
|
|
time.Sleep(2 * time.Second)
|
|
|
|
spinner.Message("uploading data")
|
|
|
|
// upload...
|
|
time.Sleep(2 * time.Second)
|
|
|
|
err = spinner.Stop()
|
|
```
|
|
|
|
## Spinners
|
|
|
|
The spinner animations below are recorded at a refresh frequency of 200ms. Some
|
|
animations may look better at a different speed, so play around with the
|
|
frequency until you find a value you find aesthetically pleasing.
|
|
|
|
yacspin.CharSets index | sample gif (Frequency: 200ms)
|
|
-----------------------|------------------------------
|
|
0 | 
|
|
1 | 
|
|
2 | 
|
|
3 | 
|
|
4 | 
|
|
5 | 
|
|
6 | 
|
|
7 | 
|
|
8 | 
|
|
9 | 
|
|
10 | 
|
|
11 | 
|
|
12 | 
|
|
13 | 
|
|
14 | 
|
|
15 | 
|
|
16 | 
|
|
17 | 
|
|
18 | 
|
|
19 | 
|
|
20 | 
|
|
21 | 
|
|
22 | 
|
|
23 | 
|
|
24 | 
|
|
25 | 
|
|
26 | 
|
|
27 | 
|
|
28 | 
|
|
29 | 
|
|
30 | 
|
|
31 | 
|
|
32 | 
|
|
33 | 
|
|
34 | 
|
|
35 | 
|
|
36 | 
|
|
37 | 
|
|
38 | 
|
|
39 | 
|
|
40 | 
|
|
41 | 
|
|
42 | 
|
|
43 | 
|
|
44 | 
|
|
45 | 
|
|
46 | 
|
|
47 | 
|
|
48 | 
|
|
49 | 
|
|
50 | 
|
|
51 | 
|
|
52 | 
|
|
53 | 
|
|
54 | 
|
|
55 | 
|
|
56 | 
|
|
57 | 
|
|
58 | 
|
|
59 | 
|
|
60 | 
|
|
61 | 
|
|
62 | 
|
|
63 | 
|
|
64 | 
|
|
65 | 
|
|
66 | 
|
|
67 | 
|
|
68 | 
|
|
69 | 
|
|
70 | 
|
|
71 | 
|
|
72 | 
|
|
73 | 
|
|
74 | 
|
|
75 | 
|
|
76 | 
|
|
77 | 
|
|
78 | 
|
|
79 | 
|
|
80 | 
|
|
81 | 
|
|
82 | 
|
|
83 | 
|
|
84 | 
|
|
85 | 
|
|
86 | 
|
|
87 | 
|
|
88 | 
|
|
89 | 
|
|
90 | 
|