├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── deno.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── example.cast └── example.svg ├── deno.json ├── deno.lock ├── examples ├── basic.ts ├── logging.ts └── spinners.ts ├── log_symbols.ts ├── mod.ts └── spinners.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: denosaurs 2 | github: denosaurs 3 | -------------------------------------------------------------------------------- /.github/workflows/deno.yml: -------------------------------------------------------------------------------- 1 | name: deno 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | style: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: checkout repo 10 | uses: actions/checkout@v2 11 | 12 | - name: setup latest deno version 13 | uses: denoland/setup-deno@main 14 | with: 15 | deno-version: v1.x 16 | 17 | - name: run deno fmt 18 | run: deno fmt --check 19 | 20 | - name: run deno lint 21 | run: deno lint --unstable 22 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | workflow_dispatch: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | 11 | permissions: 12 | contents: read 13 | id-token: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Install Deno 18 | uses: denoland/setup-deno@v1 19 | with: 20 | deno-version: v1.x 21 | 22 | - name: Publish package 23 | run: deno publish 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS files 2 | .DS_Store 3 | .cache 4 | 5 | # IDE 6 | .vscode 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog], and this project adheres to 6 | [Semantic Versioning]. 7 | 8 | ## [0.1.12] - 2021-09-15 9 | 10 | ### Bug Fixes 11 | 12 | - workers and lint error ([`bae9932`]) 13 | 14 | ## [0.1.11] - 2021-06-06 15 | 16 | ## [0.1.10] - 2021-02-02 17 | 18 | ### Bug Fixes 19 | 20 | - disable windows check in favour of setting encoding yourself on windows 21 | ([`74b6e34`]) 22 | 23 | ## [0.1.9] - 2020-11-21 24 | 25 | ## [0.1.8] - 2020-10-03 26 | 27 | ### Bug Fixes 28 | 29 | - potentially fix #2 ([`ad76632`]) 30 | - remove stray newline ([`2188df1`]) 31 | - :bug: hide cursor on Windows ([`75c5fdd`]) 32 | 33 | ## [0.1.7] - 2020-09-19 34 | 35 | ### Bug Fixes 36 | 37 | - windows symbols ([`ef26621`]) 38 | - use import type to import types ([`cc86f35`]) 39 | 40 | ## [0.1.6] - 2020-08-30 41 | 42 | ### Bug Fixes 43 | 44 | - remove on exit ([`e353b72`]) 45 | 46 | ## [0.1.5] - 2020-08-26 47 | 48 | ### Bug Fixes 49 | 50 | - export spinner ([`4782f43`]) 51 | 52 | ## [0.1.4] - 2020-08-25 53 | 54 | ### Bug Fixes 55 | 56 | - reset animation when spinner is changed ([`7ddb7a3`]) 57 | 58 | ## [0.1.3] - 2020-08-25 59 | 60 | ### Bug Fixes 61 | 62 | - formatting ([`d9f4d20`]) 63 | 64 | ## [0.1.2] - 2020-08-25 65 | 66 | ### Bug Fixes 67 | 68 | - windows log_symbols support ([`247f0a9`]) 69 | 70 | ## [0.1.1] - 2020-08-25 71 | 72 | ### Bug Fixes 73 | 74 | - windows commandline issues ([`c36120b`]) 75 | 76 | ## [0.1.0] - 2020-08-25 77 | 78 | ### Features 79 | 80 | - initial release ([`b419fd0`]) 81 | 82 | [keep a changelog]: https://keepachangelog.com/en/1.0.0/ 83 | [semantic versioning]: https://semver.org/spec/v2.0.0.html 84 | [0.1.12]: https://github.com/denosaurs/wait/compare/0.1.11...0.1.12 85 | [`bae9932`]: https://github.com/denosaurs/wait/commit/bae99325e72a16544701bad50962147b40dd090d 86 | [0.1.11]: https://github.com/denosaurs/wait/compare/0.1.10...0.1.11 87 | [0.1.10]: https://github.com/denosaurs/wait/compare/0.1.9...0.1.10 88 | [`74b6e34`]: https://github.com/denosaurs/wait/commit/74b6e3498e6df91960ff2454e361b9863ec8ee78 89 | [0.1.9]: https://github.com/denosaurs/wait/compare/0.1.8...0.1.9 90 | [0.1.8]: https://github.com/denosaurs/wait/compare/0.1.7...0.1.8 91 | [`ad76632`]: https://github.com/denosaurs/wait/commit/ad76632c798d32eabd1dc3bcb1d90c8f6cd02522 92 | [`2188df1`]: https://github.com/denosaurs/wait/commit/2188df1f09b57acd52dfc812afad39d3c33e8e7a 93 | [`75c5fdd`]: https://github.com/denosaurs/wait/commit/75c5fdd6fe68f8b4fc0685bc86382cc49dd5a816 94 | [0.1.7]: https://github.com/denosaurs/wait/compare/0.1.6...0.1.7 95 | [`ef26621`]: https://github.com/denosaurs/wait/commit/ef26621583edff99bf04e37386144fbbcceb0107 96 | [`cc86f35`]: https://github.com/denosaurs/wait/commit/cc86f3515936006ea981d0ec20fe07c7e672902c 97 | [0.1.6]: https://github.com/denosaurs/wait/compare/0.1.5...0.1.6 98 | [`e353b72`]: https://github.com/denosaurs/wait/commit/e353b729611cf0a7110c9e176e7fd4fd35a1a614 99 | [0.1.5]: https://github.com/denosaurs/wait/compare/0.1.4...0.1.5 100 | [`4782f43`]: https://github.com/denosaurs/wait/commit/4782f432a296e59df5aacf61a72d006615c064df 101 | [0.1.4]: https://github.com/denosaurs/wait/compare/0.1.3...0.1.4 102 | [`7ddb7a3`]: https://github.com/denosaurs/wait/commit/7ddb7a33862059370ea208f98c2b036d26c7b224 103 | [0.1.3]: https://github.com/denosaurs/wait/compare/0.1.2...0.1.3 104 | [`d9f4d20`]: https://github.com/denosaurs/wait/commit/d9f4d207a7b306869a6f4194c0f4db3a8c5eba2f 105 | [0.1.2]: https://github.com/denosaurs/wait/compare/0.1.1...0.1.2 106 | [`247f0a9`]: https://github.com/denosaurs/wait/commit/247f0a901460d58f7a62b1f8e6ee751d7e42c7bf 107 | [0.1.1]: https://github.com/denosaurs/wait/compare/0.1.0...0.1.1 108 | [`c36120b`]: https://github.com/denosaurs/wait/commit/c36120b4f09250fbef9ea6bbedfd4b069b3b03bc 109 | [0.1.0]: https://github.com/denosaurs/wait/compare/0.1.0 110 | [`b419fd0`]: https://github.com/denosaurs/wait/commit/b419fd0e7c15d745f69e304b22da51779c7ab0d4 111 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2024 the denosaurs team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wait 2 | 3 | [![Tags](https://img.shields.io/github/release/denosaurs/wait)](https://github.com/denosaurs/wait/releases) 4 | [![CI Status](https://img.shields.io/github/actions/workflow/status/denosaurs/wait/deno.yml?branch=master)](https://github.com/denosaurs/wait/actions) 5 | [![License](https://img.shields.io/github/license/denosaurs/wait)](https://github.com/denosaurs/wait/blob/master/LICENSE) 6 | 7 |

8 |
9 | 10 |
11 |

12 | 13 | ## Usage 14 | 15 | ```typescript 16 | import { wait } from "https://deno.land/x/wait/mod.ts"; 17 | 18 | const spinner = wait("Generating terrain").start(); 19 | 20 | setTimeout(() => { 21 | spinner.color = "yellow"; 22 | spinner.text = "Loading dinosaurs"; 23 | }, 1500); 24 | ``` 25 | 26 | ## Other 27 | 28 | ### Related 29 | 30 | - [ora](https://github.com/sindresorhus/ora) - Elegant terminal spinner 31 | 32 | ### Contribution 33 | 34 | Pull request, issues and feedback are very welcome. Code style is formatted with 35 | deno fmt and commit messages are done following Conventional Commits spec. 36 | 37 | ### Licence 38 | 39 | Copyright 2020-present, the denosaurs team. All rights reserved. MIT license. 40 | -------------------------------------------------------------------------------- /assets/example.cast: -------------------------------------------------------------------------------- 1 | {"version": 2, "width": 78, "height": 58, "timestamp": 1598874244, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} 2 | [0.041232, "o", "\u001b[?25l"] 3 | [0.042201, "o", "\u001b[36m⠋\u001b[39m Loading mesozoic\r\n"] 4 | [0.145164, "o", "\u001b[1A\u001b[2K"] 5 | [0.145229, "o", "\u001b[-1C"] 6 | [0.145391, "o", "\u001b[36m⠙\u001b[39m Loading mesozoic\r\n"] 7 | [0.244284, "o", "\u001b[1A\u001b[2K"] 8 | [0.244731, "o", "\u001b[-1C\u001b[36m⠹\u001b[39m Loading mesozoic\r\n"] 9 | [0.344938, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 10 | [0.345325, "o", "\u001b[36m⠸\u001b[39m Loading mesozoic\r\n"] 11 | [0.444039, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 12 | [0.444224, "o", "\u001b[36m⠼\u001b[39m Loading mesozoic\r\n"] 13 | [0.545667, "o", "\u001b[1A\u001b[2K\u001b[-1C\u001b[36m⠴\u001b[39m Loading mesozoic\r\n"] 14 | [0.643031, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 15 | [0.643112, "o", "\u001b[36m⠦\u001b[39m Loading mesozoic\r\n"] 16 | [0.748393, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 17 | [0.748717, "o", "\u001b[36m⠧\u001b[39m Loading mesozoic\r\n"] 18 | [0.844024, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 19 | [0.844208, "o", "\u001b[36m⠇\u001b[39m Loading mesozoic\r\n"] 20 | [0.948089, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 21 | [0.948428, "o", "\u001b[36m⠏\u001b[39m Loading mesozoic\r\n"] 22 | [1.04573, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 23 | [1.046035, "o", "\u001b[33m⠋\u001b[39m Loading meteorite\r\n"] 24 | [1.148118, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 25 | [1.148296, "o", "\u001b[33m⠙\u001b[39m Loading meteorite\r\n"] 26 | [1.24514, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 27 | [1.245444, "o", "\u001b[33m⠹\u001b[39m Loading meteorite\r\n"] 28 | [1.34847, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 29 | [1.348856, "o", "\u001b[33m⠸\u001b[39m Loading meteorite\r\n"] 30 | [1.446044, "o", "\u001b[1A\u001b[2K\u001b[-1C\u001b[33m⠼\u001b[39m Loading meteorite\r\n"] 31 | [1.548466, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 32 | [1.548771, "o", "\u001b[33m⠴\u001b[39m Loading meteorite\r\n"] 33 | [1.645888, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 34 | [1.646238, "o", "\u001b[33m⠦\u001b[39m Loading meteorite\r\n"] 35 | [1.747362, "o", "\u001b[1A\u001b[2K\u001b[-1C\u001b[33m⠧\u001b[39m Loading meteorite\r\n"] 36 | [1.846944, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 37 | [1.847248, "o", "\u001b[33m⠇\u001b[39m Loading meteorite\r\n"] 38 | [1.94426, "o", "\u001b[1A\u001b[2K\u001b[-1C\u001b[33m⠏\u001b[39m Loading meteorite\r\n"] 39 | [2.044788, "o", "\u001b[1A\u001b[2K\u001b[-1C"] 40 | [2.044869, "o", "\u001b[?25h\u001b[32m✔\u001b[39m Started human race\r\n"] 41 | -------------------------------------------------------------------------------- /assets/example.svg: -------------------------------------------------------------------------------- 1 | GeneratingterrainGeneratingterrainGeneratingterrainGeneratingterrainGeneratingterrainGeneratingterrainLoadingdinosaursLoadingdinosaursLoadingdinosaursLoadingdinosaursLoadingdinosaursGeneratingterrainGeneratingterrainGeneratingterrainGeneratingterrainLoadingdinosaursLoadingdinosaursLoadingdinosaursLoadingdinosaursLoadingdinosaursMesozoicdeployed! -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@denosaurs/wait", 3 | "version": "0.2.2", 4 | "exports": { 5 | ".": "./mod.ts", 6 | "./spinners": "./spinners.ts", 7 | "./log_symbols": "./log_symbols.ts" 8 | }, 9 | "imports": { 10 | "@denosaurs/tty": "jsr:@denosaurs/tty@^0.2.1", 11 | "@std/fmt": "jsr:@std/fmt@^1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3", 3 | "packages": { 4 | "specifiers": { 5 | "jsr:@denosaurs/tty@^0.2.0": "jsr:@denosaurs/tty@0.2.0", 6 | "jsr:@denosaurs/tty@^0.2.1": "jsr:@denosaurs/tty@0.2.1", 7 | "jsr:@std/fmt@^1.0.2": "jsr:@std/fmt@1.0.2" 8 | }, 9 | "jsr": { 10 | "@denosaurs/tty@0.2.0": { 11 | "integrity": "7bffd8da2faf026ed71e6a2292f058f49beda831bc9d8d8e141fb2f844f23b08" 12 | }, 13 | "@denosaurs/tty@0.2.1": { 14 | "integrity": "f1fc651cc9021c90bf230a45c6eb893c26a54fae6a855e9cd26aad0083d772dd" 15 | }, 16 | "@std/fmt@1.0.2": { 17 | "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7" 18 | } 19 | } 20 | }, 21 | "remote": {}, 22 | "workspace": { 23 | "dependencies": [ 24 | "jsr:@denosaurs/tty@^0.2.0", 25 | "jsr:@std/fmt@^1.0.2" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/basic.ts: -------------------------------------------------------------------------------- 1 | import { wait } from "../mod.ts"; 2 | 3 | const spinner = wait("Generating terrain").start(); 4 | 5 | setTimeout(() => { 6 | spinner.color = "yellow"; 7 | spinner.text = "Loading dinosaurs"; 8 | }, 1500); 9 | 10 | setTimeout(() => { 11 | spinner.succeed("Mesozoic deployed!"); 12 | }, 3000); 13 | -------------------------------------------------------------------------------- /examples/logging.ts: -------------------------------------------------------------------------------- 1 | import { wait } from "../mod.ts"; 2 | 3 | const spinner = wait({ 4 | text: "Generating\nterrain", 5 | color: "red", 6 | interceptConsole: true, 7 | }); 8 | 9 | spinner.start(); 10 | 11 | console.time("test"); 12 | 13 | setTimeout(() => { 14 | console.timeLog("test"); 15 | console.log("This log message should\nnot be affected by the spinner"); 16 | }, 1000); 17 | 18 | setTimeout(() => { 19 | spinner.color = "yellow"; 20 | spinner.text = "Loading dinosaurs"; 21 | console.table([{ a: 1, b: "Y" }, { a: "Z", b: 2 }]); 22 | }, 2000); 23 | 24 | setTimeout(() => { 25 | spinner.color = "blue"; 26 | spinner.text = "Loading beasts"; 27 | console.timeEnd("test"); 28 | }, 3000); 29 | 30 | setTimeout(() => { 31 | spinner.color = "blue"; 32 | spinner.text = "Loading beasts"; 33 | console.assert(false, "This is an error message"); 34 | console.dir({ a: 1, b: "Y" }); 35 | console.dirxml({ a: 1, b: "Y" }); 36 | console.count("count"); 37 | console.countReset("count"); 38 | }, 3500); 39 | 40 | setTimeout(() => { 41 | spinner.succeed("Mesozoic deployed!"); 42 | }, 4000); 43 | 44 | setTimeout(() => { 45 | console.error("byee"); 46 | }, 5000); 47 | -------------------------------------------------------------------------------- /examples/spinners.ts: -------------------------------------------------------------------------------- 1 | import { wait } from "../mod.ts"; 2 | import spinners from "../spinners.ts"; 3 | 4 | const spinner = wait("Loading something really really heavy").start(); 5 | 6 | let colorIdx = 0; 7 | const colors: string[] = [ 8 | "black", 9 | "red", 10 | "green", 11 | "yellow", 12 | "blue", 13 | "magenta", 14 | "cyan", 15 | "white", 16 | "gray", 17 | ]; 18 | 19 | let spinIdx = 0; 20 | const spins = Object.keys(spinners); 21 | 22 | setInterval(() => { 23 | colorIdx = ++colorIdx % colors.length; 24 | spinner.color = colors[colorIdx]; 25 | }, 500); 26 | 27 | setInterval(() => { 28 | spinIdx = ++spinIdx % spins.length; 29 | spinner.spinner = spins[spinIdx]; 30 | }, 1000); 31 | -------------------------------------------------------------------------------- /log_symbols.ts: -------------------------------------------------------------------------------- 1 | import * as colors from "@std/fmt/colors"; 2 | 3 | let supported = true; 4 | 5 | if ((await Deno.permissions.query({ name: "env" })).state === "granted") { 6 | supported = supported && 7 | (!!Deno.env.get("CI") || Deno.env.get("TERM") === "xterm-256color"); 8 | } 9 | 10 | export type SymbolType = "info" | "success" | "warning" | "error"; 11 | export type SymbolRecord = { [key in SymbolType]: string }; 12 | 13 | export const main: SymbolRecord = { 14 | info: colors.blue("ℹ"), 15 | success: colors.green("✔"), 16 | warning: colors.yellow("⚠"), 17 | error: colors.red("✖"), 18 | }; 19 | 20 | export const fallbacks: SymbolRecord = { 21 | info: colors.blue("i"), 22 | success: colors.green("√"), 23 | warning: colors.yellow("‼"), 24 | error: colors.red("×"), 25 | }; 26 | 27 | export const symbols: SymbolRecord = supported ? main : fallbacks; 28 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | import * as colors from "@std/fmt/colors"; 2 | import * as tty from "@denosaurs/tty"; 3 | 4 | import spinners from "./spinners.ts"; 5 | import { symbols } from "./log_symbols.ts"; 6 | 7 | export { spinners, symbols }; 8 | 9 | const encoder = new TextEncoder(); 10 | 11 | type ColorFunction = (message: string) => string; 12 | const colormap: { [key: string]: ColorFunction } = { 13 | black: colors.black, 14 | red: colors.red, 15 | green: colors.green, 16 | yellow: colors.yellow, 17 | blue: colors.blue, 18 | magenta: colors.magenta, 19 | cyan: colors.cyan, 20 | white: colors.white, 21 | gray: colors.gray, 22 | }; 23 | 24 | export interface SpinnerAnimation { 25 | interval: number; 26 | frames: string[]; 27 | } 28 | 29 | export interface SpinnerOptions { 30 | text: string; 31 | prefix?: string; 32 | spinner?: string | SpinnerAnimation; 33 | color?: string | ColorFunction; 34 | hideCursor?: boolean; 35 | indent?: number; 36 | interval?: number; 37 | stream?: tty.SyncStream; 38 | enabled?: boolean; 39 | discardStdin?: boolean; 40 | interceptConsole?: boolean; 41 | } 42 | 43 | export interface PersistOptions { 44 | prefix?: string; 45 | symbol?: string; 46 | text?: string; 47 | } 48 | 49 | export type Console = typeof globalThis.console; 50 | 51 | export function wait(opts: string | SpinnerOptions): Spinner { 52 | if (typeof opts === "string") { 53 | opts = { text: opts }; 54 | } 55 | return new Spinner({ 56 | text: opts.text, 57 | prefix: opts.prefix ?? "", 58 | color: opts.color ?? colors.cyan, 59 | spinner: opts.spinner ?? "dots", 60 | hideCursor: opts.hideCursor ?? true, 61 | indent: opts.indent ?? 0, 62 | interval: opts.interval ?? 100, 63 | stream: opts.stream ?? Deno.stdout, 64 | enabled: true, 65 | discardStdin: true, 66 | interceptConsole: opts.interceptConsole ?? true, 67 | }); 68 | } 69 | 70 | export class Spinner { 71 | #opts: Required; 72 | 73 | isSpinning: boolean; 74 | 75 | #stream: tty.SyncStream; 76 | indent: number; 77 | interval: number; 78 | 79 | #id = 0; 80 | 81 | #enabled: boolean; 82 | #frameIndex: number; 83 | #linesToClear: number; 84 | #linesCount: number; 85 | 86 | constructor(opts: Required) { 87 | this.#opts = opts; 88 | 89 | this.#stream = this.#opts.stream; 90 | 91 | this.text = this.#opts.text; 92 | this.prefix = this.#opts.prefix; 93 | 94 | this.color = this.#opts.color; 95 | this.spinner = this.#opts.spinner; 96 | this.indent = this.#opts.indent; 97 | this.interval = this.#opts.interval; 98 | 99 | this.isSpinning = false; 100 | this.#frameIndex = 0; 101 | this.#linesToClear = 0; 102 | this.#linesCount = 1; 103 | 104 | this.#enabled = typeof opts.enabled === "boolean" 105 | ? opts.enabled 106 | : tty.isInteractive(this.#stream); 107 | 108 | if (opts.hideCursor) { 109 | addEventListener("unload", () => { 110 | tty.showCursorSync(this.#stream); 111 | }); 112 | } 113 | 114 | if (opts.interceptConsole) { 115 | this.#interceptConsole(); 116 | } 117 | } 118 | 119 | #spinner: SpinnerAnimation = spinners.dots; 120 | #color: ColorFunction = colors.cyan; 121 | #text = ""; 122 | #prefix = ""; 123 | 124 | #interceptConsole(): void { 125 | const methods: (keyof Console)[] = [ 126 | "log", 127 | "debug", 128 | "info", 129 | "dir", 130 | "dirxml", 131 | "warn", 132 | "error", 133 | "assert", 134 | "count", 135 | "countReset", 136 | "table", 137 | "time", 138 | "timeLog", 139 | "timeEnd", 140 | "group", 141 | "groupCollapsed", 142 | "groupEnd", 143 | "clear", 144 | "trace", 145 | "profile", 146 | "profileEnd", 147 | "timeStamp", 148 | ]; 149 | for (const method of methods) { 150 | const original = console[method] as (...args: unknown[]) => void; 151 | // @ts-ignore Ignore the next line for dnt to type check properly 152 | console[method] = (...args: unknown[]) => { 153 | if (this.isSpinning) { 154 | this.stop(); 155 | this.clear(); 156 | original(...args); 157 | this.start(); 158 | } else { 159 | original(...args); 160 | } 161 | }; 162 | } 163 | } 164 | 165 | set spinner(spin: string | SpinnerAnimation) { 166 | this.#frameIndex = 0; 167 | if (typeof spin === "string") this.#spinner = spinners[spin]; 168 | else this.#spinner = spin; 169 | } 170 | 171 | get spinner(): SpinnerAnimation { 172 | return this.#spinner; 173 | } 174 | 175 | set color(color: string | ColorFunction) { 176 | if (typeof color === "string") this.#color = colormap[color]; 177 | else this.#color = color; 178 | } 179 | 180 | get color(): ColorFunction { 181 | return this.#color; 182 | } 183 | 184 | set text(value: string) { 185 | this.#text = value; 186 | this.updateLines(); 187 | } 188 | 189 | get text(): string { 190 | return this.#text; 191 | } 192 | set prefix(value: string) { 193 | this.#prefix = value; 194 | this.updateLines(); 195 | } 196 | 197 | get prefix(): string { 198 | return this.#prefix; 199 | } 200 | 201 | #write(data: string): void { 202 | this.#stream.writeSync(encoder.encode(data)); 203 | } 204 | 205 | start(): Spinner { 206 | if (!this.#enabled) { 207 | if (this.text) { 208 | this.#write(`- ${this.text}\n`); 209 | } 210 | return this; 211 | } 212 | 213 | if (this.isSpinning) return this; 214 | 215 | if (this.#opts.hideCursor) { 216 | tty.hideCursorSync(this.#stream); 217 | } 218 | this.isSpinning = true; 219 | this.render(); 220 | this.#id = setInterval(this.render.bind(this), this.interval); 221 | return this; 222 | } 223 | 224 | render(): void { 225 | this.clear(); 226 | this.#write(`${this.frame()}\n`); 227 | this.updateLines(); 228 | this.#linesToClear = this.#linesCount; 229 | } 230 | 231 | frame(): string { 232 | const { frames } = this.#spinner; 233 | let frame = frames[this.#frameIndex]; 234 | 235 | frame = this.#color(frame); 236 | 237 | this.#frameIndex = ++this.#frameIndex % frames.length; 238 | const fullPrefixText = typeof this.prefix === "string" && this.prefix !== "" 239 | ? this.prefix + " " 240 | : ""; 241 | const fullText = typeof this.text === "string" ? " " + this.text : ""; 242 | 243 | return fullPrefixText + frame + fullText; 244 | } 245 | 246 | clear(): void { 247 | if (!this.#enabled) return; 248 | 249 | for (let i = 0; i < this.#linesToClear; i++) { 250 | tty.goUpSync(1, this.#stream); 251 | tty.clearLineSync(this.#stream); 252 | tty.goRightSync(this.indent - 1, this.#stream); 253 | } 254 | 255 | this.#linesToClear = 0; 256 | } 257 | 258 | updateLines(): void { 259 | let columns = 80; 260 | try { 261 | //@ts-ignore TS2339 262 | columns = Deno.consoleSize().columns ?? columns; 263 | } catch { 264 | // Unstable APIs is not enabled, fallback to default 265 | } 266 | 267 | const fullPrefixText = typeof this.prefix === "string" 268 | ? this.prefix + "-" 269 | : ""; 270 | 271 | this.#linesCount = tty 272 | .stripAnsi(fullPrefixText + "--" + this.text) 273 | .split("\n") 274 | .reduce((count, line) => { 275 | return count + Math.max(1, Math.ceil(tty.wcswidth(line) / columns)); 276 | }, 0); 277 | } 278 | 279 | stop(): void { 280 | if (!this.#enabled) return; 281 | clearInterval(this.#id); 282 | this.#id = -1; 283 | this.#frameIndex = 0; 284 | this.clear(); 285 | this.isSpinning = false; 286 | if (this.#opts.hideCursor) { 287 | tty.showCursorSync(this.#stream); 288 | } 289 | } 290 | 291 | stopAndPersist(options: PersistOptions = {}): void { 292 | const prefix = options.prefix || this.prefix; 293 | const fullPrefix = typeof prefix === "string" && prefix !== "" 294 | ? prefix + " " 295 | : ""; 296 | const text = options.text || this.text; 297 | const fullText = typeof text === "string" ? " " + text : ""; 298 | 299 | this.stop(); 300 | // https://github.com/denoland/deno/issues/6001 301 | this.#write(`${fullPrefix}${options.symbol || " "}${fullText}\n`); 302 | } 303 | 304 | succeed(text?: string): void { 305 | return this.stopAndPersist({ symbol: symbols.success, text }); 306 | } 307 | 308 | fail(text?: string): void { 309 | return this.stopAndPersist({ symbol: symbols.error, text }); 310 | } 311 | 312 | warn(text?: string): void { 313 | return this.stopAndPersist({ symbol: symbols.warning, text }); 314 | } 315 | 316 | info(text?: string): void { 317 | return this.stopAndPersist({ symbol: symbols.info, text }); 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /spinners.ts: -------------------------------------------------------------------------------- 1 | import type { SpinnerAnimation } from "./mod.ts"; 2 | 3 | export default <{ [key: string]: SpinnerAnimation }> { 4 | dots: { 5 | interval: 80, 6 | frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"], 7 | }, 8 | dots2: { 9 | interval: 80, 10 | frames: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"], 11 | }, 12 | dots3: { 13 | interval: 80, 14 | frames: ["⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"], 15 | }, 16 | dots4: { 17 | interval: 80, 18 | frames: [ 19 | "⠄", 20 | "⠆", 21 | "⠇", 22 | "⠋", 23 | "⠙", 24 | "⠸", 25 | "⠰", 26 | "⠠", 27 | "⠰", 28 | "⠸", 29 | "⠙", 30 | "⠋", 31 | "⠇", 32 | "⠆", 33 | ], 34 | }, 35 | dots5: { 36 | interval: 80, 37 | frames: [ 38 | "⠋", 39 | "⠙", 40 | "⠚", 41 | "⠒", 42 | "⠂", 43 | "⠂", 44 | "⠒", 45 | "⠲", 46 | "⠴", 47 | "⠦", 48 | "⠖", 49 | "⠒", 50 | "⠐", 51 | "⠐", 52 | "⠒", 53 | "⠓", 54 | "⠋", 55 | ], 56 | }, 57 | dots6: { 58 | interval: 80, 59 | frames: [ 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 | dots7: { 87 | interval: 80, 88 | frames: [ 89 | "⠈", 90 | "⠉", 91 | "⠋", 92 | "⠓", 93 | "⠒", 94 | "⠐", 95 | "⠐", 96 | "⠒", 97 | "⠖", 98 | "⠦", 99 | "⠤", 100 | "⠠", 101 | "⠠", 102 | "⠤", 103 | "⠦", 104 | "⠖", 105 | "⠒", 106 | "⠐", 107 | "⠐", 108 | "⠒", 109 | "⠓", 110 | "⠋", 111 | "⠉", 112 | "⠈", 113 | ], 114 | }, 115 | dots8: { 116 | interval: 80, 117 | frames: [ 118 | "⠁", 119 | "⠁", 120 | "⠉", 121 | "⠙", 122 | "⠚", 123 | "⠒", 124 | "⠂", 125 | "⠂", 126 | "⠒", 127 | "⠲", 128 | "⠴", 129 | "⠤", 130 | "⠄", 131 | "⠄", 132 | "⠤", 133 | "⠠", 134 | "⠠", 135 | "⠤", 136 | "⠦", 137 | "⠖", 138 | "⠒", 139 | "⠐", 140 | "⠐", 141 | "⠒", 142 | "⠓", 143 | "⠋", 144 | "⠉", 145 | "⠈", 146 | "⠈", 147 | ], 148 | }, 149 | dots9: { 150 | interval: 80, 151 | frames: ["⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"], 152 | }, 153 | dots10: { 154 | interval: 80, 155 | frames: ["⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"], 156 | }, 157 | dots11: { 158 | interval: 100, 159 | frames: ["⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"], 160 | }, 161 | dots12: { 162 | interval: 80, 163 | frames: [ 164 | "⢀⠀", 165 | "⡀⠀", 166 | "⠄⠀", 167 | "⢂⠀", 168 | "⡂⠀", 169 | "⠅⠀", 170 | "⢃⠀", 171 | "⡃⠀", 172 | "⠍⠀", 173 | "⢋⠀", 174 | "⡋⠀", 175 | "⠍⠁", 176 | "⢋⠁", 177 | "⡋⠁", 178 | "⠍⠉", 179 | "⠋⠉", 180 | "⠋⠉", 181 | "⠉⠙", 182 | "⠉⠙", 183 | "⠉⠩", 184 | "⠈⢙", 185 | "⠈⡙", 186 | "⢈⠩", 187 | "⡀⢙", 188 | "⠄⡙", 189 | "⢂⠩", 190 | "⡂⢘", 191 | "⠅⡘", 192 | "⢃⠨", 193 | "⡃⢐", 194 | "⠍⡐", 195 | "⢋⠠", 196 | "⡋⢀", 197 | "⠍⡁", 198 | "⢋⠁", 199 | "⡋⠁", 200 | "⠍⠉", 201 | "⠋⠉", 202 | "⠋⠉", 203 | "⠉⠙", 204 | "⠉⠙", 205 | "⠉⠩", 206 | "⠈⢙", 207 | "⠈⡙", 208 | "⠈⠩", 209 | "⠀⢙", 210 | "⠀⡙", 211 | "⠀⠩", 212 | "⠀⢘", 213 | "⠀⡘", 214 | "⠀⠨", 215 | "⠀⢐", 216 | "⠀⡐", 217 | "⠀⠠", 218 | "⠀⢀", 219 | "⠀⡀", 220 | ], 221 | }, 222 | dots8Bit: { 223 | interval: 80, 224 | frames: [ 225 | "⠀", 226 | "⠁", 227 | "⠂", 228 | "⠃", 229 | "⠄", 230 | "⠅", 231 | "⠆", 232 | "⠇", 233 | "⡀", 234 | "⡁", 235 | "⡂", 236 | "⡃", 237 | "⡄", 238 | "⡅", 239 | "⡆", 240 | "⡇", 241 | "⠈", 242 | "⠉", 243 | "⠊", 244 | "⠋", 245 | "⠌", 246 | "⠍", 247 | "⠎", 248 | "⠏", 249 | "⡈", 250 | "⡉", 251 | "⡊", 252 | "⡋", 253 | "⡌", 254 | "⡍", 255 | "⡎", 256 | "⡏", 257 | "⠐", 258 | "⠑", 259 | "⠒", 260 | "⠓", 261 | "⠔", 262 | "⠕", 263 | "⠖", 264 | "⠗", 265 | "⡐", 266 | "⡑", 267 | "⡒", 268 | "⡓", 269 | "⡔", 270 | "⡕", 271 | "⡖", 272 | "⡗", 273 | "⠘", 274 | "⠙", 275 | "⠚", 276 | "⠛", 277 | "⠜", 278 | "⠝", 279 | "⠞", 280 | "⠟", 281 | "⡘", 282 | "⡙", 283 | "⡚", 284 | "⡛", 285 | "⡜", 286 | "⡝", 287 | "⡞", 288 | "⡟", 289 | "⠠", 290 | "⠡", 291 | "⠢", 292 | "⠣", 293 | "⠤", 294 | "⠥", 295 | "⠦", 296 | "⠧", 297 | "⡠", 298 | "⡡", 299 | "⡢", 300 | "⡣", 301 | "⡤", 302 | "⡥", 303 | "⡦", 304 | "⡧", 305 | "⠨", 306 | "⠩", 307 | "⠪", 308 | "⠫", 309 | "⠬", 310 | "⠭", 311 | "⠮", 312 | "⠯", 313 | "⡨", 314 | "⡩", 315 | "⡪", 316 | "⡫", 317 | "⡬", 318 | "⡭", 319 | "⡮", 320 | "⡯", 321 | "⠰", 322 | "⠱", 323 | "⠲", 324 | "⠳", 325 | "⠴", 326 | "⠵", 327 | "⠶", 328 | "⠷", 329 | "⡰", 330 | "⡱", 331 | "⡲", 332 | "⡳", 333 | "⡴", 334 | "⡵", 335 | "⡶", 336 | "⡷", 337 | "⠸", 338 | "⠹", 339 | "⠺", 340 | "⠻", 341 | "⠼", 342 | "⠽", 343 | "⠾", 344 | "⠿", 345 | "⡸", 346 | "⡹", 347 | "⡺", 348 | "⡻", 349 | "⡼", 350 | "⡽", 351 | "⡾", 352 | "⡿", 353 | "⢀", 354 | "⢁", 355 | "⢂", 356 | "⢃", 357 | "⢄", 358 | "⢅", 359 | "⢆", 360 | "⢇", 361 | "⣀", 362 | "⣁", 363 | "⣂", 364 | "⣃", 365 | "⣄", 366 | "⣅", 367 | "⣆", 368 | "⣇", 369 | "⢈", 370 | "⢉", 371 | "⢊", 372 | "⢋", 373 | "⢌", 374 | "⢍", 375 | "⢎", 376 | "⢏", 377 | "⣈", 378 | "⣉", 379 | "⣊", 380 | "⣋", 381 | "⣌", 382 | "⣍", 383 | "⣎", 384 | "⣏", 385 | "⢐", 386 | "⢑", 387 | "⢒", 388 | "⢓", 389 | "⢔", 390 | "⢕", 391 | "⢖", 392 | "⢗", 393 | "⣐", 394 | "⣑", 395 | "⣒", 396 | "⣓", 397 | "⣔", 398 | "⣕", 399 | "⣖", 400 | "⣗", 401 | "⢘", 402 | "⢙", 403 | "⢚", 404 | "⢛", 405 | "⢜", 406 | "⢝", 407 | "⢞", 408 | "⢟", 409 | "⣘", 410 | "⣙", 411 | "⣚", 412 | "⣛", 413 | "⣜", 414 | "⣝", 415 | "⣞", 416 | "⣟", 417 | "⢠", 418 | "⢡", 419 | "⢢", 420 | "⢣", 421 | "⢤", 422 | "⢥", 423 | "⢦", 424 | "⢧", 425 | "⣠", 426 | "⣡", 427 | "⣢", 428 | "⣣", 429 | "⣤", 430 | "⣥", 431 | "⣦", 432 | "⣧", 433 | "⢨", 434 | "⢩", 435 | "⢪", 436 | "⢫", 437 | "⢬", 438 | "⢭", 439 | "⢮", 440 | "⢯", 441 | "⣨", 442 | "⣩", 443 | "⣪", 444 | "⣫", 445 | "⣬", 446 | "⣭", 447 | "⣮", 448 | "⣯", 449 | "⢰", 450 | "⢱", 451 | "⢲", 452 | "⢳", 453 | "⢴", 454 | "⢵", 455 | "⢶", 456 | "⢷", 457 | "⣰", 458 | "⣱", 459 | "⣲", 460 | "⣳", 461 | "⣴", 462 | "⣵", 463 | "⣶", 464 | "⣷", 465 | "⢸", 466 | "⢹", 467 | "⢺", 468 | "⢻", 469 | "⢼", 470 | "⢽", 471 | "⢾", 472 | "⢿", 473 | "⣸", 474 | "⣹", 475 | "⣺", 476 | "⣻", 477 | "⣼", 478 | "⣽", 479 | "⣾", 480 | "⣿", 481 | ], 482 | }, 483 | line: { 484 | interval: 130, 485 | frames: ["-", "\\", "|", "/"], 486 | }, 487 | line2: { 488 | interval: 100, 489 | frames: ["⠂", "-", "–", "—", "–", "-"], 490 | }, 491 | pipe: { 492 | interval: 100, 493 | frames: ["┤", "┘", "┴", "└", "├", "┌", "┬", "┐"], 494 | }, 495 | simpleDots: { 496 | interval: 400, 497 | frames: [". ", ".. ", "...", " "], 498 | }, 499 | simpleDotsScrolling: { 500 | interval: 200, 501 | frames: [". ", ".. ", "...", " ..", " .", " "], 502 | }, 503 | star: { 504 | interval: 70, 505 | frames: ["✶", "✸", "✹", "✺", "✹", "✷"], 506 | }, 507 | star2: { 508 | interval: 80, 509 | frames: ["+", "x", "*"], 510 | }, 511 | flip: { 512 | interval: 70, 513 | frames: ["_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_"], 514 | }, 515 | hamburger: { 516 | interval: 100, 517 | frames: ["☱", "☲", "☴"], 518 | }, 519 | growVertical: { 520 | interval: 120, 521 | frames: ["▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃"], 522 | }, 523 | growHorizontal: { 524 | interval: 120, 525 | frames: ["▏", "▎", "▍", "▌", "▋", "▊", "▉", "▊", "▋", "▌", "▍", "▎"], 526 | }, 527 | balloon: { 528 | interval: 140, 529 | frames: [" ", ".", "o", "O", "@", "*", " "], 530 | }, 531 | balloon2: { 532 | interval: 120, 533 | frames: [".", "o", "O", "°", "O", "o", "."], 534 | }, 535 | noise: { 536 | interval: 100, 537 | frames: ["▓", "▒", "░"], 538 | }, 539 | bounce: { 540 | interval: 120, 541 | frames: ["⠁", "⠂", "⠄", "⠂"], 542 | }, 543 | boxBounce: { 544 | interval: 120, 545 | frames: ["▖", "▘", "▝", "▗"], 546 | }, 547 | boxBounce2: { 548 | interval: 100, 549 | frames: ["▌", "▀", "▐", "▄"], 550 | }, 551 | triangle: { 552 | interval: 50, 553 | frames: ["◢", "◣", "◤", "◥"], 554 | }, 555 | arc: { 556 | interval: 100, 557 | frames: ["◜", "◠", "◝", "◞", "◡", "◟"], 558 | }, 559 | circle: { 560 | interval: 120, 561 | frames: ["◡", "⊙", "◠"], 562 | }, 563 | squareCorners: { 564 | interval: 180, 565 | frames: ["◰", "◳", "◲", "◱"], 566 | }, 567 | circleQuarters: { 568 | interval: 120, 569 | frames: ["◴", "◷", "◶", "◵"], 570 | }, 571 | circleHalves: { 572 | interval: 50, 573 | frames: ["◐", "◓", "◑", "◒"], 574 | }, 575 | squish: { 576 | interval: 100, 577 | frames: ["╫", "╪"], 578 | }, 579 | toggle: { 580 | interval: 250, 581 | frames: ["⊶", "⊷"], 582 | }, 583 | toggle2: { 584 | interval: 80, 585 | frames: ["▫", "▪"], 586 | }, 587 | toggle3: { 588 | interval: 120, 589 | frames: ["□", "■"], 590 | }, 591 | toggle4: { 592 | interval: 100, 593 | frames: ["■", "□", "▪", "▫"], 594 | }, 595 | toggle5: { 596 | interval: 100, 597 | frames: ["▮", "▯"], 598 | }, 599 | toggle6: { 600 | interval: 300, 601 | frames: ["ဝ", "၀"], 602 | }, 603 | toggle7: { 604 | interval: 80, 605 | frames: ["⦾", "⦿"], 606 | }, 607 | toggle8: { 608 | interval: 100, 609 | frames: ["◍", "◌"], 610 | }, 611 | toggle9: { 612 | interval: 100, 613 | frames: ["◉", "◎"], 614 | }, 615 | toggle10: { 616 | interval: 100, 617 | frames: ["㊂", "㊀", "㊁"], 618 | }, 619 | toggle11: { 620 | interval: 50, 621 | frames: ["⧇", "⧆"], 622 | }, 623 | toggle12: { 624 | interval: 120, 625 | frames: ["☗", "☖"], 626 | }, 627 | toggle13: { 628 | interval: 80, 629 | frames: ["=", "*", "-"], 630 | }, 631 | arrow: { 632 | interval: 100, 633 | frames: ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"], 634 | }, 635 | arrow2: { 636 | interval: 80, 637 | frames: ["⬆️ ", "↗️ ", "➡️ ", "↘️ ", "⬇️ ", "↙️ ", "⬅️ ", "↖️ "], 638 | }, 639 | arrow3: { 640 | interval: 120, 641 | frames: ["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"], 642 | }, 643 | bouncingBar: { 644 | interval: 80, 645 | frames: [ 646 | "[ ]", 647 | "[= ]", 648 | "[== ]", 649 | "[=== ]", 650 | "[ ===]", 651 | "[ ==]", 652 | "[ =]", 653 | "[ ]", 654 | "[ =]", 655 | "[ ==]", 656 | "[ ===]", 657 | "[====]", 658 | "[=== ]", 659 | "[== ]", 660 | "[= ]", 661 | ], 662 | }, 663 | bouncingBall: { 664 | interval: 80, 665 | frames: [ 666 | "( ● )", 667 | "( ● )", 668 | "( ● )", 669 | "( ● )", 670 | "( ●)", 671 | "( ● )", 672 | "( ● )", 673 | "( ● )", 674 | "( ● )", 675 | "(● )", 676 | ], 677 | }, 678 | smiley: { 679 | interval: 200, 680 | frames: ["😄 ", "😝 "], 681 | }, 682 | monkey: { 683 | interval: 300, 684 | frames: ["🙈 ", "🙈 ", "🙉 ", "🙊 "], 685 | }, 686 | hearts: { 687 | interval: 100, 688 | frames: ["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "], 689 | }, 690 | clock: { 691 | interval: 100, 692 | frames: [ 693 | "🕛 ", 694 | "🕐 ", 695 | "🕑 ", 696 | "🕒 ", 697 | "🕓 ", 698 | "🕔 ", 699 | "🕕 ", 700 | "🕖 ", 701 | "🕗 ", 702 | "🕘 ", 703 | "🕙 ", 704 | "🕚 ", 705 | ], 706 | }, 707 | earth: { 708 | interval: 180, 709 | frames: ["🌍 ", "🌎 ", "🌏 "], 710 | }, 711 | material: { 712 | interval: 17, 713 | frames: [ 714 | "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 715 | "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 716 | "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 717 | "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 718 | "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 719 | "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 720 | "███████▁▁▁▁▁▁▁▁▁▁▁▁▁", 721 | "████████▁▁▁▁▁▁▁▁▁▁▁▁", 722 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 723 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 724 | "██████████▁▁▁▁▁▁▁▁▁▁", 725 | "███████████▁▁▁▁▁▁▁▁▁", 726 | "█████████████▁▁▁▁▁▁▁", 727 | "██████████████▁▁▁▁▁▁", 728 | "██████████████▁▁▁▁▁▁", 729 | "▁██████████████▁▁▁▁▁", 730 | "▁██████████████▁▁▁▁▁", 731 | "▁██████████████▁▁▁▁▁", 732 | "▁▁██████████████▁▁▁▁", 733 | "▁▁▁██████████████▁▁▁", 734 | "▁▁▁▁█████████████▁▁▁", 735 | "▁▁▁▁██████████████▁▁", 736 | "▁▁▁▁██████████████▁▁", 737 | "▁▁▁▁▁██████████████▁", 738 | "▁▁▁▁▁██████████████▁", 739 | "▁▁▁▁▁██████████████▁", 740 | "▁▁▁▁▁▁██████████████", 741 | "▁▁▁▁▁▁██████████████", 742 | "▁▁▁▁▁▁▁█████████████", 743 | "▁▁▁▁▁▁▁█████████████", 744 | "▁▁▁▁▁▁▁▁████████████", 745 | "▁▁▁▁▁▁▁▁████████████", 746 | "▁▁▁▁▁▁▁▁▁███████████", 747 | "▁▁▁▁▁▁▁▁▁███████████", 748 | "▁▁▁▁▁▁▁▁▁▁██████████", 749 | "▁▁▁▁▁▁▁▁▁▁██████████", 750 | "▁▁▁▁▁▁▁▁▁▁▁▁████████", 751 | "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", 752 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████", 753 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", 754 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", 755 | "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", 756 | "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", 757 | "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", 758 | "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", 759 | "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", 760 | "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", 761 | "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", 762 | "██████▁▁▁▁▁▁▁▁▁▁▁▁▁█", 763 | "████████▁▁▁▁▁▁▁▁▁▁▁▁", 764 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 765 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 766 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 767 | "█████████▁▁▁▁▁▁▁▁▁▁▁", 768 | "███████████▁▁▁▁▁▁▁▁▁", 769 | "████████████▁▁▁▁▁▁▁▁", 770 | "████████████▁▁▁▁▁▁▁▁", 771 | "██████████████▁▁▁▁▁▁", 772 | "██████████████▁▁▁▁▁▁", 773 | "▁██████████████▁▁▁▁▁", 774 | "▁██████████████▁▁▁▁▁", 775 | "▁▁▁█████████████▁▁▁▁", 776 | "▁▁▁▁▁████████████▁▁▁", 777 | "▁▁▁▁▁████████████▁▁▁", 778 | "▁▁▁▁▁▁███████████▁▁▁", 779 | "▁▁▁▁▁▁▁▁█████████▁▁▁", 780 | "▁▁▁▁▁▁▁▁█████████▁▁▁", 781 | "▁▁▁▁▁▁▁▁▁█████████▁▁", 782 | "▁▁▁▁▁▁▁▁▁█████████▁▁", 783 | "▁▁▁▁▁▁▁▁▁▁█████████▁", 784 | "▁▁▁▁▁▁▁▁▁▁▁████████▁", 785 | "▁▁▁▁▁▁▁▁▁▁▁████████▁", 786 | "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", 787 | "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", 788 | "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", 789 | "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", 790 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", 791 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", 792 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", 793 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", 794 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", 795 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", 796 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", 797 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", 798 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", 799 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", 800 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", 801 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", 802 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 803 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 804 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 805 | "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", 806 | ], 807 | }, 808 | moon: { 809 | interval: 80, 810 | frames: ["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "], 811 | }, 812 | runner: { 813 | interval: 140, 814 | frames: ["🚶 ", "🏃 "], 815 | }, 816 | pong: { 817 | interval: 80, 818 | frames: [ 819 | "▐⠂ ▌", 820 | "▐⠈ ▌", 821 | "▐ ⠂ ▌", 822 | "▐ ⠠ ▌", 823 | "▐ ⡀ ▌", 824 | "▐ ⠠ ▌", 825 | "▐ ⠂ ▌", 826 | "▐ ⠈ ▌", 827 | "▐ ⠂ ▌", 828 | "▐ ⠠ ▌", 829 | "▐ ⡀ ▌", 830 | "▐ ⠠ ▌", 831 | "▐ ⠂ ▌", 832 | "▐ ⠈ ▌", 833 | "▐ ⠂▌", 834 | "▐ ⠠▌", 835 | "▐ ⡀▌", 836 | "▐ ⠠ ▌", 837 | "▐ ⠂ ▌", 838 | "▐ ⠈ ▌", 839 | "▐ ⠂ ▌", 840 | "▐ ⠠ ▌", 841 | "▐ ⡀ ▌", 842 | "▐ ⠠ ▌", 843 | "▐ ⠂ ▌", 844 | "▐ ⠈ ▌", 845 | "▐ ⠂ ▌", 846 | "▐ ⠠ ▌", 847 | "▐ ⡀ ▌", 848 | "▐⠠ ▌", 849 | ], 850 | }, 851 | shark: { 852 | interval: 120, 853 | frames: [ 854 | "▐|\\____________▌", 855 | "▐_|\\___________▌", 856 | "▐__|\\__________▌", 857 | "▐___|\\_________▌", 858 | "▐____|\\________▌", 859 | "▐_____|\\_______▌", 860 | "▐______|\\______▌", 861 | "▐_______|\\_____▌", 862 | "▐________|\\____▌", 863 | "▐_________|\\___▌", 864 | "▐__________|\\__▌", 865 | "▐___________|\\_▌", 866 | "▐____________|\\▌", 867 | "▐____________/|▌", 868 | "▐___________/|_▌", 869 | "▐__________/|__▌", 870 | "▐_________/|___▌", 871 | "▐________/|____▌", 872 | "▐_______/|_____▌", 873 | "▐______/|______▌", 874 | "▐_____/|_______▌", 875 | "▐____/|________▌", 876 | "▐___/|_________▌", 877 | "▐__/|__________▌", 878 | "▐_/|___________▌", 879 | "▐/|____________▌", 880 | ], 881 | }, 882 | dqpb: { 883 | interval: 100, 884 | frames: ["d", "q", "p", "b"], 885 | }, 886 | weather: { 887 | interval: 100, 888 | frames: [ 889 | "☀️ ", 890 | "☀️ ", 891 | "☀️ ", 892 | "🌤 ", 893 | "⛅️ ", 894 | "🌥 ", 895 | "☁️ ", 896 | "🌧 ", 897 | "🌨 ", 898 | "🌧 ", 899 | "🌨 ", 900 | "🌧 ", 901 | "🌨 ", 902 | "⛈ ", 903 | "🌨 ", 904 | "🌧 ", 905 | "🌨 ", 906 | "☁️ ", 907 | "🌥 ", 908 | "⛅️ ", 909 | "🌤 ", 910 | "☀️ ", 911 | "☀️ ", 912 | ], 913 | }, 914 | christmas: { 915 | interval: 400, 916 | frames: ["🌲", "🎄"], 917 | }, 918 | grenade: { 919 | interval: 80, 920 | frames: [ 921 | "، ", 922 | "′ ", 923 | " ´ ", 924 | " ‾ ", 925 | " ⸌", 926 | " ⸊", 927 | " |", 928 | " ⁎", 929 | " ⁕", 930 | " ෴ ", 931 | " ⁓", 932 | " ", 933 | " ", 934 | " ", 935 | ], 936 | }, 937 | point: { 938 | interval: 125, 939 | frames: ["∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"], 940 | }, 941 | layer: { 942 | interval: 150, 943 | frames: ["-", "=", "≡"], 944 | }, 945 | betaWave: { 946 | interval: 80, 947 | frames: [ 948 | "ρββββββ", 949 | "βρβββββ", 950 | "ββρββββ", 951 | "βββρβββ", 952 | "ββββρββ", 953 | "βββββρβ", 954 | "ββββββρ", 955 | ], 956 | }, 957 | }; 958 | --------------------------------------------------------------------------------