A terminal spinner for tasks that have non-deterministic time frame.
Term::Screen provides an independent spinner component for crystal-term.
-
Add the dependency to your
shard.yml:dependencies: term-spinner: github: crystal-term/spinner
-
Run
shards install
First the library must be required:
require "term-spinner"Term::Spinner by default requires no arguments and defaults to the :classic formatter:
spinner = Term::Spinner.newIn addition you can provide a format message with a :spinner token where you would like the spinner to appear:
spinner = Term::Spinner.new("[:spinner] Loading ...", format: :pulse_2)
spinner.auto_spin # Automatic animation with default interval
sleep(2) # Perform task
spinner.stop("Done!") # Stop animationThis would produce an animation in your terminal that looks something like this:
[|] Loading ...and when finished:
[✖] Loading ... Done!This is the main workhorse of the spinner. Looping over the spin method will animate a given spinner.
loop do
spinner.spin
endThe auto_spin method performs automatic spinning in its own dedicated fiber.
spinner.auto_spin
The speed at which the spinning occurs is determined by the interval parameter, or defaults to the given format interval. The interval is a number in hertz, or (n * 100) milliseconds.
Pauses a spinner that's using auto_spin:
spinner.pauseResumes a paused spinner:
spinner.resumeUse run, passing a block with a job that will run and display the spinning animation. From within the block you can call stop, success, or error to finish the animation, or just allow the animation to finish when the block exits:
spinner.run do |spinner|
...
endOptionally you can pass a stop message to the run command to display when the animation is finished:
spinner.run("Done!") do |spinner|
...
endThe stop message passed to run is overridden if you use stop, success, or error.
To set a start time or reuse the same spinner after it has stopped, call start:
spinner.startIn order to stop the spinner, call stop. This will finish drawing the spinning animation and go to a new line:
spinner.stopYou can also pass a message to stop, which will be displayed after the animation:
spinner.stop("Done!")Use success to stop the spinning animation and replace the spinning symbol with success_mark (defaults to ✔) to indicate successful completion:
spinner = Term::Spinner.new("[:spinner] Task name")
spinner.success("(successful)")This will produce:
[✔] Task name (successful)Use error to stop the spinning animation and replace the spinning symbol with error_mark (defaults to ✖) to indicate a failed completion:
spinner = Term::Spinner.new("[:spinner] Task name")
spinner.error("(error)")This will produce:
[✖] Task name (error)Use update to dynamically change label name(s). Labels can be used like :spinner, allowing you to inject a value into a format string while the spinner is running.
Provide arbitrary token name(s) in the message string, such as :status:
spinner = TTY::Spinner.new("[:spinner] :status")and then pass token name and value:
spinner.update(status: "Downloading File 1")next start the animation:
spinner.run { ... }
# => | Downloading File 1Once the animation finishes, or even while it's still running, you can update the status to whatever you want:
spinner.update(status: "Downloading File 2")Reset the spinner to its initial state:
spinner.resetThese are the values that can be supplied to Term::Spinner.new to customize the behavior of the spinner.
Use one of the prefefined spinner styles by passing the formatting token :format:
spinner = Term::Spinner.new(format: :pulse_2)All accepted formats are located in /src/spinner/format.cr
If you want an example of each of the formats in action, you can try running /examples/formats.cr
You can always set your own custom frames using the frames option:
spinner = Term::Spinner.new(frames: [".", "o", "0", "@", "*"])The interval option accepts a number representing Hz. For instance, a value of 10, the default, will result in 10 animation frames per second. You can also pass in a Time::Span ti use as a delay between frames.
spinner = Term::Spinner.new(interval: 20) # 20 Hz (20 times per second)Hides cursors whens pinning animation is running. Defaults to false.
spinner = Term::Spinner.new(hide_cursor: true)After spinner is finished, clears its output. Defaults to false.
spinner = Term::Spinner.new(clear: true)To change marker indicating successful completion use the success_mark option:
spinner = Term::Spinner.new(success_mark: "+")To change marker indicating an error completion use the error_mark option:
spinner = Term::Spinner.new(error_mark: "x")The spinner only outputs to a console and when output is redirected to a file or a pipe it does nothing. This is so, for example, your error logs do not overflow with spinner output.
You can change where console output is streamed with output option:
spinner = Term::Spinner.new(output: STDOUT)Create and register a Term::Spinner under the multispinner
new_spinner = multi_spinner.register("[:spinner] Task 1 name", **options)
# or
# spinner = Term::Spinner.new("[:spinner] one")
# sp1 = multi_spinner.register(spinner)If no options are given it will use the options given to the multi_spinner when it was initialized to create the new spinner. If options are passed, they will override any options given to the top level spinner.
To create a top level spinner that tracks the activity of all the registered spinners, the multispinner has to be given a message on initialization:
spinners = Term::Spinner::Multi.new("[:spinner] Top level spinner")The top level multi spinner will perform spinning animation automatically when at least one of the registered spinners starts spinning.
If you register spinners without any tasks then you will have to manually control when the multispinner finishes by calling stop, success or error.
Alternatively, you can register spinners with tasks that will automatically animate and finish spinners when respective tasks are done.
The speed with which the spinning happens is determined by the :interval parameter. All the spinner formats have their default intervals specified.
In the case where you wish to have complete control over multiple spinners, you will need to perform all actions manually.
For example, create a multispinner that will track the status of all registered spinners:
spinners = Term::Spinner::Multi.new("[:spinner] top")and then register all spinners with their formats:
sp1 = spinners.register "[:spinner] one"
sp2 = spinners.register "[:spinner] two"Once registered, you can set spinners running in separate threads:
sp1.auto_spin
sp2.auto_spinFinally, you need to stop each spinner manually, in our case we mark the second spinner as failure which in turn will stop the top level multi spinner automatically and mark it as failure:
sp1.success
sp2.errorThe result may look like this:
┌ [✖] top
├── [✔] one
└── [✖] twoIn the case when you wish to execute async tasks and update individual spinners automatically, in any order, about their task status use #register and pass additional block parameter with the job to be executed.
For example, create a multi spinner that will track status of all registered spinners:
spinners = TTY::Spinner::Multi.new("[:spinner] top")and then register spinners with their respective tasks:
spinners.register("[:spinner] one") { |sp| sleep(2); sp.success("yes 2") }
spinners.register("[:spinner] two") { |sp| sleep(3); sp.error("no 2") }Finally, call #auto_spin to kick things off:
spinners.auto_spinIf any of the child spinners stop with an error then the top level spinner will be marked as failure.
In order to stop the multi spinner call stop. This will stop the top level spinner, if it exists, and any sub-spinners are still spinning.
spinners.stopUse success call to stop the spinning animation and replace the spinning symbol with a check mark character to indicate successful completion. This will also call #success on any sub-spinners that are still spinning.
spinners.successUse error call to stop the spinning animation and replace the spinning symbol with cross character to indicate error completion. This will also call #error on any sub-spinners that are still spinning.
spinners.errorIn addition to all the standard Spinner configuration options you can style a multispinner like so:
spinner = Term::Spinner::Multi.new("[:spinner] parent", style: {
top: ". "
middle: "|-> "
bottom: "|__ "
})Term::Spinner emits :done, :success, and :error events. You can listen for these using the #on method.
spinner.on(:done) { ... }
spinner.on(:error) { ... }
spinner.on(:success) { ... }- Fork it (https://github.com/watzon/spinner/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
- Chris Watson - creator and maintainer

