├── .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 | [](https://github.com/denosaurs/wait/releases)
4 | [](https://github.com/denosaurs/wait/actions)
5 | [](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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------