[
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test\n\non: [push, pull_request]\n\njobs:\n  test:\n    name: Ruby ${{ matrix.ruby }} (${{ matrix.os }})\n    if: \"!contains(github.event.head_commit.message, '[skip ci]')\"\n    strategy:\n      matrix:\n        ruby:\n        - '4.0'\n        - '3.4'\n        - '3.3'\n        - '3.2'\n        - '3.1'\n        - '3.0'\n        - 'jruby'\n        os:\n        - ubuntu-latest\n        - macos-latest\n    runs-on: ${{matrix.os}}\n    steps:\n    - uses: actions/checkout@v6\n    - name: Set up Ruby\n      uses: ruby/setup-ruby@v1\n      with:\n        ruby-version: ${{matrix.ruby}}\n        bundler-cache: true\n    - name: Run tests\n      run: bundle exec rake\n\n  # test-windows:\n  #   name: Ruby ${{ matrix.ruby }} (windows-latest)\n  #   if: \"!contains(github.event.head_commit.message, '[skip ci]')\"\n  #   strategy:\n  #     matrix:\n  #       ruby:\n  #       - 3.0\n  #       - 2.7\n  #       - 2.6\n  #   runs-on: windows-latest\n  #   steps:\n  #   - uses: actions/checkout@v2\n  #   - name: Set up Ruby\n  #     uses: ruby/setup-ruby@v1\n  #     with:\n  #       ruby-version: ${{matrix.ruby}}\n  #       bundler-cache: true\n  #   - run: cinst ansicon\n  #   - name: Run tests\n  #     run: bundle exec rake\n"
  },
  {
    "path": ".gitignore",
    "content": "Gemfile.lock\n/pkg\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"data/external/cli-spinners\"]\n\tpath = data/external/cli-spinners\n\turl = https://github.com/sindresorhus/cli-spinners.git\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## CHANGELOG\n\n### 0.4.0\n\n- Allow Ruby 4.x\n- Add json (default) gem as dependency\n- Update CLI spinners to 3.3.0\n\n### 0.3.0\n\n- Allow more recent versions of Ruby and unicode-display_width gem\n- Update CLI spinners to 2.6.0\n\n### 0.2.6\n\n- Update CLI spinners to 1.1.0 (adds \"weather\" and \"christmas\")\n\n### 0.2.5\n\n- Update CLI spinners to 1.0.1\n\n### 0.2.4\n\n- Fix bug that the Whirly thread will also stop when main thread throws error\n  (patch by @monkbroc)\n- New spinner: xberg\n\n### 0.2.3\n\n- Fix bug that in some cases whirly output would be shown on non-ttys\n- New spinners: card, cloud, photo, banknote, white_square\n\n### 0.2.2\n\n- More emotions for whirly (the spinner)\n- Add cat spinner\n\n### 0.2.1\n\n- Use macOS terminal app compatible ANSI sequences\n\n### 0.2.0\n\n- Make paint dependency optional\n- Remove pause feature\n- Separate configuring into its own method, remember whirly's configuration, can be cleared with the new .reset method\n- Introduce \"stop\" frames to display when spinner is over\n- Different newline behaviour; append newline by default after spinner ran. Use position: \"below\" for old behaviour\n- Support multiple frame modes: \"linear\", \"random\", \"reverse\", \"swing\"\n- Proper unrendering (use unicode-display\\_width)\n- Introduce spinner packs (to deal with eventual name conflicts, currently: whirly + cli)\n- Add more bundled spinners\n- Update CLI spinners to v0.3.0 (two new spinners)\n- Rename option :use\\_color to just :color\n- Option to set spinner can also take frames or proc directly\n- Add ANSI escape mode option\n- Add remove\\_after\\_stop option\n\n### 0.1.1\n\n- `non_tty` option to force TTY behaviour (whirly deactivates itself for non TTY by default)\n- Allow passing in spinner hashes instead of only spinner names\n\n### 0.1.0\n\n- Initial release\n\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\nadvances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at opensource@janlelis.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\ngemspec\n\ngem 'minitest'\ngem 'paint'\ngem 'rake'\ngem 'stringio'\n"
  },
  {
    "path": "MIT-LICENSE.txt",
    "content": "Copyright (c) 2016 Jan Lelis, https://janlelis.com\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Whirly 😀 [![[version]](https://badge.fury.io/rb/whirly.svg)](https://badge.fury.io/rb/whirly)  [<img src=\"https://github.com/janlelis/whirly/workflows/Test/badge.svg\" />](https://github.com/janlelis/whirly/actions/workflows/test.yml)\n\nA simple, colorful and customizable terminal spinner library for Ruby. It comes with 24 custom spinners and also includes those from the [cli-spinners](https://github.com/sindresorhus/cli-spinners) project.\n\n## Demonstration\n\n![](whirly.gif)\n\n### Bundled Whirly Spinners\n\n[Play on asciinema](https://asciinema.org/a/88198?size=big)\n\n### Bundled Spinners from CLI Spinners\n\n![](https://raw.githubusercontent.com/sindresorhus/cli-spinners/main/screenshot.gif)\n\n[Play on asciinema](https://asciinema.org/a/9mlcoussb137m32swwuqtb2p1?size=big)\n\n## Setup\n\nAdd to your `Gemfile`:\n\n```ruby\ngem 'whirly'\ngem 'paint' # makes whirly colorful (recommended)\n```\n\n## Usage\n\n### Basic Usage\n\nThe spinner is shown while the block executes:\n\n```ruby\nWhirly.start do\n  # do the heavy work here\n  sleep 5\nend\n```\n\nYou can update the spinner text from inside the block:\n\n```ruby\nWhirly.start do\n  Whirly.status = \"Set some text to display alongside the spinner symbol\"\n  sleep 3\n  Whirly.status = \"Update it\"\n  sleep 2\nend\n```\n\nIf you want to avoid the block syntax, you can also stop it manually:\n\n```ruby\nWhirly.start\nsleep 5\nWhirly.stop\n```\n\nThe `start` method takes a lot of options, like which spinner to use or an initial status. See further below for the full description of available options.\n\n```ruby\nWhirly.start spinner: \"pong\", color: false, status: \"The Game of Pong\" do\n  sleep 10\nend\n```\n\nAlso see the [examples directory](https://github.com/janlelis/whirly/tree/main/examples) for example scripts.\n\n### Configuring Whirly\n\nYou can pass the same options you would pass to `.start` to `.configure` instead to create a persistent configuration that will be used by `.start`:\n\n```ruby\nWhirly.configure spinner: \"dots\"\n\nWhirly.start do\n  sleep 3 # will use dots\nend\n\nWhirly.start do\n  sleep 3 # will use dots again\nend\n```\n\nCall `.reset` to restore unconfigured behaviour:\n\n```ruby\nWhirly.configure spinner: \"dots\"\n\nWhirly.reset\n\nWhirly.start do\n  sleep 3 # will use default spinner\nend\n```\n\n## Spinners\n\n### Included Spinners\n\nSee [`data/whirly-static-spinnes.json`](https://github.com/janlelis/whirly/blob/main/data/whirly-static-spinners.json), [`lib/whirly/spinners/whirly.rb`](https://github.com/janlelis/whirly/blob/main/lib/whirly/spinners/whirly.rb) and [cli-spinners](https://github.com/sindresorhus/cli-spinners). You can get a demonstration of all bundled spinners by running the [`examples/all_spinners.rb`](https://github.com/janlelis/whirly/blob/main/examples/all_spinners.rb) script.\n\n## All `Whirly.start` / `Whirly.configure` Configuration Options\n\n### Main Options\n\n#### `spinner:`\n\n*Default:* `\"whirly\"`\n\nYou have multiple ways of telling *Whirly* which spinner should be used. You can pass the following to the `spinner:` option:\n\n- The name of a bundled spinner\n- An array of spinner frames to use\n- A proc which generates the frames dynamically\n- A full spinner hash object ([explained below](https://github.com/janlelis/whirly#full-spinner-hash-format))\n\n#### `status:`\n\n*Default:* None\n\nAllows you to directly set the first status text to display alongside the spinner icon.\n\n#### `interval:`\n\n*Default:* `100`\n\nThe number of milliseconds between changing to the next spinner icon frame.\n\n### Advanced Options\n\n#### `ambiguous_characters_width:`\n\n*Default:* `1`\n\nIf set to `2`, ambiguous Unicode charatcers will be treated as 2 colums wide. See [unicode-display_width](https://github.com/janlelis/unicode-display_width) for more details.\n\n#### `ansi_escape_mode:`\n\n*Default:* `\"restore\"`\n\nCan be set to `\"line\"` to use an different way of producing ANSI escape sequences necessary (experimental).\n\n#### `append_newline:`\n\n*Default:* `true`\n\nWhen the Whirly block is over (or `.stop` was called), a `\"\\n\"` will be outputted. Change to `false` to prevent this.\n\n#### `color:`\n\n*Default:* `!!defined?(Paint)`\n\nThis option is responsible for displaying the spinner icon in random colors. Set to `false` if you do not want this. Related option `:color_change_rate`.\n\n#### `color_change_rate:`\n\n*Default:* `30`\n\nA value which describes how fast the color of the spinner icon changes.\n\n#### `hide_cursor:`\n\n*Default:* `true`\n\nBy default, the terminal cursor gets hidden while displaying the spinner. This also registers an `at_exit` callback, which always restores the cursor when exitting the program. If you do not want to hide the cursor, change this option to `false`.\n\n#### `mode:`\n\n*Default:* `\"linear\"`\n\nInstructs Whirly to play the frames in a different order. Possible values: `\"linear\"`, `\"reverse\"`, `\"swing\"`, and `\"random\"`. See [spinner format section](https://github.com/janlelis/whirly#mode-1) for more details.\n\n#### `non_tty:`\n\n*Default:* `false`\n\nWhirly only gets activated if the current process appears to be a real terminal. If you want to activate it for non-terimnals, set this option to `true`.\n\n#### `position:`\n\n*Default:* `\"normal\"`\n\nYou can set this to `\"below\"` to let Whirly appear one line below its normal position.\n\n#### `remove_after_stop:`\n\n*Default:* `false`\n\nCauses the last frame to be removed after the spinner stopped.\n\n#### `stop:`\n\n*Default:* None\n\nYou can pass a custom frame to be used to end the animation, for example:\n\n```ruby\nWhirly.start spinner: \"clock\", interval: 1000, stop: \"⏰\" do\n  sleep 12\nend\n```\n\n#### `spinner_packs:`\n\n*Default:* `[:whirly, :cli]`\n\nWhirly comes with spinners from different sources. This options defines which sources to consider (the value refers to an uppercased child constant of `Whirly::Spinners`) and in which order.\n\n#### `stream:`\n\n*Default:* `$stdout`\n\nYou can pass in an [IO](https://ruby-doc.org/core/IO.html)-like object, if you want to display *Whirly* on an other stream than `$stdout`.\n\n## Full Spinner Hash Format\n\nA full spinner is defined by a hash which can have the following key-value pairs. Please note that in order to keep the format more portable, all keys are strings and not Ruby symbols. Except for `\"frames\"` and `\"proc\"`, all options are overwritable when starting/configuring Whirly. See the included spinners for example definitions of spinners.\n\n### `\"frames\"`\n\nAn [Array](https://ruby-doc.org/core/Array.html) or [Enumerable](https://ruby-doc.org/core/Enumerable.html) of strings that will be used as the spinner icon.\n\n### `\"proc\"`\n\nInstead of using `\"frames\"`: A proc which will generate the next frame with each call.\n\n### `\"interval\"`\n\nThe number of milliseconds between changing to the next spinner icon frame.\n\n### `\"mode\"`\n\nThe order in which frames should be played. It can be one of the following:\n\n- `\"linear\"`: Cycle through all frames in normal order\n- `\"reverse\"`: Cycle through all frames in reverse order\n- `\"swing\"`: Cycle through all frames in normal order, and then in reverse order, but only play first and last frame once each round\n- `\"random\"`: Play random frames\n\nPlease note: While `\"linear\"` also works with frames that are just an [Enumerable](https://ruby-doc.org/core/Enumerable.html), all other frame modes require the object to be representable as an [Array](https://ruby-doc.org/core/Array.html).\n\n### `\"stop\"`\n\nA frame to be used to end the spinner icon animation.\n\n## Remarks, Troubleshooting, Caveats\n\n- Interval is milliseconds, but don't rely on exact timing\n- Will not do anything if stream is not a real console (or `non_tty: true` is passed)\n- Colors not working? Be sure to include the [paint](https://github.com/janlelis/paint/) gem in your Gemfile\n- Don't set very short intervals (or it might affect performance substantially)\n\n## MIT License\n\n- Copyright (C) 2016 Jan Lelis <https://janlelis.com>. Released under the MIT license.\n- Contains data from cli-spinners:  MIT License, Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)\n"
  },
  {
    "path": "Rakefile",
    "content": "# # #\n# Get gemspec info\n\ngemspec_file = Dir['*.gemspec'].first\ngemspec = eval File.read(gemspec_file), binding, gemspec_file\ninfo = \"#{gemspec.name} | #{gemspec.version} | \" \\\n       \"#{gemspec.runtime_dependencies.size} dependencies | \" \\\n       \"#{gemspec.files.size} files\"\n\n\n# # #\n# Gem build and install task\n\ndesc info\ntask :gem do\n  puts info + \"\\n\\n\"\n  print \"  \"; sh \"gem build #{gemspec_file}\"\n  FileUtils.mkdir_p 'pkg'\n  FileUtils.mv \"#{gemspec.name}-#{gemspec.version}.gem\", 'pkg'\n  puts; sh %{gem install --no-document pkg/#{gemspec.name}-#{gemspec.version}.gem}\nend\n\n\n# # #\n# Start an IRB session with the gem loaded\n\ndesc \"#{gemspec.name} | IRB\"\ntask :irb do\n  sh \"irb -I ./lib -r #{gemspec.name.gsub '-','/'}\"\nend\n\n\n# # #\n# Run Specs\n\ndesc \"#{gemspec.name} | Spec\"\ntask :spec do\n  ruby \"spec/whirly_spec.rb\"\nend\ntask default: :spec\n\n\n# # #\n# Update spinners\n\ndesc \"Update spinners\"\ntask :update_spinners do\n  sh \"git submodule update --recursive --remote\"\n  cp \"data/external/cli-spinners/spinners.json\", \"data/cli-spinners.json\"\nend\n\n\n# # #\n# Record ASCIICAST\n\ndesc \"Record an asciicast via asciinema\"\ntask :record_acsiicast do\n  sh \"cd && asciinema rec whirly-bundled-spinners-v0.2.0.json --title='Whirly v0.2.0 Bundled Spinners' --command='ruby #{File.dirname(__FILE__)}/examples/asciinema_bundled_spinners.rb'\"\nend\n\n"
  },
  {
    "path": "data/cli-spinners.json",
    "content": "{\n\t\"dots\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠋\",\n\t\t\t\"⠙\",\n\t\t\t\"⠹\",\n\t\t\t\"⠸\",\n\t\t\t\"⠼\",\n\t\t\t\"⠴\",\n\t\t\t\"⠦\",\n\t\t\t\"⠧\",\n\t\t\t\"⠇\",\n\t\t\t\"⠏\"\n\t\t]\n\t},\n\t\"dots2\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⣾\",\n\t\t\t\"⣽\",\n\t\t\t\"⣻\",\n\t\t\t\"⢿\",\n\t\t\t\"⡿\",\n\t\t\t\"⣟\",\n\t\t\t\"⣯\",\n\t\t\t\"⣷\"\n\t\t]\n\t},\n\t\"dots3\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠋\",\n\t\t\t\"⠙\",\n\t\t\t\"⠚\",\n\t\t\t\"⠞\",\n\t\t\t\"⠖\",\n\t\t\t\"⠦\",\n\t\t\t\"⠴\",\n\t\t\t\"⠲\",\n\t\t\t\"⠳\",\n\t\t\t\"⠓\"\n\t\t]\n\t},\n\t\"dots4\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠄\",\n\t\t\t\"⠆\",\n\t\t\t\"⠇\",\n\t\t\t\"⠋\",\n\t\t\t\"⠙\",\n\t\t\t\"⠸\",\n\t\t\t\"⠰\",\n\t\t\t\"⠠\",\n\t\t\t\"⠰\",\n\t\t\t\"⠸\",\n\t\t\t\"⠙\",\n\t\t\t\"⠋\",\n\t\t\t\"⠇\",\n\t\t\t\"⠆\"\n\t\t]\n\t},\n\t\"dots5\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠋\",\n\t\t\t\"⠙\",\n\t\t\t\"⠚\",\n\t\t\t\"⠒\",\n\t\t\t\"⠂\",\n\t\t\t\"⠂\",\n\t\t\t\"⠒\",\n\t\t\t\"⠲\",\n\t\t\t\"⠴\",\n\t\t\t\"⠦\",\n\t\t\t\"⠖\",\n\t\t\t\"⠒\",\n\t\t\t\"⠐\",\n\t\t\t\"⠐\",\n\t\t\t\"⠒\",\n\t\t\t\"⠓\",\n\t\t\t\"⠋\"\n\t\t]\n\t},\n\t\"dots6\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠁\",\n\t\t\t\"⠉\",\n\t\t\t\"⠙\",\n\t\t\t\"⠚\",\n\t\t\t\"⠒\",\n\t\t\t\"⠂\",\n\t\t\t\"⠂\",\n\t\t\t\"⠒\",\n\t\t\t\"⠲\",\n\t\t\t\"⠴\",\n\t\t\t\"⠤\",\n\t\t\t\"⠄\",\n\t\t\t\"⠄\",\n\t\t\t\"⠤\",\n\t\t\t\"⠴\",\n\t\t\t\"⠲\",\n\t\t\t\"⠒\",\n\t\t\t\"⠂\",\n\t\t\t\"⠂\",\n\t\t\t\"⠒\",\n\t\t\t\"⠚\",\n\t\t\t\"⠙\",\n\t\t\t\"⠉\",\n\t\t\t\"⠁\"\n\t\t]\n\t},\n\t\"dots7\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠈\",\n\t\t\t\"⠉\",\n\t\t\t\"⠋\",\n\t\t\t\"⠓\",\n\t\t\t\"⠒\",\n\t\t\t\"⠐\",\n\t\t\t\"⠐\",\n\t\t\t\"⠒\",\n\t\t\t\"⠖\",\n\t\t\t\"⠦\",\n\t\t\t\"⠤\",\n\t\t\t\"⠠\",\n\t\t\t\"⠠\",\n\t\t\t\"⠤\",\n\t\t\t\"⠦\",\n\t\t\t\"⠖\",\n\t\t\t\"⠒\",\n\t\t\t\"⠐\",\n\t\t\t\"⠐\",\n\t\t\t\"⠒\",\n\t\t\t\"⠓\",\n\t\t\t\"⠋\",\n\t\t\t\"⠉\",\n\t\t\t\"⠈\"\n\t\t]\n\t},\n\t\"dots8\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠁\",\n\t\t\t\"⠁\",\n\t\t\t\"⠉\",\n\t\t\t\"⠙\",\n\t\t\t\"⠚\",\n\t\t\t\"⠒\",\n\t\t\t\"⠂\",\n\t\t\t\"⠂\",\n\t\t\t\"⠒\",\n\t\t\t\"⠲\",\n\t\t\t\"⠴\",\n\t\t\t\"⠤\",\n\t\t\t\"⠄\",\n\t\t\t\"⠄\",\n\t\t\t\"⠤\",\n\t\t\t\"⠠\",\n\t\t\t\"⠠\",\n\t\t\t\"⠤\",\n\t\t\t\"⠦\",\n\t\t\t\"⠖\",\n\t\t\t\"⠒\",\n\t\t\t\"⠐\",\n\t\t\t\"⠐\",\n\t\t\t\"⠒\",\n\t\t\t\"⠓\",\n\t\t\t\"⠋\",\n\t\t\t\"⠉\",\n\t\t\t\"⠈\",\n\t\t\t\"⠈\"\n\t\t]\n\t},\n\t\"dots9\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⢹\",\n\t\t\t\"⢺\",\n\t\t\t\"⢼\",\n\t\t\t\"⣸\",\n\t\t\t\"⣇\",\n\t\t\t\"⡧\",\n\t\t\t\"⡗\",\n\t\t\t\"⡏\"\n\t\t]\n\t},\n\t\"dots10\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⢄\",\n\t\t\t\"⢂\",\n\t\t\t\"⢁\",\n\t\t\t\"⡁\",\n\t\t\t\"⡈\",\n\t\t\t\"⡐\",\n\t\t\t\"⡠\"\n\t\t]\n\t},\n\t\"dots11\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"⠁\",\n\t\t\t\"⠂\",\n\t\t\t\"⠄\",\n\t\t\t\"⡀\",\n\t\t\t\"⢀\",\n\t\t\t\"⠠\",\n\t\t\t\"⠐\",\n\t\t\t\"⠈\"\n\t\t]\n\t},\n\t\"dots12\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⢀⠀\",\n\t\t\t\"⡀⠀\",\n\t\t\t\"⠄⠀\",\n\t\t\t\"⢂⠀\",\n\t\t\t\"⡂⠀\",\n\t\t\t\"⠅⠀\",\n\t\t\t\"⢃⠀\",\n\t\t\t\"⡃⠀\",\n\t\t\t\"⠍⠀\",\n\t\t\t\"⢋⠀\",\n\t\t\t\"⡋⠀\",\n\t\t\t\"⠍⠁\",\n\t\t\t\"⢋⠁\",\n\t\t\t\"⡋⠁\",\n\t\t\t\"⠍⠉\",\n\t\t\t\"⠋⠉\",\n\t\t\t\"⠋⠉\",\n\t\t\t\"⠉⠙\",\n\t\t\t\"⠉⠙\",\n\t\t\t\"⠉⠩\",\n\t\t\t\"⠈⢙\",\n\t\t\t\"⠈⡙\",\n\t\t\t\"⢈⠩\",\n\t\t\t\"⡀⢙\",\n\t\t\t\"⠄⡙\",\n\t\t\t\"⢂⠩\",\n\t\t\t\"⡂⢘\",\n\t\t\t\"⠅⡘\",\n\t\t\t\"⢃⠨\",\n\t\t\t\"⡃⢐\",\n\t\t\t\"⠍⡐\",\n\t\t\t\"⢋⠠\",\n\t\t\t\"⡋⢀\",\n\t\t\t\"⠍⡁\",\n\t\t\t\"⢋⠁\",\n\t\t\t\"⡋⠁\",\n\t\t\t\"⠍⠉\",\n\t\t\t\"⠋⠉\",\n\t\t\t\"⠋⠉\",\n\t\t\t\"⠉⠙\",\n\t\t\t\"⠉⠙\",\n\t\t\t\"⠉⠩\",\n\t\t\t\"⠈⢙\",\n\t\t\t\"⠈⡙\",\n\t\t\t\"⠈⠩\",\n\t\t\t\"⠀⢙\",\n\t\t\t\"⠀⡙\",\n\t\t\t\"⠀⠩\",\n\t\t\t\"⠀⢘\",\n\t\t\t\"⠀⡘\",\n\t\t\t\"⠀⠨\",\n\t\t\t\"⠀⢐\",\n\t\t\t\"⠀⡐\",\n\t\t\t\"⠀⠠\",\n\t\t\t\"⠀⢀\",\n\t\t\t\"⠀⡀\"\n\t\t]\n\t},\n\t\"dots13\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⣼\",\n\t\t\t\"⣹\",\n\t\t\t\"⢻\",\n\t\t\t\"⠿\",\n\t\t\t\"⡟\",\n\t\t\t\"⣏\",\n\t\t\t\"⣧\",\n\t\t\t\"⣶\"\n\t\t]\n\t},\n\t\"dots14\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠉⠉\",\n\t\t\t\"⠈⠙\",\n\t\t\t\"⠀⠹\",\n\t\t\t\"⠀⢸\",\n\t\t\t\"⠀⣰\",\n\t\t\t\"⢀⣠\",\n\t\t\t\"⣀⣀\",\n\t\t\t\"⣄⡀\",\n\t\t\t\"⣆⠀\",\n\t\t\t\"⡇⠀\",\n\t\t\t\"⠏⠀\",\n\t\t\t\"⠋⠁\"\n\t\t]\n\t},\n\t\"dots8Bit\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠀\",\n\t\t\t\"⠁\",\n\t\t\t\"⠂\",\n\t\t\t\"⠃\",\n\t\t\t\"⠄\",\n\t\t\t\"⠅\",\n\t\t\t\"⠆\",\n\t\t\t\"⠇\",\n\t\t\t\"⡀\",\n\t\t\t\"⡁\",\n\t\t\t\"⡂\",\n\t\t\t\"⡃\",\n\t\t\t\"⡄\",\n\t\t\t\"⡅\",\n\t\t\t\"⡆\",\n\t\t\t\"⡇\",\n\t\t\t\"⠈\",\n\t\t\t\"⠉\",\n\t\t\t\"⠊\",\n\t\t\t\"⠋\",\n\t\t\t\"⠌\",\n\t\t\t\"⠍\",\n\t\t\t\"⠎\",\n\t\t\t\"⠏\",\n\t\t\t\"⡈\",\n\t\t\t\"⡉\",\n\t\t\t\"⡊\",\n\t\t\t\"⡋\",\n\t\t\t\"⡌\",\n\t\t\t\"⡍\",\n\t\t\t\"⡎\",\n\t\t\t\"⡏\",\n\t\t\t\"⠐\",\n\t\t\t\"⠑\",\n\t\t\t\"⠒\",\n\t\t\t\"⠓\",\n\t\t\t\"⠔\",\n\t\t\t\"⠕\",\n\t\t\t\"⠖\",\n\t\t\t\"⠗\",\n\t\t\t\"⡐\",\n\t\t\t\"⡑\",\n\t\t\t\"⡒\",\n\t\t\t\"⡓\",\n\t\t\t\"⡔\",\n\t\t\t\"⡕\",\n\t\t\t\"⡖\",\n\t\t\t\"⡗\",\n\t\t\t\"⠘\",\n\t\t\t\"⠙\",\n\t\t\t\"⠚\",\n\t\t\t\"⠛\",\n\t\t\t\"⠜\",\n\t\t\t\"⠝\",\n\t\t\t\"⠞\",\n\t\t\t\"⠟\",\n\t\t\t\"⡘\",\n\t\t\t\"⡙\",\n\t\t\t\"⡚\",\n\t\t\t\"⡛\",\n\t\t\t\"⡜\",\n\t\t\t\"⡝\",\n\t\t\t\"⡞\",\n\t\t\t\"⡟\",\n\t\t\t\"⠠\",\n\t\t\t\"⠡\",\n\t\t\t\"⠢\",\n\t\t\t\"⠣\",\n\t\t\t\"⠤\",\n\t\t\t\"⠥\",\n\t\t\t\"⠦\",\n\t\t\t\"⠧\",\n\t\t\t\"⡠\",\n\t\t\t\"⡡\",\n\t\t\t\"⡢\",\n\t\t\t\"⡣\",\n\t\t\t\"⡤\",\n\t\t\t\"⡥\",\n\t\t\t\"⡦\",\n\t\t\t\"⡧\",\n\t\t\t\"⠨\",\n\t\t\t\"⠩\",\n\t\t\t\"⠪\",\n\t\t\t\"⠫\",\n\t\t\t\"⠬\",\n\t\t\t\"⠭\",\n\t\t\t\"⠮\",\n\t\t\t\"⠯\",\n\t\t\t\"⡨\",\n\t\t\t\"⡩\",\n\t\t\t\"⡪\",\n\t\t\t\"⡫\",\n\t\t\t\"⡬\",\n\t\t\t\"⡭\",\n\t\t\t\"⡮\",\n\t\t\t\"⡯\",\n\t\t\t\"⠰\",\n\t\t\t\"⠱\",\n\t\t\t\"⠲\",\n\t\t\t\"⠳\",\n\t\t\t\"⠴\",\n\t\t\t\"⠵\",\n\t\t\t\"⠶\",\n\t\t\t\"⠷\",\n\t\t\t\"⡰\",\n\t\t\t\"⡱\",\n\t\t\t\"⡲\",\n\t\t\t\"⡳\",\n\t\t\t\"⡴\",\n\t\t\t\"⡵\",\n\t\t\t\"⡶\",\n\t\t\t\"⡷\",\n\t\t\t\"⠸\",\n\t\t\t\"⠹\",\n\t\t\t\"⠺\",\n\t\t\t\"⠻\",\n\t\t\t\"⠼\",\n\t\t\t\"⠽\",\n\t\t\t\"⠾\",\n\t\t\t\"⠿\",\n\t\t\t\"⡸\",\n\t\t\t\"⡹\",\n\t\t\t\"⡺\",\n\t\t\t\"⡻\",\n\t\t\t\"⡼\",\n\t\t\t\"⡽\",\n\t\t\t\"⡾\",\n\t\t\t\"⡿\",\n\t\t\t\"⢀\",\n\t\t\t\"⢁\",\n\t\t\t\"⢂\",\n\t\t\t\"⢃\",\n\t\t\t\"⢄\",\n\t\t\t\"⢅\",\n\t\t\t\"⢆\",\n\t\t\t\"⢇\",\n\t\t\t\"⣀\",\n\t\t\t\"⣁\",\n\t\t\t\"⣂\",\n\t\t\t\"⣃\",\n\t\t\t\"⣄\",\n\t\t\t\"⣅\",\n\t\t\t\"⣆\",\n\t\t\t\"⣇\",\n\t\t\t\"⢈\",\n\t\t\t\"⢉\",\n\t\t\t\"⢊\",\n\t\t\t\"⢋\",\n\t\t\t\"⢌\",\n\t\t\t\"⢍\",\n\t\t\t\"⢎\",\n\t\t\t\"⢏\",\n\t\t\t\"⣈\",\n\t\t\t\"⣉\",\n\t\t\t\"⣊\",\n\t\t\t\"⣋\",\n\t\t\t\"⣌\",\n\t\t\t\"⣍\",\n\t\t\t\"⣎\",\n\t\t\t\"⣏\",\n\t\t\t\"⢐\",\n\t\t\t\"⢑\",\n\t\t\t\"⢒\",\n\t\t\t\"⢓\",\n\t\t\t\"⢔\",\n\t\t\t\"⢕\",\n\t\t\t\"⢖\",\n\t\t\t\"⢗\",\n\t\t\t\"⣐\",\n\t\t\t\"⣑\",\n\t\t\t\"⣒\",\n\t\t\t\"⣓\",\n\t\t\t\"⣔\",\n\t\t\t\"⣕\",\n\t\t\t\"⣖\",\n\t\t\t\"⣗\",\n\t\t\t\"⢘\",\n\t\t\t\"⢙\",\n\t\t\t\"⢚\",\n\t\t\t\"⢛\",\n\t\t\t\"⢜\",\n\t\t\t\"⢝\",\n\t\t\t\"⢞\",\n\t\t\t\"⢟\",\n\t\t\t\"⣘\",\n\t\t\t\"⣙\",\n\t\t\t\"⣚\",\n\t\t\t\"⣛\",\n\t\t\t\"⣜\",\n\t\t\t\"⣝\",\n\t\t\t\"⣞\",\n\t\t\t\"⣟\",\n\t\t\t\"⢠\",\n\t\t\t\"⢡\",\n\t\t\t\"⢢\",\n\t\t\t\"⢣\",\n\t\t\t\"⢤\",\n\t\t\t\"⢥\",\n\t\t\t\"⢦\",\n\t\t\t\"⢧\",\n\t\t\t\"⣠\",\n\t\t\t\"⣡\",\n\t\t\t\"⣢\",\n\t\t\t\"⣣\",\n\t\t\t\"⣤\",\n\t\t\t\"⣥\",\n\t\t\t\"⣦\",\n\t\t\t\"⣧\",\n\t\t\t\"⢨\",\n\t\t\t\"⢩\",\n\t\t\t\"⢪\",\n\t\t\t\"⢫\",\n\t\t\t\"⢬\",\n\t\t\t\"⢭\",\n\t\t\t\"⢮\",\n\t\t\t\"⢯\",\n\t\t\t\"⣨\",\n\t\t\t\"⣩\",\n\t\t\t\"⣪\",\n\t\t\t\"⣫\",\n\t\t\t\"⣬\",\n\t\t\t\"⣭\",\n\t\t\t\"⣮\",\n\t\t\t\"⣯\",\n\t\t\t\"⢰\",\n\t\t\t\"⢱\",\n\t\t\t\"⢲\",\n\t\t\t\"⢳\",\n\t\t\t\"⢴\",\n\t\t\t\"⢵\",\n\t\t\t\"⢶\",\n\t\t\t\"⢷\",\n\t\t\t\"⣰\",\n\t\t\t\"⣱\",\n\t\t\t\"⣲\",\n\t\t\t\"⣳\",\n\t\t\t\"⣴\",\n\t\t\t\"⣵\",\n\t\t\t\"⣶\",\n\t\t\t\"⣷\",\n\t\t\t\"⢸\",\n\t\t\t\"⢹\",\n\t\t\t\"⢺\",\n\t\t\t\"⢻\",\n\t\t\t\"⢼\",\n\t\t\t\"⢽\",\n\t\t\t\"⢾\",\n\t\t\t\"⢿\",\n\t\t\t\"⣸\",\n\t\t\t\"⣹\",\n\t\t\t\"⣺\",\n\t\t\t\"⣻\",\n\t\t\t\"⣼\",\n\t\t\t\"⣽\",\n\t\t\t\"⣾\",\n\t\t\t\"⣿\"\n\t\t]\n\t},\n\t\"dotsCircle\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⢎ \",\n\t\t\t\"⠎⠁\",\n\t\t\t\"⠊⠑\",\n\t\t\t\"⠈⠱\",\n\t\t\t\" ⡱\",\n\t\t\t\"⢀⡰\",\n\t\t\t\"⢄⡠\",\n\t\t\t\"⢆⡀\"\n\t\t]\n\t},\n\t\"sand\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⠁\",\n\t\t\t\"⠂\",\n\t\t\t\"⠄\",\n\t\t\t\"⡀\",\n\t\t\t\"⡈\",\n\t\t\t\"⡐\",\n\t\t\t\"⡠\",\n\t\t\t\"⣀\",\n\t\t\t\"⣁\",\n\t\t\t\"⣂\",\n\t\t\t\"⣄\",\n\t\t\t\"⣌\",\n\t\t\t\"⣔\",\n\t\t\t\"⣤\",\n\t\t\t\"⣥\",\n\t\t\t\"⣦\",\n\t\t\t\"⣮\",\n\t\t\t\"⣶\",\n\t\t\t\"⣷\",\n\t\t\t\"⣿\",\n\t\t\t\"⡿\",\n\t\t\t\"⠿\",\n\t\t\t\"⢟\",\n\t\t\t\"⠟\",\n\t\t\t\"⡛\",\n\t\t\t\"⠛\",\n\t\t\t\"⠫\",\n\t\t\t\"⢋\",\n\t\t\t\"⠋\",\n\t\t\t\"⠍\",\n\t\t\t\"⡉\",\n\t\t\t\"⠉\",\n\t\t\t\"⠑\",\n\t\t\t\"⠡\",\n\t\t\t\"⢁\"\n\t\t]\n\t},\n\t\"line\": {\n\t\t\"interval\": 130,\n\t\t\"frames\": [\n\t\t\t\"-\",\n\t\t\t\"\\\\\",\n\t\t\t\"|\",\n\t\t\t\"/\"\n\t\t]\n\t},\n\t\"line2\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"⠂\",\n\t\t\t\"-\",\n\t\t\t\"–\",\n\t\t\t\"—\",\n\t\t\t\"–\",\n\t\t\t\"-\"\n\t\t]\n\t},\n\t\"rollingLine\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"/  \", \n\t\t\t\" - \", \n\t\t\t\" \\\\ \", \n\t\t\t\"  |\", \n\t\t\t\"  |\", \n\t\t\t\" \\\\ \", \n\t\t\t\" - \", \n\t\t\t\"/  \"\n\t\t]\n\t},\n\t\"pipe\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"┤\",\n\t\t\t\"┘\",\n\t\t\t\"┴\",\n\t\t\t\"└\",\n\t\t\t\"├\",\n\t\t\t\"┌\",\n\t\t\t\"┬\",\n\t\t\t\"┐\"\n\t\t]\n\t},\n\t\"simpleDots\": {\n\t\t\"interval\": 400,\n\t\t\"frames\": [\n\t\t\t\".  \",\n\t\t\t\".. \",\n\t\t\t\"...\",\n\t\t\t\"   \"\n\t\t]\n\t},\n\t\"simpleDotsScrolling\": {\n\t\t\"interval\": 200,\n\t\t\"frames\": [\n\t\t\t\".  \",\n\t\t\t\".. \",\n\t\t\t\"...\",\n\t\t\t\" ..\",\n\t\t\t\"  .\",\n\t\t\t\"   \"\n\t\t]\n\t},\n\t\"star\": {\n\t\t\"interval\": 70,\n\t\t\"frames\": [\n\t\t\t\"✶\",\n\t\t\t\"✸\",\n\t\t\t\"✹\",\n\t\t\t\"✺\",\n\t\t\t\"✹\",\n\t\t\t\"✷\"\n\t\t]\n\t},\n\t\"star2\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"+\",\n\t\t\t\"x\",\n\t\t\t\"*\"\n\t\t]\n\t},\n\t\"flip\": {\n\t\t\"interval\": 70,\n\t\t\"frames\": [\n\t\t\t\"_\",\n\t\t\t\"_\",\n\t\t\t\"_\",\n\t\t\t\"-\",\n\t\t\t\"`\",\n\t\t\t\"`\",\n\t\t\t\"'\",\n\t\t\t\"´\",\n\t\t\t\"-\",\n\t\t\t\"_\",\n\t\t\t\"_\",\n\t\t\t\"_\"\n\t\t]\n\t},\n\t\"hamburger\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"☱\",\n\t\t\t\"☲\",\n\t\t\t\"☴\"\n\t\t]\n\t},\n\t\"growVertical\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"▁\",\n\t\t\t\"▃\",\n\t\t\t\"▄\",\n\t\t\t\"▅\",\n\t\t\t\"▆\",\n\t\t\t\"▇\",\n\t\t\t\"▆\",\n\t\t\t\"▅\",\n\t\t\t\"▄\",\n\t\t\t\"▃\"\n\t\t]\n\t},\n\t\"growHorizontal\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"▏\",\n\t\t\t\"▎\",\n\t\t\t\"▍\",\n\t\t\t\"▌\",\n\t\t\t\"▋\",\n\t\t\t\"▊\",\n\t\t\t\"▉\",\n\t\t\t\"▊\",\n\t\t\t\"▋\",\n\t\t\t\"▌\",\n\t\t\t\"▍\",\n\t\t\t\"▎\"\n\t\t]\n\t},\n\t\"balloon\": {\n\t\t\"interval\": 140,\n\t\t\"frames\": [\n\t\t\t\" \",\n\t\t\t\".\",\n\t\t\t\"o\",\n\t\t\t\"O\",\n\t\t\t\"@\",\n\t\t\t\"*\",\n\t\t\t\" \"\n\t\t]\n\t},\n\t\"balloon2\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\".\",\n\t\t\t\"o\",\n\t\t\t\"O\",\n\t\t\t\"°\",\n\t\t\t\"O\",\n\t\t\t\"o\",\n\t\t\t\".\"\n\t\t]\n\t},\n\t\"noise\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"▓\",\n\t\t\t\"▒\",\n\t\t\t\"░\"\n\t\t]\n\t},\n\t\"bounce\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"⠁\",\n\t\t\t\"⠂\",\n\t\t\t\"⠄\",\n\t\t\t\"⠂\"\n\t\t]\n\t},\n\t\"boxBounce\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"▖\",\n\t\t\t\"▘\",\n\t\t\t\"▝\",\n\t\t\t\"▗\"\n\t\t]\n\t},\n\t\"boxBounce2\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"▌\",\n\t\t\t\"▀\",\n\t\t\t\"▐\",\n\t\t\t\"▄\"\n\t\t]\n\t},\n\t\"triangle\": {\n\t\t\"interval\": 50,\n\t\t\"frames\": [\n\t\t\t\"◢\",\n\t\t\t\"◣\",\n\t\t\t\"◤\",\n\t\t\t\"◥\"\n\t\t]\n\t},\n\t\"binary\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"010010\",\n            \"001100\",\n            \"100101\",\n            \"111010\",\n            \"111101\",\n            \"010111\",\n\t\t\t\"101011\",\n\t\t\t\"111000\",\n\t\t\t\"110011\",\n\t\t\t\"110101\"\n\t\t]\n\t},\n\t\"arc\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"◜\",\n\t\t\t\"◠\",\n\t\t\t\"◝\",\n\t\t\t\"◞\",\n\t\t\t\"◡\",\n\t\t\t\"◟\"\n\t\t]\n\t},\n\t\"circle\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"◡\",\n\t\t\t\"⊙\",\n\t\t\t\"◠\"\n\t\t]\n\t},\n\t\"squareCorners\": {\n\t\t\"interval\": 180,\n\t\t\"frames\": [\n\t\t\t\"◰\",\n\t\t\t\"◳\",\n\t\t\t\"◲\",\n\t\t\t\"◱\"\n\t\t]\n\t},\n\t\"circleQuarters\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"◴\",\n\t\t\t\"◷\",\n\t\t\t\"◶\",\n\t\t\t\"◵\"\n\t\t]\n\t},\n\t\"circleHalves\": {\n\t\t\"interval\": 50,\n\t\t\"frames\": [\n\t\t\t\"◐\",\n\t\t\t\"◓\",\n\t\t\t\"◑\",\n\t\t\t\"◒\"\n\t\t]\n\t},\n\t\"squish\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"╫\",\n\t\t\t\"╪\"\n\t\t]\n\t},\n\t\"toggle\": {\n\t\t\"interval\": 250,\n\t\t\"frames\": [\n\t\t\t\"⊶\",\n\t\t\t\"⊷\"\n\t\t]\n\t},\n\t\"toggle2\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"▫\",\n\t\t\t\"▪\"\n\t\t]\n\t},\n\t\"toggle3\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"□\",\n\t\t\t\"■\"\n\t\t]\n\t},\n\t\"toggle4\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"■\",\n\t\t\t\"□\",\n\t\t\t\"▪\",\n\t\t\t\"▫\"\n\t\t]\n\t},\n\t\"toggle5\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"▮\",\n\t\t\t\"▯\"\n\t\t]\n\t},\n\t\"toggle6\": {\n\t\t\"interval\": 300,\n\t\t\"frames\": [\n\t\t\t\"ဝ\",\n\t\t\t\"၀\"\n\t\t]\n\t},\n\t\"toggle7\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⦾\",\n\t\t\t\"⦿\"\n\t\t]\n\t},\n\t\"toggle8\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"◍\",\n\t\t\t\"◌\"\n\t\t]\n\t},\n\t\"toggle9\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"◉\",\n\t\t\t\"◎\"\n\t\t]\n\t},\n\t\"toggle10\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"㊂\",\n\t\t\t\"㊀\",\n\t\t\t\"㊁\"\n\t\t]\n\t},\n\t\"toggle11\": {\n\t\t\"interval\": 50,\n\t\t\"frames\": [\n\t\t\t\"⧇\",\n\t\t\t\"⧆\"\n\t\t]\n\t},\n\t\"toggle12\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"☗\",\n\t\t\t\"☖\"\n\t\t]\n\t},\n\t\"toggle13\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"=\",\n\t\t\t\"*\",\n\t\t\t\"-\"\n\t\t]\n\t},\n\t\"arrow\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"←\",\n\t\t\t\"↖\",\n\t\t\t\"↑\",\n\t\t\t\"↗\",\n\t\t\t\"→\",\n\t\t\t\"↘\",\n\t\t\t\"↓\",\n\t\t\t\"↙\"\n\t\t]\n\t},\n\t\"arrow2\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"⬆️ \",\n\t\t\t\"↗️ \",\n\t\t\t\"➡️ \",\n\t\t\t\"↘️ \",\n\t\t\t\"⬇️ \",\n\t\t\t\"↙️ \",\n\t\t\t\"⬅️ \",\n\t\t\t\"↖️ \"\n\t\t]\n\t},\n\t\"arrow3\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"▹▹▹▹▹\",\n\t\t\t\"▸▹▹▹▹\",\n\t\t\t\"▹▸▹▹▹\",\n\t\t\t\"▹▹▸▹▹\",\n\t\t\t\"▹▹▹▸▹\",\n\t\t\t\"▹▹▹▹▸\"\n\t\t]\n\t},\n\t\"bouncingBar\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"[    ]\",\n\t\t\t\"[=   ]\",\n\t\t\t\"[==  ]\",\n\t\t\t\"[=== ]\",\n\t\t\t\"[====]\",\n\t\t\t\"[ ===]\",\n\t\t\t\"[  ==]\",\n\t\t\t\"[   =]\",\n\t\t\t\"[    ]\",\n\t\t\t\"[   =]\",\n\t\t\t\"[  ==]\",\n\t\t\t\"[ ===]\",\n\t\t\t\"[====]\",\n\t\t\t\"[=== ]\",\n\t\t\t\"[==  ]\",\n\t\t\t\"[=   ]\"\n\t\t]\n\t},\n\t\"bouncingBall\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"( ●    )\",\n\t\t\t\"(  ●   )\",\n\t\t\t\"(   ●  )\",\n\t\t\t\"(    ● )\",\n\t\t\t\"(     ●)\",\n\t\t\t\"(    ● )\",\n\t\t\t\"(   ●  )\",\n\t\t\t\"(  ●   )\",\n\t\t\t\"( ●    )\",\n\t\t\t\"(●     )\"\n\t\t]\n\t},\n\t\"smiley\": {\n\t\t\"interval\": 200,\n\t\t\"frames\": [\n\t\t\t\"😄 \",\n\t\t\t\"😝 \"\n\t\t]\n\t},\n\t\"monkey\": {\n\t\t\"interval\": 300,\n\t\t\"frames\": [\n\t\t\t\"🙈 \",\n\t\t\t\"🙈 \",\n\t\t\t\"🙉 \",\n\t\t\t\"🙊 \"\n\t\t]\n\t},\n\t\"hearts\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"💛 \",\n\t\t\t\"💙 \",\n\t\t\t\"💜 \",\n\t\t\t\"💚 \",\n\t\t\t\"💗 \"\n\t\t]\n\t},\n\t\"clock\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"🕛 \",\n\t\t\t\"🕐 \",\n\t\t\t\"🕑 \",\n\t\t\t\"🕒 \",\n\t\t\t\"🕓 \",\n\t\t\t\"🕔 \",\n\t\t\t\"🕕 \",\n\t\t\t\"🕖 \",\n\t\t\t\"🕗 \",\n\t\t\t\"🕘 \",\n\t\t\t\"🕙 \",\n\t\t\t\"🕚 \"\n\t\t]\n\t},\n\t\"earth\": {\n\t\t\"interval\": 180,\n\t\t\"frames\": [\n\t\t\t\"🌍 \",\n\t\t\t\"🌎 \",\n\t\t\t\"🌏 \"\n\t\t]\n\t},\n\t\"material\": {\n\t\t\"interval\": 17,\n\t\t\"frames\": [\n\t\t\t\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"██████████▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"███████████▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████████▁▁▁▁▁▁▁\",\n\t\t\t\"██████████████▁▁▁▁▁▁\",\n\t\t\t\"██████████████▁▁▁▁▁▁\",\n\t\t\t\"▁██████████████▁▁▁▁▁\",\n\t\t\t\"▁██████████████▁▁▁▁▁\",\n\t\t\t\"▁██████████████▁▁▁▁▁\",\n\t\t\t\"▁▁██████████████▁▁▁▁\",\n\t\t\t\"▁▁▁██████████████▁▁▁\",\n\t\t\t\"▁▁▁▁█████████████▁▁▁\",\n\t\t\t\"▁▁▁▁██████████████▁▁\",\n\t\t\t\"▁▁▁▁██████████████▁▁\",\n\t\t\t\"▁▁▁▁▁██████████████▁\",\n\t\t\t\"▁▁▁▁▁██████████████▁\",\n\t\t\t\"▁▁▁▁▁██████████████▁\",\n\t\t\t\"▁▁▁▁▁▁██████████████\",\n\t\t\t\"▁▁▁▁▁▁██████████████\",\n\t\t\t\"▁▁▁▁▁▁▁█████████████\",\n\t\t\t\"▁▁▁▁▁▁▁█████████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁████████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁████████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁███████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁███████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁██████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁██████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\n\t\t\t\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n\t\t\t\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n\t\t\t\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n\t\t\t\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n\t\t\t\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n\t\t\t\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"███████████▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"████████████▁▁▁▁▁▁▁▁\",\n\t\t\t\"████████████▁▁▁▁▁▁▁▁\",\n\t\t\t\"██████████████▁▁▁▁▁▁\",\n\t\t\t\"██████████████▁▁▁▁▁▁\",\n\t\t\t\"▁██████████████▁▁▁▁▁\",\n\t\t\t\"▁██████████████▁▁▁▁▁\",\n\t\t\t\"▁▁▁█████████████▁▁▁▁\",\n\t\t\t\"▁▁▁▁▁████████████▁▁▁\",\n\t\t\t\"▁▁▁▁▁████████████▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁███████████▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\n\t\t\t\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"\n\t\t]\n\t},\n\t\"moon\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"🌑 \",\n\t\t\t\"🌒 \",\n\t\t\t\"🌓 \",\n\t\t\t\"🌔 \",\n\t\t\t\"🌕 \",\n\t\t\t\"🌖 \",\n\t\t\t\"🌗 \",\n\t\t\t\"🌘 \"\n\t\t]\n\t},\n\t\"runner\": {\n\t\t\"interval\": 140,\n\t\t\"frames\": [\n\t\t\t\"🚶 \",\n\t\t\t\"🏃 \"\n\t\t]\n\t},\n\t\"pong\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"▐⠂       ▌\",\n\t\t\t\"▐⠈       ▌\",\n\t\t\t\"▐ ⠂      ▌\",\n\t\t\t\"▐ ⠠      ▌\",\n\t\t\t\"▐  ⡀     ▌\",\n\t\t\t\"▐  ⠠     ▌\",\n\t\t\t\"▐   ⠂    ▌\",\n\t\t\t\"▐   ⠈    ▌\",\n\t\t\t\"▐    ⠂   ▌\",\n\t\t\t\"▐    ⠠   ▌\",\n\t\t\t\"▐     ⡀  ▌\",\n\t\t\t\"▐     ⠠  ▌\",\n\t\t\t\"▐      ⠂ ▌\",\n\t\t\t\"▐      ⠈ ▌\",\n\t\t\t\"▐       ⠂▌\",\n\t\t\t\"▐       ⠠▌\",\n\t\t\t\"▐       ⡀▌\",\n\t\t\t\"▐      ⠠ ▌\",\n\t\t\t\"▐      ⠂ ▌\",\n\t\t\t\"▐     ⠈  ▌\",\n\t\t\t\"▐     ⠂  ▌\",\n\t\t\t\"▐    ⠠   ▌\",\n\t\t\t\"▐    ⡀   ▌\",\n\t\t\t\"▐   ⠠    ▌\",\n\t\t\t\"▐   ⠂    ▌\",\n\t\t\t\"▐  ⠈     ▌\",\n\t\t\t\"▐  ⠂     ▌\",\n\t\t\t\"▐ ⠠      ▌\",\n\t\t\t\"▐ ⡀      ▌\",\n\t\t\t\"▐⠠       ▌\"\n\t\t]\n\t},\n\t\"shark\": {\n\t\t\"interval\": 120,\n\t\t\"frames\": [\n\t\t\t\"▐|\\\\____________▌\",\n\t\t\t\"▐_|\\\\___________▌\",\n\t\t\t\"▐__|\\\\__________▌\",\n\t\t\t\"▐___|\\\\_________▌\",\n\t\t\t\"▐____|\\\\________▌\",\n\t\t\t\"▐_____|\\\\_______▌\",\n\t\t\t\"▐______|\\\\______▌\",\n\t\t\t\"▐_______|\\\\_____▌\",\n\t\t\t\"▐________|\\\\____▌\",\n\t\t\t\"▐_________|\\\\___▌\",\n\t\t\t\"▐__________|\\\\__▌\",\n\t\t\t\"▐___________|\\\\_▌\",\n\t\t\t\"▐____________|\\\\▌\",\n\t\t\t\"▐____________/|▌\",\n\t\t\t\"▐___________/|_▌\",\n\t\t\t\"▐__________/|__▌\",\n\t\t\t\"▐_________/|___▌\",\n\t\t\t\"▐________/|____▌\",\n\t\t\t\"▐_______/|_____▌\",\n\t\t\t\"▐______/|______▌\",\n\t\t\t\"▐_____/|_______▌\",\n\t\t\t\"▐____/|________▌\",\n\t\t\t\"▐___/|_________▌\",\n\t\t\t\"▐__/|__________▌\",\n\t\t\t\"▐_/|___________▌\",\n\t\t\t\"▐/|____________▌\"\n\t\t]\n\t},\n\t\"dqpb\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"d\",\n\t\t\t\"q\",\n\t\t\t\"p\",\n\t\t\t\"b\"\n\t\t]\n\t},\n\t\"weather\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"☀️ \",\n\t\t\t\"☀️ \",\n\t\t\t\"☀️ \",\n\t\t\t\"🌤 \",\n\t\t\t\"⛅️ \",\n\t\t\t\"🌥 \",\n\t\t\t\"☁️ \",\n\t\t\t\"🌧 \",\n\t\t\t\"🌨 \",\n\t\t\t\"🌧 \",\n\t\t\t\"🌨 \",\n\t\t\t\"🌧 \",\n\t\t\t\"🌨 \",\n\t\t\t\"⛈ \",\n\t\t\t\"🌨 \",\n\t\t\t\"🌧 \",\n\t\t\t\"🌨 \",\n\t\t\t\"☁️ \",\n\t\t\t\"🌥 \",\n\t\t\t\"⛅️ \",\n\t\t\t\"🌤 \",\n\t\t\t\"☀️ \",\n\t\t\t\"☀️ \"\n\t\t]\n\t},\n\t\"christmas\": {\n\t\t\"interval\": 400,\n\t\t\"frames\": [\n\t\t\t\"🌲\",\n\t\t\t\"🎄\"\n\t\t]\n\t},\n\t\"grenade\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"،  \",\n\t\t\t\"′  \",\n\t\t\t\" ´ \",\n\t\t\t\" ‾ \",\n\t\t\t\"  ⸌\",\n\t\t\t\"  ⸊\",\n\t\t\t\"  |\",\n\t\t\t\"  ⁎\",\n\t\t\t\"  ⁕\",\n\t\t\t\" ෴ \",\n\t\t\t\"  ⁓\",\n\t\t\t\"   \",\n\t\t\t\"   \",\n\t\t\t\"   \"\n\t\t]\n\t},\n\t\"point\": {\n\t\t\"interval\": 125,\n\t\t\"frames\": [\n\t\t\t\"∙∙∙\",\n\t\t\t\"●∙∙\",\n\t\t\t\"∙●∙\",\n\t\t\t\"∙∙●\",\n\t\t\t\"∙∙∙\"\n\t\t]\n\t},\n\t\"layer\": {\n\t\t\"interval\": 150,\n\t\t\"frames\": [\n\t\t\t\"-\",\n\t\t\t\"=\",\n\t\t\t\"≡\"\n\t\t]\n\t},\n\t\"betaWave\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"ρββββββ\",\n\t\t\t\"βρβββββ\",\n\t\t\t\"ββρββββ\",\n\t\t\t\"βββρβββ\",\n\t\t\t\"ββββρββ\",\n\t\t\t\"βββββρβ\",\n\t\t\t\"ββββββρ\"\n\t\t]\n\t},\n\t\"fingerDance\": {\n\t\t\"interval\": 160,\n\t\t\"frames\": [\n\t\t\t\"🤘 \",\n\t\t\t\"🤟 \",\n\t\t\t\"🖖 \",\n\t\t\t\"✋ \",\n\t\t\t\"🤚 \",\n\t\t\t\"👆 \"\n\t\t]\n\t},\n\t\"fistBump\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"🤜\\u3000\\u3000\\u3000\\u3000🤛 \",\n\t\t\t\"🤜\\u3000\\u3000\\u3000\\u3000🤛 \",\n\t\t\t\"🤜\\u3000\\u3000\\u3000\\u3000🤛 \",\n\t\t\t\"\\u3000🤜\\u3000\\u3000🤛\\u3000 \",\n\t\t\t\"\\u3000\\u3000🤜🤛\\u3000\\u3000 \",\n\t\t\t\"\\u3000🤜✨🤛\\u3000\\u3000 \",\n\t\t\t\"🤜\\u3000✨\\u3000🤛\\u3000 \"\n\t\t]\n\t},\n\t\"soccerHeader\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\" 🧑⚽️       🧑 \",\n\t\t\t\"🧑  ⚽️      🧑 \",\n\t\t\t\"🧑   ⚽️     🧑 \",\n\t\t\t\"🧑    ⚽️    🧑 \",\n\t\t\t\"🧑     ⚽️   🧑 \",\n\t\t\t\"🧑      ⚽️  🧑 \",\n\t\t\t\"🧑       ⚽️🧑  \",\n\t\t\t\"🧑      ⚽️  🧑 \",\n\t\t\t\"🧑     ⚽️   🧑 \",\n\t\t\t\"🧑    ⚽️    🧑 \",\n\t\t\t\"🧑   ⚽️     🧑 \",\n\t\t\t\"🧑  ⚽️      🧑 \"\n\t\t]\n\t},\n\t\"mindblown\": {\n\t\t\"interval\": 160,\n\t\t\"frames\": [\n\t\t\t\"😐 \",\n\t\t\t\"😐 \",\n\t\t\t\"😮 \",\n\t\t\t\"😮 \",\n\t\t\t\"😦 \",\n\t\t\t\"😦 \",\n\t\t\t\"😧 \",\n\t\t\t\"😧 \",\n\t\t\t\"🤯 \",\n\t\t\t\"💥 \",\n\t\t\t\"✨ \",\n\t\t\t\"\\u3000 \",\n\t\t\t\"\\u3000 \",\n\t\t\t\"\\u3000 \"\n\t\t]\n\t},\n\t\"speaker\": {\n\t\t\"interval\": 160,\n\t\t\"frames\": [\n\t\t\t\"🔈 \",\n\t\t\t\"🔉 \",\n\t\t\t\"🔊 \",\n\t\t\t\"🔉 \"\n\t\t]\n\t},\n\t\"orangePulse\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"🔸 \",\n\t\t\t\"🔶 \",\n\t\t\t\"🟠 \",\n\t\t\t\"🟠 \",\n\t\t\t\"🔶 \"\n\t\t]\n\t},\n\t\"bluePulse\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"🔹 \",\n\t\t\t\"🔷 \",\n\t\t\t\"🔵 \",\n\t\t\t\"🔵 \",\n\t\t\t\"🔷 \"\n\t\t]\n\t},\n\t\"orangeBluePulse\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"🔸 \",\n\t\t\t\"🔶 \",\n\t\t\t\"🟠 \",\n\t\t\t\"🟠 \",\n\t\t\t\"🔶 \",\n\t\t\t\"🔹 \",\n\t\t\t\"🔷 \",\n\t\t\t\"🔵 \",\n\t\t\t\"🔵 \",\n\t\t\t\"🔷 \"\n\t\t]\n\t},\n\t\"timeTravel\": {\n\t\t\"interval\": 100,\n\t\t\"frames\": [\n\t\t\t\"🕛 \",\n\t\t\t\"🕚 \",\n\t\t\t\"🕙 \",\n\t\t\t\"🕘 \",\n\t\t\t\"🕗 \",\n\t\t\t\"🕖 \",\n\t\t\t\"🕕 \",\n\t\t\t\"🕔 \",\n\t\t\t\"🕓 \",\n\t\t\t\"🕒 \",\n\t\t\t\"🕑 \",\n\t\t\t\"🕐 \"\n\t\t]\n\t},\n\t\"aesthetic\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\"▰▱▱▱▱▱▱\",\n\t\t\t\"▰▰▱▱▱▱▱\",\n\t\t\t\"▰▰▰▱▱▱▱\",\n\t\t\t\"▰▰▰▰▱▱▱\",\n\t\t\t\"▰▰▰▰▰▱▱\",\n\t\t\t\"▰▰▰▰▰▰▱\",\n\t\t\t\"▰▰▰▰▰▰▰\",\n\t\t\t\"▰▱▱▱▱▱▱\"\n\t\t]\n\t},\n\t\"dwarfFortress\": {\n\t\t\"interval\": 80,\n\t\t\"frames\": [\n\t\t\t\" ██████£££  \",\n\t\t\t\"☺██████£££  \",\n\t\t\t\"☺██████£££  \",\n\t\t\t\"☺▓█████£££  \",\n\t\t\t\"☺▓█████£££  \",\n\t\t\t\"☺▒█████£££  \",\n\t\t\t\"☺▒█████£££  \",\n\t\t\t\"☺░█████£££  \",\n\t\t\t\"☺░█████£££  \",\n\t\t\t\"☺ █████£££  \",\n\t\t\t\" ☺█████£££  \",\n\t\t\t\" ☺█████£££  \",\n\t\t\t\" ☺▓████£££  \",\n\t\t\t\" ☺▓████£££  \",\n\t\t\t\" ☺▒████£££  \",\n\t\t\t\" ☺▒████£££  \",\n\t\t\t\" ☺░████£££  \",\n\t\t\t\" ☺░████£££  \",\n\t\t\t\" ☺ ████£££  \",\n\t\t\t\"  ☺████£££  \",\n\t\t\t\"  ☺████£££  \",\n\t\t\t\"  ☺▓███£££  \",\n\t\t\t\"  ☺▓███£££  \",\n\t\t\t\"  ☺▒███£££  \",\n\t\t\t\"  ☺▒███£££  \",\n\t\t\t\"  ☺░███£££  \",\n\t\t\t\"  ☺░███£££  \",\n\t\t\t\"  ☺ ███£££  \",\n\t\t\t\"   ☺███£££  \",\n\t\t\t\"   ☺███£££  \",\n\t\t\t\"   ☺▓██£££  \",\n\t\t\t\"   ☺▓██£££  \",\n\t\t\t\"   ☺▒██£££  \",\n\t\t\t\"   ☺▒██£££  \",\n\t\t\t\"   ☺░██£££  \",\n\t\t\t\"   ☺░██£££  \",\n\t\t\t\"   ☺ ██£££  \",\n\t\t\t\"    ☺██£££  \",\n\t\t\t\"    ☺██£££  \",\n\t\t\t\"    ☺▓█£££  \",\n\t\t\t\"    ☺▓█£££  \",\n\t\t\t\"    ☺▒█£££  \",\n\t\t\t\"    ☺▒█£££  \",\n\t\t\t\"    ☺░█£££  \",\n\t\t\t\"    ☺░█£££  \",\n\t\t\t\"    ☺ █£££  \",\n\t\t\t\"     ☺█£££  \",\n\t\t\t\"     ☺█£££  \",\n\t\t\t\"     ☺▓£££  \",\n\t\t\t\"     ☺▓£££  \",\n\t\t\t\"     ☺▒£££  \",\n\t\t\t\"     ☺▒£££  \",\n\t\t\t\"     ☺░£££  \",\n\t\t\t\"     ☺░£££  \",\n\t\t\t\"     ☺ £££  \",\n\t\t\t\"      ☺£££  \",\n\t\t\t\"      ☺£££  \",\n\t\t\t\"      ☺▓££  \",\n\t\t\t\"      ☺▓££  \",\n\t\t\t\"      ☺▒££  \",\n\t\t\t\"      ☺▒££  \",\n\t\t\t\"      ☺░££  \",\n\t\t\t\"      ☺░££  \",\n\t\t\t\"      ☺ ££  \",\n\t\t\t\"       ☺££  \",\n\t\t\t\"       ☺££  \",\n\t\t\t\"       ☺▓£  \",\n\t\t\t\"       ☺▓£  \",\n\t\t\t\"       ☺▒£  \",\n\t\t\t\"       ☺▒£  \",\n\t\t\t\"       ☺░£  \",\n\t\t\t\"       ☺░£  \",\n\t\t\t\"       ☺ £  \",\n\t\t\t\"        ☺£  \",\n\t\t\t\"        ☺£  \",\n\t\t\t\"        ☺▓  \",\n\t\t\t\"        ☺▓  \",\n\t\t\t\"        ☺▒  \",\n\t\t\t\"        ☺▒  \",\n\t\t\t\"        ☺░  \",\n\t\t\t\"        ☺░  \",\n\t\t\t\"        ☺   \",\n\t\t\t\"        ☺  &\",\n\t\t\t\"        ☺ ☼&\",\n\t\t\t\"       ☺ ☼ &\",\n\t\t\t\"       ☺☼  &\",\n\t\t\t\"      ☺☼  & \",\n\t\t\t\"      ‼   & \",\n\t\t\t\"     ☺   &  \",\n\t\t\t\"    ‼    &  \",\n\t\t\t\"   ☺    &   \",\n\t\t\t\"  ‼     &   \",\n\t\t\t\" ☺     &    \",\n\t\t\t\"‼      &    \",\n\t\t\t\"      &     \",\n\t\t\t\"      &     \",\n\t\t\t\"     &   ░  \",\n\t\t\t\"     &   ▒  \",\n\t\t\t\"    &    ▓  \",\n\t\t\t\"    &    £  \",\n\t\t\t\"   &    ░£  \",\n\t\t\t\"   &    ▒£  \",\n\t\t\t\"  &     ▓£  \",\n\t\t\t\"  &     ££  \",\n\t\t\t\" &     ░££  \",\n\t\t\t\" &     ▒££  \",\n\t\t\t\"&      ▓££  \",\n\t\t\t\"&      £££  \",\n\t\t\t\"      ░£££  \",\n\t\t\t\"      ▒£££  \",\n\t\t\t\"      ▓£££  \",\n\t\t\t\"      █£££  \",\n\t\t\t\"     ░█£££  \",\n\t\t\t\"     ▒█£££  \",\n\t\t\t\"     ▓█£££  \",\n\t\t\t\"     ██£££  \",\n\t\t\t\"    ░██£££  \",\n\t\t\t\"    ▒██£££  \",\n\t\t\t\"    ▓██£££  \",\n\t\t\t\"    ███£££  \",\n\t\t\t\"   ░███£££  \",\n\t\t\t\"   ▒███£££  \",\n\t\t\t\"   ▓███£££  \",\n\t\t\t\"   ████£££  \",\n\t\t\t\"  ░████£££  \",\n\t\t\t\"  ▒████£££  \",\n\t\t\t\"  ▓████£££  \",\n\t\t\t\"  █████£££  \",\n\t\t\t\" ░█████£££  \",\n\t\t\t\" ▒█████£££  \",\n\t\t\t\" ▓█████£££  \",\n\t\t\t\" ██████£££  \",\n\t\t\t\" ██████£££  \"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "data/whirly-static-spinners.json",
    "content": "{\n  \"roman_numerals\": {\n    \"interval\": 90,\n    \"mode\": \"swing\",\n    \"frames\": [\n      \"Ⅰ\",\n      \"Ⅱ\",\n      \"Ⅲ\",\n      \"Ⅳ\",\n      \"Ⅴ\",\n      \"Ⅵ\",\n      \"Ⅶ\",\n      \"Ⅷ\",\n      \"Ⅸ\",\n      \"Ⅹ\"\n    ]\n  },\n  \"double_mark\": {\n    \"interval\": 120,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"⁇\",\n      \"⁈\",\n      \"⁉\",\n      \"‼\"\n    ]\n  },\n  \"heart_exclamation\": {\n    \"interval\": 45,\n    \"frames\": [\n      \"❢\",\n      \"❣\"\n    ]\n  },\n  \"pencil\": {\n    \"interval\": 200,\n    \"frames\": [\n      \"✏\",\n      \"✎\"\n    ]\n  },\n  \"bars\": {\n    \"interval\": 80,\n    \"mode\": \"swing\",\n    \"frames\": [\n      \"𝍠\",\n      \"𝍡\",\n      \"𝍢\",\n      \"𝍣\",\n      \"𝍤\"\n    ]\n  },\n  \"dice\": {\n    \"interval\": 100,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"⚀\",\n      \"⚁\",\n      \"⚂\",\n      \"⚃\",\n      \"⚄\",\n      \"⚅\"\n    ]\n  },\n  \"hanoi\": {\n    \"interval\": 150,\n    \"mode\": \"swing\",\n    \"frames\": [\n      \"𝍥\",\n      \"𝍦\",\n      \"𝍧\",\n      \"𝍨\"\n    ]\n  },\n  \"vertical_bars\": {\n    \"interval\": 80,\n    \"mode\": \"swing\",\n    \"frames\": [\n      \"𝍩\",\n      \"𝍪\",\n      \"𝍫\",\n      \"𝍬\",\n      \"𝍭\"\n    ]\n  },\n  \"whirly\": {\n    \"interval\": 200,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"😀\",\n      \"😁\",\n      \"😂\",\n      \"😃\",\n      \"😄\",\n      \"😅\",\n      \"😆\",\n      \"😇\",\n      \"😈\",\n      \"😉\",\n      \"😊\",\n      \"😋\",\n      \"😌\",\n      \"😍\",\n      \"😎\",\n      \"😏\",\n      \"😐\",\n      \"😑\",\n      \"😒\",\n      \"😓\",\n      \"😔\",\n      \"😕\",\n      \"😖\",\n      \"😗\",\n      \"😘\",\n      \"😙\",\n      \"😚\",\n      \"😛\",\n      \"😜\",\n      \"😝\",\n      \"😞\",\n      \"😟\",\n      \"😠\",\n      \"😡\",\n      \"😢\",\n      \"😣\",\n      \"😤\",\n      \"😥\",\n      \"😦\",\n      \"😧\",\n      \"😨\",\n      \"😩\",\n      \"😪\",\n      \"😫\",\n      \"😬\",\n      \"😭\",\n      \"😮\",\n      \"😯\",\n      \"😰\",\n      \"😱\",\n      \"😲\",\n      \"😳\",\n      \"😴\",\n      \"😵\",\n      \"😶\",\n      \"🙁\",\n      \"🙂\",\n      \"🙃\",\n      \"🙄\",\n      \"😷\",\n      \"🤐\",\n      \"🤑\",\n      \"🤒\",\n      \"🤓\",\n      \"🤔\",\n      \"🤕\",\n      \"🤖\",\n      \"🤗\"\n    ]\n  },\n  \"cat\": {\n    \"interval\": 200,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"😸\",\n      \"😹\",\n      \"😺\",\n      \"😻\",\n      \"😼\",\n      \"😽\",\n      \"😾\",\n      \"😿\",\n      \"🙀\"\n    ]\n  },\n  \"card\": {\n    \"interval\": 90,\n    \"stop\": \"🂠\",\n    \"frames\": [\n      \"🃁\", \"🃂\", \"🃃\", \"🃄\", \"🃅\", \"🃆\", \"🃇\", \"🃈\", \"🃉\", \"🃊\", \"🃋\", \"🃌\", \"🃍\", \"🃎\",\n      \"🂱\", \"🂲\", \"🂳\", \"🂴\", \"🂵\", \"🂶\", \"🂷\", \"🂸\", \"🂹\", \"🂺\", \"🂻\", \"🂼\", \"🂽\", \"🂾\",\n      \"🂡\", \"🂢\", \"🂣\", \"🂤\", \"🂥\", \"🂦\", \"🂧\", \"🂨\", \"🂩\", \"🂪\", \"🂫\", \"🂬\", \"🂭\", \"🂮\",\n      \"🃑\", \"🃒\", \"🃓\", \"🃔\", \"🃕\", \"🃖\", \"🃗\", \"🃘\", \"🃙\", \"🃚\", \"🃛\", \"🃜\", \"🃝\", \"🃞\"\n    ]\n  },\n  \"cloud\": {\n    \"interval\": 140,\n    \"frames\": [\n      \"🌥\",\n      \"🌦\",\n      \"🌧\",\n      \"🌨\",\n      \"🌩\",\n      \"🌪\"\n    ]\n  },\n  \"photo\": {\n    \"interval\": 200,\n    \"frames\": [\n      \"📷\",\n      \"📸\"\n    ]\n  },\n  \"banknote\": {\n    \"interval\": 100,\n    \"frames\": [\n      \"💴\",\n      \"💵\",\n      \"💶\",\n      \"💷\"\n    ]\n  },\n  \"white_square\": {\n    \"interval\": 100,\n    \"mode\": \"swing\",\n    \"frames\": [\n      \"🞓\",\n      \"🞒\",\n      \"🞑\",\n      \"🞐\",\n      \"🞏\",\n      \"🞎\",\n      \"🞔\"\n    ]\n  },\n  \"xberg\": {\n    \"interval\": 150,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"⨯\",\n      \"⛰\",\n      \"⛰\",\n      \"⛰\",\n      \"⛰\",\n      \"⛰\",\n      \"⛰\"\n    ]\n  },\n  \"circled_letter\": {\n    \"interval\": 120,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"Ⓐ\", \"Ⓑ\", \"Ⓒ\", \"Ⓓ\", \"Ⓔ\", \"Ⓕ\", \"Ⓖ\", \"Ⓗ\", \"Ⓘ\",\n      \"Ⓙ\", \"Ⓚ\", \"Ⓛ\", \"Ⓜ\", \"Ⓝ\", \"Ⓞ\", \"Ⓟ\", \"Ⓠ\", \"Ⓡ\",\n      \"Ⓢ\", \"Ⓣ\", \"Ⓤ\", \"Ⓥ\", \"Ⓦ\", \"Ⓧ\", \"Ⓨ\", \"Ⓩ\"\n    ]\n  },\n  \"circled_number\": {\n    \"interval\": 120,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"①\", \"②\", \"③\", \"④\", \"⑤\", \"⑥\", \"⑦\", \"⑧\", \"⑨\"\n    ]\n  },\n  \"letter_with_parens\": {\n    \"interval\": 150,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"🄐\", \"🄑\", \"🄒\", \"🄓\", \"🄔\", \"🄕\", \"🄖\", \"🄗\", \"🄘\",\n      \"🄙\", \"🄚\", \"🄛\", \"🄜\", \"🄝\", \"🄞\", \"🄟\", \"🄠\", \"🄡\",\n      \"🄢\", \"🄣\", \"🄤\", \"🄥\", \"🄦\", \"🄧\", \"🄨\", \"🄩\"\n    ]\n  },\n  \"starlike\": {\n    \"interval\": 120,\n    \"mode\": \"random\",\n    \"frames\": [\n      \"✩\", \"✪\", \"✫\", \"✬\", \"✭\", \"✮\", \"✯\", \"✰\",\n      \"✱\", \"✲\", \"✳\", \"✴\", \"✵\", \"✶\", \"✷\", \"✸\",\n      \"✹\", \"✺\", \"✻\", \"✼\", \"✽\", \"✾\", \"✿\", \"❀\",\n      \"❁\", \"❂\", \"❃\", \"❄\", \"❅\", \"❆\", \"❇\", \"❈\",\n      \"❉\", \"❊\"\n    ]\n  }\n}\n"
  },
  {
    "path": "examples/all_spinners.rb",
    "content": "require_relative \"../lib/whirly\"\nrequire \"paint\"\n\n# Demonstrates all available spinners\n\nif spinner_pack = $*[0]\n  constants = [spinner_pack.upcase]\nelse\n  constants = Whirly::Spinners.constants\nend\n\nconstants.each{ |spinner_pack|\n  puts\n  puts Paint[spinner_pack, :underline]\n  puts\n  Whirly::Spinners.const_get(spinner_pack).keys.sort.each{ |spinner_name|\n    Whirly.start(spinner: spinner_name, status: spinner_name){\n      sleep 1.5\n    }\n  }\n}\n"
  },
  {
    "path": "examples/asciinema_bundled_spinners.rb",
    "content": "require_relative \"../lib/whirly\"\nrequire \"paint\"\n\nsystem \"clear\"\n\nWhirly::Spinners::WHIRLY.keys.sort.each{ |spinner_name|\n  Whirly.start(spinner: spinner_name, status: spinner_name, append_newline: false, ansi_escape_mode: \"line\", remove_after_stop: true){\n    sleep 1.5\n  }\n}\n\nsystem \"exit\"\n"
  },
  {
    "path": "examples/euruko.rb",
    "content": "require_relative '../lib/whirly'\nrequire 'paint'\n\n# Lightning talk at EuRuKo 2016\n\n# # # Whirly\n\nprint \"\\033c\"\nputs Paint[\"Whirly\", :underline]\n\nWhirly.start status: 'Generating something huge…' do\n  sleep 15\n  Whirly.status = \"(actually it's just `sleep 15`)\"\n  sleep 15\n  Whirly.status = \"Almost done…\"\n  sleep 3\n  Whirly.status = \"10 more seconds!\"\n  sleep 10\nend\n\nputs\nputs\nputs\nputs \"Done\"\nsleep 5\n\n# # # Earth\n\nprint \"\\033c\"\nputs Paint[\"Earth Spinner\", :underline]\n\nWhirly.start spinner: \"earth\"\nWhirly.status = \"Travelling…\"\nsleep 9\nWhirly.stop\n\nputs\nputs\nputs\nputs \"Done\"\nsleep 5\n\n# # # Pong Game\n\nprint \"\\033c\"\nputs Paint[\"Pong\", :underline]\n\nWhirly.start spinner: \"pong\", color: false, status: \"Two computers in a game of Pong\" do\n  sleep 9\nend\n\nputs\nputs\nputs\nputs \"Done\"\nsleep 5\n\n# # # Ticking Clock\n\nprint \"\\033c\"\nputs Paint[\"Clock\", :underline]\n\nWhirly.start spinner: \"clock\", interval: 1000 do\n  sleep 12\nend\n\nputs\nputs\nputs\nputs \"Time is over\"\n\n# # # URL\n\nprint \"\\033c\"\nputs Paint[\"Get WHIRLY\", :bold]\n\nWhirly.start spinner: \"whirly\", status: \"https://github.com/janlelis/whirly\" do\n  sleep 60\nend\n"
  },
  {
    "path": "examples/multi_lines.rb",
    "content": "require_relative \"../lib/whirly\"\nrequire \"paint\"\n\n# Demonstrate the look of using multiple spinners\n\nWhirly.configure(spinner: \"dots\", stop: \"✔\")\n\nWhirly.start status: \"Processing\" do\n  sleep 2\nend\n\nWhirly.start status: \"More processing\" do\n  sleep 2\nend\n\nWhirly.start status: \"Even more processing\" do\n  sleep 2\nend\n\nputs \"Done\"\n"
  },
  {
    "path": "examples/single.rb",
    "content": "require_relative \"../lib/whirly\"\nrequire \"paint\"\n\n# Call a single spinner from the command-line\n\nWhirly.start(spinner: $*[0], status: $*[2] || $*[0], color: false){ sleep(($*[1] || 10).to_i) }\n"
  },
  {
    "path": "examples/status.rb",
    "content": "require_relative \"../lib/whirly\"\n\nWhirly.start status: \"Initial status, passed when starting Whirly\"\nsleep 3\nWhirly.status = \"Status update\"\nsleep 3\nWhirly.stop\n"
  },
  {
    "path": "lib/whirly/spinners/cli.rb",
    "content": "require \"json\"\n\nmodule Whirly\n  module Spinners\n    CLI = JSON.load(File.read(File.dirname(__FILE__) + \"/../../../data/cli-spinners.json\")).freeze\n  end\nend\n"
  },
  {
    "path": "lib/whirly/spinners/whirly.rb",
    "content": "require \"json\"\n\nmodule Whirly\n  module Spinners\n    WHIRLY = {\n      \"random_dots\"         => { \"proc\" => ->(){ [ 0x2800 + rand(256)].pack(\"U\") }, \"interval\" => 100 },\n      \"mahjong\"             => { \"proc\" => ->(){ [0x1F000 + rand(44)].pack(\"U\") }, \"interval\" => 200 },\n      \"domino\"              => { \"proc\" => ->(){ [0x1F030 + rand(50)].pack(\"U\") }, \"interval\" => 200 },\n      \"vertical_domino\"     => { \"proc\" => ->(){ [0x1F062 + rand(50)].pack(\"U\") }, \"interval\" => 200 }\n    }\n    WHIRLY.merge! JSON.load(File.read(File.dirname(__FILE__) + \"/../../../data/whirly-static-spinners.json\"))\n\n    WHIRLY.freeze\n  end\nend\n"
  },
  {
    "path": "lib/whirly/spinners.rb",
    "content": "module Whirly\n  module Spinners\n  end\nend\n\nrequire_relative \"spinners/whirly\"\nrequire_relative \"spinners/cli\"\n"
  },
  {
    "path": "lib/whirly/version.rb",
    "content": "# frozen_string_literal: true\n\nmodule Whirly\n  VERSION = \"0.4.0\"\nend\n"
  },
  {
    "path": "lib/whirly.rb",
    "content": "require_relative \"whirly/version\"\nrequire_relative \"whirly/spinners\"\n\nrequire \"unicode/display_width\"\n\nbegin\n  require \"paint\"\nrescue LoadError\nend\n\nmodule Whirly\n  @configured = false\n\n  CLI_COMMANDS = {\n    hide_cursor: \"\\x1b[?25l\",\n    show_cursor: \"\\x1b[?25h\",\n  }.freeze\n\n  DEFAULT_OPTIONS = {\n    ambiguous_character_width: 1,\n    ansi_escape_mode: \"restore\",\n    append_newline: true,\n    color: !!defined?(Paint),\n    color_change_rate: 30,\n    hide_cursor: true,\n    non_tty: false,\n    position: \"normal\",\n    remove_after_stop: false,\n    spinner: \"whirly\",\n    spinner_packs: [:whirly, :cli],\n    status: nil,\n    stream: $stdout,\n  }.freeze\n\n  SOFT_DEFAULT_OPTIONS = {\n    interval: 100,\n    mode: \"linear\",\n    stop: nil,\n  }.freeze\n\n  class << self\n    attr_accessor :status\n    attr_reader :options\n\n    def enabled?\n      !!(defined?(@enabled) && @enabled)\n    end\n\n    def configured?\n      !!(@configured)\n    end\n  end\n\n  # set spinner directly or lookup\n  def self.configure_spinner(spinner_option)\n    case spinner_option\n    when Hash\n      spinner = spinner_option.dup\n    when Enumerable\n      spinner = { \"frames\" => spinner_option.dup }\n    when Proc\n      spinner = { \"proc\" => spinner_option.dup }\n    else\n      spinner = nil\n      catch(:found){\n        @options[:spinner_packs].each{ |spinner_pack|\n          spinners = Whirly::Spinners.const_get(spinner_pack.to_s.upcase)\n          if spinners[spinner_option]\n            spinner = spinners[spinner_option].dup\n            throw(:found)\n          end\n        }\n      }\n    end\n\n    # validate spinner\n    if !spinner || (!spinner[\"frames\"] && !spinner[\"proc\"])\n      raise(ArgumentError, \"Whirly: Invalid spinner given\")\n    end\n\n    spinner\n  end\n\n  # frames can be generated from enumerables or procs\n  def self.configure_frames(spinner)\n    if spinner[\"frames\"]\n      case spinner[\"mode\"]\n      when \"swing\"\n        frames = (spinner[\"frames\"].to_a + spinner[\"frames\"].to_a[1..-2].reverse).cycle\n      when \"random\"\n        frame_pool = spinner[\"frames\"].to_a\n        frames = ->(){ frame_pool.sample }\n      when \"reverse\"\n        frames = spinner[\"frames\"].to_a.reverse.cycle\n      else\n        frames = spinner[\"frames\"].cycle\n      end\n    elsif spinner[\"proc\"]\n      frames = spinner[\"proc\"].dup\n    else\n      raise(ArgumentError, \"Whirly: Invalid spinner given\")\n    end\n\n    if frames.is_a? Proc\n      class << frames\n        alias next call\n      end\n    end\n\n    frames\n  end\n\n  # save options and preprocess, set defaults if value is still unknown\n  def self.configure(**options)\n    if !defined?(@configured) || !@configured || !defined?(@options) || !@options\n      @options = DEFAULT_OPTIONS.dup\n      @configured = true\n    end\n\n    @options.merge!(options)\n\n    spinner = configure_spinner(@options[:spinner])\n    spinner_overwrites = {}\n    spinner_overwrites[\"mode\"] = @options[:mode] if @options.key?(:mode)\n    @frames   = configure_frames(spinner.merge(spinner_overwrites))\n\n    @interval = (@options[:interval] || spinner[\"interval\"] || SOFT_DEFAULT_OPTIONS[:interval]) * 0.001\n    @stop     = @options[:stop] || spinner[\"stop\"]\n    @status   = @options[:status]\n  end\n\n  def self.start(**options)\n    # optionally overwrite configuration on start\n    configure(**options)\n\n    # only enable once\n    return false if defined?(@enabled) && @enabled\n\n    # set status to enabled\n    @enabled = true\n\n    # only do something if we are on a real terminal (or forced)\n    return false unless @options[:stream].tty? || @options[:non_tty]\n\n    # ensure cursor is visible after exit the program (only register for the very first time)\n    if (!defined?(@at_exit_handler_registered) || !@at_exit_handler_registered) && @options[:hide_cursor]\n      @at_exit_handler_registered = true\n      stream = @options[:stream]\n      at_exit{ stream.print CLI_COMMANDS[:show_cursor] }\n    end\n\n    # init color\n    initialize_color if @options[:color]\n\n    # hide cursor\n    @options[:stream].print CLI_COMMANDS[:hide_cursor] if @options[:hide_cursor]\n\n    # start spinner loop\n    @thread = Thread.new do\n      @current_frame = nil\n      while true # it's just a spinner, no exact timing here\n        next_color if @color\n        render\n        sleep(@interval)\n      end\n    end\n\n    # idiomatic block syntax support\n    if block_given?\n      begin\n        yield\n      ensure\n        Whirly.stop\n      end\n    end\n\n    true\n  end\n\n  def self.stop(stop_frame = nil)\n    return false unless @enabled\n    @enabled = false\n    return false unless @options[:stream].tty? || @options[:non_tty]\n\n    @thread.terminate if @thread\n    render(stop_frame || @stop) if stop_frame || @stop\n    unrender if @options[:remove_after_stop]\n    @options[:stream].puts if @options[:append_newline]\n    @options[:stream].print CLI_COMMANDS[:show_cursor] if @options[:hide_cursor]\n\n    true\n  end\n\n  def self.reset\n    at_exit_handler_registered = defined?(@at_exit_handler_registered) && @at_exit_handler_registered\n    instance_variables.each{ |iv| remove_instance_variable(iv) }\n    @at_exit_handler_registered = at_exit_handler_registered\n    @configured = false\n  end\n\n  # - - -\n\n  def self.unrender\n    return unless @current_frame\n    case @options[:ansi_escape_mode]\n    when \"restore\"\n      @options[:stream].print(render_prefix + (\n          ' ' * (Unicode::DisplayWidth.of(@current_frame, @options[:ambiguous_character_width]) + 1)\n      ) + render_suffix)\n    when \"line\"\n      @options[:stream].print \"\\e[1K\"\n    end\n  end\n\n  def self.render(next_frame = nil)\n    unrender\n\n    @current_frame = next_frame || @frames.next\n    @current_frame = Paint[@current_frame, @color] if @options[:color]\n    @current_frame += \"  #{@status}\" if @status\n\n    @options[:stream].print(render_prefix + @current_frame.to_s + render_suffix)\n  end\n\n  def self.render_prefix\n    res = \"\"\n    res << \"\\n\" if @options[:position] == \"below\"\n    res << \"\\e7\" if @options[:ansi_escape_mode] == \"restore\"\n    res << \"\\e[G\" if @options[:ansi_escape_mode] == \"line\"\n    res\n  end\n\n  def self.render_suffix\n    res = \"\"\n    res << \"\\e8\" if @options[:ansi_escape_mode] == \"restore\"\n    res << \"\\e[1A\" if @options[:position] == \"below\"\n    res\n  end\n\n  def self.initialize_color\n    if !defined?(Paint)\n      warn \"Whirly warning: Using colors requires the paint gem\"\n    else\n      @color = \"%.6x\" % rand(16777216)\n      @color_directions = (0..2).map{ |e| rand(3) - 1 }\n    end\n  end\n\n  def self.next_color\n    @color = @color.scan(/../).map.with_index{ |c, i|\n      color_change = rand(@options[:color_change_rate]) * @color_directions[i]\n      nc = c.to_i(16) + color_change\n      if nc <= 0\n        nc = 0\n        @color_directions[i] = rand(3) - 1\n      elsif nc >= 255\n        nc = 255\n        @color_directions[i] = rand(3) - 1\n      end\n      \"%.2x\" % nc\n    }.join\n  end\nend\n"
  },
  {
    "path": "spec/whirly_spec.rb",
    "content": "require_relative \"../lib/whirly\"\nrequire \"minitest/autorun\"\nrequire \"paint\"\n# require \"irbtools/binding\"\nrequire \"stringio\"\n\ndef short_sleep\n  sleep 0.4\nend\n\ndef medium_sleep\n  sleep 0.7\nend\n\ndef long_sleep\n  sleep 1\nend\n\ndescribe Whirly do\n  before do\n    Whirly.reset\n    @capture = StringIO.new\n    Whirly.configure(non_tty: true, stream: @capture)\n  end\n\n  describe \"General Usage\" do\n    it \"outputs every frame of the spinner\" do\n      spinner = { \"frames\" => [\"first\", \"second\", \"third\"], \"interval\" => 5 }\n\n      Whirly.start(spinner: spinner)\n      short_sleep\n      Whirly.stop\n\n      assert_match /first.*second.*third/m, @capture.string\n    end\n\n    it \"calls spinner proc instead of frames if proc is given\" do\n      spinner = { \"proc\" => ->(){ \"frame\" }, \"interval\" => 5 }\n\n      Whirly.start(spinner: spinner)\n      short_sleep\n      Whirly.stop\n\n      assert_match /frame/, @capture.string\n    end\n  end\n\n  describe \"Status Updates\" do\n    it \"shows status text alongside spinner\" do\n      Whirly.start\n      Whirly.status = \"Fetching…\"\n      medium_sleep\n      Whirly.status = \"Updates…\"\n      medium_sleep\n      Whirly.stop\n\n      assert_match /Fetching.*Updates…/m, @capture.string\n    end\n\n    it \"shows initial status\" do\n      Whirly.start(status: \"Initial\")\n      short_sleep\n      Whirly.stop\n\n      assert_match /Initial/, @capture.string\n    end\n  end\n\n  describe \"Finishing\" do\n    it \"shows spinner finished frame if stop is set in spinner definition\" do\n      spinner = { \"frames\" => [\"first\", \"second\", \"third\"], \"stop\" => \"STOP\", \"interval\" => 5 }\n\n      Whirly.start(spinner: spinner)\n      short_sleep\n      Whirly.stop\n\n      assert_match /STOP/, @capture.string\n    end\n\n    it \"shows spinner finished frame if stop frame is passed when stopping\" do\n      spinner = { \"frames\" => [\"first\", \"second\", \"third\"], \"interval\" => 5 }\n\n      Whirly.start(spinner: spinner)\n      short_sleep\n      Whirly.stop(\"STOP\")\n\n      assert_match /STOP/, @capture.string\n    end\n\n    it \"shows spinner finished frame if stop frame is passed when starting\" do\n      spinner = { \"frames\" => [\"first\", \"second\", \"third\"], \"interval\" => 5 }\n\n      Whirly.start(spinner: spinner, stop: \"STOP\")\n      short_sleep\n      Whirly.stop\n\n      assert_match /STOP/, @capture.string\n    end\n\n    it \"appends newline when stopping\" do\n      Whirly.start(hide_cursor: false)\n      short_sleep\n      Whirly.stop\n\n      assert_match /\\n\\z/, @capture.string\n    end\n\n    it \"appends no newline when stopping when :append_newline option is false\" do\n      Whirly.start(hide_cursor: false, append_newline: false)\n      short_sleep\n      Whirly.stop\n\n      assert_match /[^\\n]\\z/, @capture.string\n    end\n\n    it \"removes the spinner after stopping when :remove_after_stop is true\" do\n      Whirly.start(hide_cursor: false, remove_after_stop: true)\n      short_sleep\n      Whirly.stop\n\n      assert_match /\\e8\\n\\z/, @capture.string\n    end\n  end\n\n  describe \"Spinner\" do\n    describe \"Passing a Spinner\" do\n      it \"can be the name of a bundled spinner (whirly-spinners)\" do\n        Whirly.start(spinner: \"pencil\")\n        medium_sleep\n        Whirly.stop\n\n        assert_match /✎/, @capture.string\n      end\n\n      it \"can be the name of a bundled spinner (cli-spinners)\" do\n        Whirly.start(spinner: \"dots3\")\n        medium_sleep\n        Whirly.stop\n\n        assert_match /⠋/, @capture.string\n      end\n\n      it \"can be an Array of frames\" do\n        Whirly.start(spinner: [\"A\", \"B\"])\n        medium_sleep\n        Whirly.stop\n\n        assert_match /A.*B/m, @capture.string\n      end\n\n      it \"can be an Enumerator of frames\" do\n        Whirly.start(spinner: \"A\"..\"B\")\n        medium_sleep\n        Whirly.stop\n\n        assert_match /A.*B/m, @capture.string\n      end\n\n      it \"can be a Proc which generates frames\" do\n        Whirly.start(spinner: ->(){ \"frame\" })\n        medium_sleep\n        Whirly.stop\n\n        assert_match /frame/m, @capture.string\n      end\n    end\n\n    describe \"Frame Mode\" do\n      it \"can be set to random\" do\n        spinner = { \"frames\" => \"A\"..\"M\", \"mode\" => \"random\", \"interval\" => 10 }\n\n        Whirly.start(spinner: spinner)\n        short_sleep\n        Whirly.stop\n\n        refute /\\A.*?A.*?B.*?C.*?D.*?E.*?F.*?G.*?H.*?I.*?J.*?K.*?L.*?M/m =~ @capture.string\n      end\n\n      it \"can be set to reverse\" do\n        spinner = { \"frames\" => \"A\"..\"H\", \"mode\" => \"reverse\", \"interval\" => 10 }\n\n        Whirly.start(spinner: spinner)\n        medium_sleep\n        Whirly.stop\n\n        assert_match /H.*G.*F.*E.*D.*C.*B.*A/m, @capture.string\n      end\n\n      it \"can be set to swing\" do\n        spinner = { \"frames\" => \"A\"..\"H\", \"mode\" => \"swing\", \"interval\" => 10 }\n\n        Whirly.start(spinner: spinner)\n        medium_sleep\n        Whirly.stop\n\n        assert_match /A.*B.*C.*D.*E.*F.*G.*H.*G.*F.*E.*D.*C.*B.*A/m, @capture.string\n      end\n    end\n\n    describe \"Interval\" do\n      it \"spins more often when interval is lower\" do\n        capture1 = StringIO.new\n        Whirly.start(stream: capture1, interval: 100)\n        long_sleep\n        Whirly.stop\n\n        capture2 = StringIO.new\n        Whirly.start(stream: capture2, interval: 50)\n        long_sleep\n        Whirly.stop\n\n        assert capture1.string.size < capture2.string.size\n      end\n    end\n  end\n\n  describe \"Colors\" do\n    it \"will use no color when :color option is falsey\" do\n      Whirly.start(color: false)\n      short_sleep\n      Whirly.stop\n\n      refute /\\[38;/ =~ @capture.string\n    end\n\n    it \"will use color when :color option is truthy\" do\n      Whirly.start(color: true)\n      short_sleep\n      Whirly.stop\n\n      assert /\\[38;/ =~ @capture.string\n    end\n\n    it \"defaults :color to true when the paint gem is available\" do\n      Whirly.reset\n      Whirly.configure\n      assert Whirly.options[:color]\n    end\n\n    # it \"defaults :color to true when the paint gem is not available\" do\n    #   remember_paint = Paint\n    #   Object.send(:remove_const, :Paint)\n    #   Whirly.reset\n    #   Whirly.configure\n    #   Object.send(:const_set, :Paint, remember_paint)\n    #   refute Whirly.options[:color]\n    # end\n\n    it \"changes the the color\" do\n      Whirly.start\n      long_sleep\n      Whirly.stop\n\n      colors = @capture.string.scan(/\\[38;.*?m/).flatten\n      assert colors.uniq.size > 1\n    end\n  end\n\n  describe \"Cursor\" do\n    it \"hides (and later shows) cursor when :hide_cursor => true option is given (default)\" do\n      Whirly.start(hide_cursor: true)\n      short_sleep\n      Whirly.stop\n\n      assert_match /\\[?25l.*\\[?25h/m, @capture.string\n    end\n\n    it \"does not hide cursor when :hide_cursor => false option is given\" do\n      Whirly.start(hide_cursor: false)\n      short_sleep\n      Whirly.stop\n\n      refute /\\[?25l.*\\[?25h/m =~ @capture.string\n    end\n  end\n\n  describe \"Spinner Packs\" do\n    it \"can be passed an alternative set of :spinner_packs\" do\n      assert_raises ArgumentError do\n        Whirly.start(spinner_packs: [:cli], spinner: \"cat\") # whirly is part of :whirly, but not of :cli\n        Whirly.stop\n      end\n    end\n  end\n\n  describe \"Ansi Escape Mode\" do\n    it \"will use save and restore ANSI sequences as default (or when 'restore') is given\" do\n      Whirly.start\n      short_sleep\n      Whirly.stop\n      assert_match /\\e7.*\\e8/m, @capture.string\n    end\n\n    it \"will use beginning of line and clear line ANSI sequences when 'line' is given\" do\n      Whirly.start(ansi_escape_mode: 'line')\n      medium_sleep\n      Whirly.stop\n      assert_match /\\e\\[G.*\\e\\[1K/m, @capture.string\n    end\n  end\n\n  describe \"Streams and TTYs\" do\n    it \"will not output anything on non-ttys\" do\n      Whirly.reset\n      @capture = StringIO.new\n      Whirly.start(stream: @capture)\n      short_sleep\n      Whirly.stop\n      assert_equal \"\", @capture.string\n    end\n\n    it \"will output something on non-ttys when :non_tty => true option is given\" do\n      Whirly.reset\n      @capture = StringIO.new\n      Whirly.start(stream: @capture, non_tty: true)\n      short_sleep\n      Whirly.stop\n      refute_equal \"\", @capture.string\n    end\n\n    it \"can be configured to which stream whirly's output goes\" do\n      iolike = StringIO.new\n      Whirly.start(stream: iolike, non_tty: true)\n      short_sleep\n      Whirly.stop\n      refute_equal \"\", iolike.string\n    end\n  end\n\n  describe \"Positioning\" do\n    it \"will render spinner 1 line further below (useful for spinning while git cloning)\" do\n      Whirly.start(position: \"below\")\n      short_sleep\n      Whirly.stop\n\n      assert_match /\\n.*\\e\\[1A/m, @capture.string\n    end\n  end\n\n  describe \"Configure and Reset\" do\n    it \"can be configured before starting\" do\n      Whirly.configure spinner: \"dots\", interval: 5\n\n      Whirly.start\n      short_sleep\n      Whirly.stop\n\n      assert_match /⠧/, @capture.string\n    end\n\n    it \"can be reset using .reset\" do\n      Whirly.configure spinner: \"dots\", interval: 5\n      Whirly.reset\n\n      Whirly.start(non_tty: true, stream: @capture)\n      short_sleep\n      Whirly.stop\n      assert_match /\\A[^⠧]+\\z/, @capture.string\n    end\n  end\n\n  describe \".enabled?\" do\n    it \"returns false if whirly was not started yet\" do\n      refute_predicate Whirly, :enabled?\n    end\n\n    it \"returns true if whirly was started, but not yet stopped\" do\n      Whirly.start\n      assert_predicate Whirly, :enabled?\n      Whirly.stop\n    end\n\n    it \"returns false if whirly was stopped\" do\n      Whirly.start\n      Whirly.stop\n      refute_predicate Whirly, :enabled?\n    end\n  end\n\n  describe \"Error Handling\" do\n    it \"stops the spinner, when the main thread threw an exception [gh#3]\" do\n      begin\n        Whirly.start status: \"working\" do\n          short_sleep\n          raise 'error!'\n        end\n      rescue\n      end\n\n      refute_predicate Whirly, :enabled?\n    end\n  end\nend\n"
  },
  {
    "path": "whirly.gemspec",
    "content": "# -*- encoding: utf-8 -*-\n\nrequire File.dirname(__FILE__) + \"/lib/whirly/version\"\n\nGem::Specification.new do |gem|\n  gem.name          = \"whirly\"\n  gem.version       = Whirly::VERSION\n  gem.summary       = \"Whirly: The friendly terminal spinner\"\n  gem.description   = \"Simple terminal spinner with support for custom spinners. Includes spinners from npm's cli-spinners.\"\n  gem.authors       = [\"Jan Lelis\"]\n  gem.email         = [\"hi@ruby.consulting\"]\n  gem.homepage      = \"https://github.com/janlelis/whirly\"\n  gem.license       = \"MIT\"\n\n  gem.files         = Dir[\"{**/}{.*,*}\"].select{ |path| File.file?(path) && path !~ /^(pkg|data)/ } + %w[\n                        data/cli-spinners.json\n                        data/whirly-static-spinners.json\n                      ]\n  gem.executables   = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }\n  gem.test_files    = gem.files.grep(%r{^(test|spec|features)/})\n  gem.require_paths = [\"lib\"]\n\n  gem.add_dependency \"unicode-display_width\", \">= 1.1\"\n  gem.add_dependency \"json\"\n\n  gem.required_ruby_version = \">= 2.0\", \"< 5.0\"\nend\n"
  }
]