unicode-animations 0.1.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  Unicode spinner animations as raw frame data — no dependencies, works everywhere.
4
4
 
5
- ## Preview
5
+ ## Demo
6
6
 
7
- ```
8
- braille ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
9
- arc ◜ ◠ ◝ ◞ ◡ ◟
10
- halfmoon ◐
11
- blocks ▁ ▇ █ ▇ ▆ ▅ ▄ ▃ ▂
12
- line | / \
7
+ See all 18 spinners animating live:
8
+
9
+ ```bash
10
+ npx unicode-animations --web # open browser demo
11
+ npx unicode-animations # cycle through all in terminal
12
+ npx unicode-animations helix # preview a specific spinner
13
+ npx unicode-animations --list # list all spinners
13
14
  ```
14
15
 
15
16
  ## Install
@@ -32,7 +33,7 @@ Each spinner is a `{ frames: string[], interval: number }` object.
32
33
 
33
34
  ## Examples
34
35
 
35
- ### Spinner while awaiting an async task
36
+ ### CLI tool spinner during async work
36
37
 
37
38
  ```js
38
39
  import spinners from 'unicode-animations';
@@ -41,114 +42,103 @@ const { frames, interval } = spinners.braille;
41
42
  let i = 0;
42
43
 
43
44
  const spinner = setInterval(() => {
44
- process.stdout.write(`\r ${frames[i++ % frames.length]} Installing dependencies...`);
45
+ process.stdout.write(`\r\x1B[2K ${frames[i++ % frames.length]} Deploying to production...`);
45
46
  }, interval);
46
47
 
47
- await install();
48
+ await deploy();
48
49
 
49
50
  clearInterval(spinner);
50
- process.stdout.write('\r ✔ Installed successfully.\n');
51
+ process.stdout.write('\r\x1B[2KDeployed.\n');
51
52
  ```
52
53
 
53
- ### Multi-character braille spinner
54
-
55
- The grid-based spinners produce wider animated patterns — useful for visual flair in CLI tools:
54
+ ### Reusable spinner helper
56
55
 
57
56
  ```js
58
57
  import spinners from 'unicode-animations';
59
58
 
60
- const { frames, interval } = spinners.helix;
61
- let i = 0;
59
+ function createSpinner(msg, name = 'braille') {
60
+ const { frames, interval } = spinners[name];
61
+ let i = 0, text = msg;
62
+ const timer = setInterval(() => {
63
+ process.stdout.write(`\r\x1B[2K ${frames[i++ % frames.length]} ${text}`);
64
+ }, interval);
62
65
 
63
- const spinner = setInterval(() => {
64
- process.stdout.write(`\r ${frames[i++ % frames.length]} Building...`);
65
- }, interval);
66
+ return {
67
+ update(msg) { text = msg; },
68
+ stop(msg) { clearInterval(timer); process.stdout.write(`\r\x1B[2K ✔ ${msg}\n`); },
69
+ };
70
+ }
71
+
72
+ const s = createSpinner('Connecting to database...');
73
+ const db = await connect();
74
+ s.update(`Running ${migrations.length} migrations...`);
75
+ await db.migrate(migrations);
76
+ s.stop('Database ready.');
66
77
  ```
67
78
 
68
- ### Progress indicator with dynamic message
79
+ ### Multi-step pipeline
69
80
 
70
81
  ```js
71
82
  import spinners from 'unicode-animations';
72
83
 
73
- function withSpinner(message, spinner = spinners.braille) {
84
+ async function runWithSpinner(label, fn, name = 'braille') {
85
+ const { frames, interval } = spinners[name];
74
86
  let i = 0;
75
- const { frames, interval } = spinner;
76
87
  const timer = setInterval(() => {
77
- process.stdout.write(`\r\x1B[2K ${frames[i++ % frames.length]} ${message}`);
88
+ process.stdout.write(`\r\x1B[2K ${frames[i++ % frames.length]} ${label}`);
78
89
  }, interval);
79
-
80
- return {
81
- update(msg) { message = msg; },
82
- stop(finalMsg) {
83
- clearInterval(timer);
84
- process.stdout.write(`\r\x1B[2K ✔ ${finalMsg}\n`);
85
- },
86
- };
90
+ const result = await fn();
91
+ clearInterval(timer);
92
+ process.stdout.write(`\r\x1B[2K ✔ ${label}\n`);
93
+ return result;
87
94
  }
88
95
 
89
- const spin = withSpinner('Fetching data...');
90
- const data = await fetchData();
91
- spin.update(`Processing ${data.length} records...`);
92
- await processData(data);
93
- spin.stop('Done.');
96
+ await runWithSpinner('Linting...', lint, 'scan');
97
+ await runWithSpinner('Running tests...', test, 'helix');
98
+ await runWithSpinner('Building...', build, 'cascade');
99
+ await runWithSpinner('Publishing...', publish, 'braille');
94
100
  ```
95
101
 
96
- ### Cycle through different spinners
102
+ ### React component
97
103
 
98
- ```js
104
+ ```jsx
105
+ import { useState, useEffect } from 'react';
99
106
  import spinners from 'unicode-animations';
100
107
 
101
- const names = ['scan', 'rain', 'helix', 'cascade'];
102
- let current = 0;
103
- let i = 0;
108
+ function Spinner({ name = 'braille', children }) {
109
+ const [frame, setFrame] = useState(0);
110
+ const s = spinners[name];
104
111
 
105
- setInterval(() => {
106
- const s = spinners[names[current]];
107
- process.stdout.write(`\r\x1B[2K ${s.frames[i % s.frames.length]} ${names[current]}`);
108
- i++;
109
- if (i % 20 === 0) current = (current + 1) % names.length;
110
- }, 80);
112
+ useEffect(() => {
113
+ const timer = setInterval(
114
+ () => setFrame(f => (f + 1) % s.frames.length),
115
+ s.interval
116
+ );
117
+ return () => clearInterval(timer);
118
+ }, [name]);
119
+
120
+ return <span style={{ fontFamily: 'monospace' }}>{s.frames[frame]} {children}</span>;
121
+ }
122
+
123
+ // Usage: <Spinner name="helix">Generating response...</Spinner>
111
124
  ```
112
125
 
113
- ### Browser — inline loading text
126
+ ### Browser — status indicator
114
127
 
115
128
  ```js
116
129
  import spinners from 'unicode-animations';
117
130
 
118
131
  const el = document.getElementById('status');
119
- const { frames, interval } = spinners.arc;
132
+ const { frames, interval } = spinners.orbit;
120
133
  let i = 0;
121
134
 
122
135
  const spinner = setInterval(() => {
123
- el.textContent = `${frames[i++ % frames.length]} Loading...`;
136
+ el.textContent = `${frames[i++ % frames.length]} Syncing...`;
124
137
  }, interval);
125
138
 
126
- // Stop when done
127
- function onLoaded() {
128
- clearInterval(spinner);
129
- el.textContent = '✔ Ready';
130
- }
131
- ```
132
-
133
- ### React component
134
-
135
- ```jsx
136
- import { useState, useEffect } from 'react';
137
- import spinners from 'unicode-animations';
138
-
139
- function Spinner({ name = 'braille', text = 'Loading...' }) {
140
- const [frame, setFrame] = useState(0);
141
- const spinner = spinners[name];
142
-
143
- useEffect(() => {
144
- const timer = setInterval(() => {
145
- setFrame(f => (f + 1) % spinner.frames.length);
146
- }, spinner.interval);
147
- return () => clearInterval(timer);
148
- }, [name]);
149
-
150
- return <span>{spinner.frames[frame]} {text}</span>;
151
- }
139
+ await sync();
140
+ clearInterval(spinner);
141
+ el.textContent = '✔ Synced';
152
142
  ```
153
143
 
154
144
  ## All spinners
@@ -158,8 +148,8 @@ function Spinner({ name = 'braille', text = 'Loading...' }) {
158
148
  | Name | Preview | Interval |
159
149
  |------|---------|----------|
160
150
  | `braille` | `⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏` | 80ms |
161
- | `braillewave` | `⠁⠂⠄⡀⢀⠠⠐⠈``⠂⠄⡀⢀⠠⠐⠈⠁` | 100ms |
162
- | `dna` | `⠋⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄``⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠠` | 80ms |
151
+ | `braillewave` | `⠁⠂⠄⡀``⠂⠄⡀⢀` | 100ms |
152
+ | `dna` | `⠋⠉⠙⠚``⠉⠙⠚⠒` | 80ms |
163
153
 
164
154
  ### Grid animations (braille)
165
155
 
@@ -181,15 +171,6 @@ function Spinner({ name = 'braille', text = 'Loading...' }) {
181
171
  | `fillsweep` | 11 | 100ms |
182
172
  | `diagswipe` | 16 | 60ms |
183
173
 
184
- ### Non-braille classics
185
-
186
- | Name | Preview | Interval |
187
- |------|---------|----------|
188
- | `arc` | `◜ ◠ ◝ ◞ ◡ ◟` | 100ms |
189
- | `halfmoon` | `◐ ◓ ◑ ◒` | 180ms |
190
- | `line` | `\| / — \` | 100ms |
191
- | `blocks` | `▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▇ ▆ ▅ ▄ ▃ ▂` | 100ms |
192
-
193
174
  ## Custom spinners
194
175
 
195
176
  Create your own braille spinners using the grid utilities:
@@ -228,7 +209,7 @@ interface Spinner {
228
209
  | `gridToBraille(grid)` | `(boolean[][]) => string` |
229
210
  | `makeGrid(rows, cols)` | `(number, number) => boolean[][]` |
230
211
  | `Spinner` | TypeScript interface |
231
- | `BrailleSpinnerName` | Union type of all 22 spinner names |
212
+ | `BrailleSpinnerName` | Union type of all 18 spinner names |
232
213
 
233
214
  ### Exports from `'unicode-animations/braille'`
234
215
 
package/dist/braille.cjs CHANGED
@@ -56,6 +56,7 @@ function gridToBraille(grid) {
56
56
  return result;
57
57
  }
58
58
  function makeGrid(rows, cols) {
59
+ if (rows <= 0 || cols <= 0) return [];
59
60
  return Array.from({ length: rows }, () => Array(cols).fill(false));
60
61
  }
61
62
  function genScan() {
@@ -334,31 +335,31 @@ var spinners = {
334
335
  },
335
336
  braillewave: {
336
337
  frames: [
337
- "\u2801\u2802\u2804\u2840\u2880\u2820\u2810\u2808",
338
- "\u2808\u2801\u2802\u2804\u2840\u2880\u2820\u2810",
339
- "\u2810\u2808\u2801\u2802\u2804\u2840\u2880\u2820",
340
- "\u2820\u2810\u2808\u2801\u2802\u2804\u2840\u2880",
341
- "\u2880\u2820\u2810\u2808\u2801\u2802\u2804\u2840",
342
- "\u2840\u2880\u2820\u2810\u2808\u2801\u2802\u2804",
343
- "\u2804\u2840\u2880\u2820\u2810\u2808\u2801\u2802",
344
- "\u2802\u2804\u2840\u2880\u2820\u2810\u2808\u2801"
338
+ "\u2801\u2802\u2804\u2840",
339
+ "\u2802\u2804\u2840\u2880",
340
+ "\u2804\u2840\u2880\u2820",
341
+ "\u2840\u2880\u2820\u2810",
342
+ "\u2880\u2820\u2810\u2808",
343
+ "\u2820\u2810\u2808\u2801",
344
+ "\u2810\u2808\u2801\u2802",
345
+ "\u2808\u2801\u2802\u2804"
345
346
  ],
346
347
  interval: 100
347
348
  },
348
349
  dna: {
349
350
  frames: [
350
- "\u280B\u2809\u2819\u281A\u2812\u2802\u2802\u2812\u2832\u2834\u2824\u2804",
351
- "\u2819\u281A\u2812\u2802\u2802\u2812\u2832\u2834\u2824\u2804\u2804\u2820",
352
- "\u2839\u2812\u2802\u2802\u2812\u2832\u2834\u2824\u2804\u2804\u2820\u2820",
353
- "\u2838\u2802\u2802\u2812\u2832\u2834\u2824\u2804\u2804\u2820\u2820\u2804",
354
- "\u283C\u2802\u2812\u2832\u2834\u2824\u2804\u2804\u2820\u2820\u2804\u2824",
355
- "\u2834\u2812\u2832\u2834\u2824\u2804\u2804\u2820\u2820\u2804\u2824\u2834",
356
- "\u2826\u2832\u2834\u2824\u2804\u2804\u2820\u2820\u2804\u2824\u2834\u2832",
357
- "\u2827\u2834\u2824\u2804\u2804\u2820\u2820\u2804\u2824\u2834\u2832\u2812",
358
- "\u2807\u2824\u2804\u2804\u2820\u2820\u2804\u2824\u2834\u2832\u2812\u2802",
359
- "\u280F\u2804\u2804\u2820\u2820\u2804\u2824\u2834\u2832\u2812\u2802\u2802",
360
- "\u280B\u2804\u2820\u2820\u2804\u2824\u2834\u2832\u2812\u2802\u2802\u2812",
361
- "\u2809\u2820\u2820\u2804\u2824\u2834\u2832\u2812\u2802\u2802\u2812\u2832"
351
+ "\u280B\u2809\u2819\u281A",
352
+ "\u2809\u2819\u281A\u2812",
353
+ "\u2819\u281A\u2812\u2802",
354
+ "\u281A\u2812\u2802\u2802",
355
+ "\u2812\u2802\u2802\u2812",
356
+ "\u2802\u2802\u2812\u2832",
357
+ "\u2802\u2812\u2832\u2834",
358
+ "\u2812\u2832\u2834\u2824",
359
+ "\u2832\u2834\u2824\u2804",
360
+ "\u2834\u2824\u2804\u280B",
361
+ "\u2824\u2804\u280B\u2809",
362
+ "\u2804\u280B\u2809\u2819"
362
363
  ],
363
364
  interval: 80
364
365
  },
@@ -377,24 +378,7 @@ var spinners = {
377
378
  checkerboard: { frames: genCheckerboard(), interval: 250 },
378
379
  helix: { frames: genHelix(), interval: 80 },
379
380
  fillsweep: { frames: genFillSweep(), interval: 100 },
380
- diagswipe: { frames: genDiagonalSwipe(), interval: 60 },
381
- // === Non-braille classics ===
382
- arc: {
383
- frames: ["\u25DC", "\u25E0", "\u25DD", "\u25DE", "\u25E1", "\u25DF"],
384
- interval: 100
385
- },
386
- halfmoon: {
387
- frames: ["\u25D0", "\u25D3", "\u25D1", "\u25D2"],
388
- interval: 180
389
- },
390
- line: {
391
- frames: ["|", "/", "\u2014", "\\"],
392
- interval: 100
393
- },
394
- blocks: {
395
- frames: ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588", "\u2587", "\u2586", "\u2585", "\u2584", "\u2583", "\u2582"],
396
- interval: 100
397
- }
381
+ diagswipe: { frames: genDiagonalSwipe(), interval: 60 }
398
382
  };
399
383
  var braille_default = spinners;
400
384
  // Annotate the CommonJS export names for ESM import in node:
@@ -9,7 +9,7 @@ interface Spinner {
9
9
  readonly frames: readonly string[];
10
10
  readonly interval: number;
11
11
  }
12
- type BrailleSpinnerName = 'braille' | 'braillewave' | 'dna' | 'scan' | 'rain' | 'scanline' | 'pulse' | 'snake' | 'sparkle' | 'cascade' | 'columns' | 'orbit' | 'breathe' | 'waverows' | 'checkerboard' | 'helix' | 'fillsweep' | 'diagswipe' | 'arc' | 'halfmoon' | 'line' | 'blocks';
12
+ type BrailleSpinnerName = 'braille' | 'braillewave' | 'dna' | 'scan' | 'rain' | 'scanline' | 'pulse' | 'snake' | 'sparkle' | 'cascade' | 'columns' | 'orbit' | 'breathe' | 'waverows' | 'checkerboard' | 'helix' | 'fillsweep' | 'diagswipe';
13
13
  /**
14
14
  * Convert a 2D boolean grid into a braille string.
15
15
  * grid[row][col] = true means dot is raised.
package/dist/braille.d.ts CHANGED
@@ -9,7 +9,7 @@ interface Spinner {
9
9
  readonly frames: readonly string[];
10
10
  readonly interval: number;
11
11
  }
12
- type BrailleSpinnerName = 'braille' | 'braillewave' | 'dna' | 'scan' | 'rain' | 'scanline' | 'pulse' | 'snake' | 'sparkle' | 'cascade' | 'columns' | 'orbit' | 'breathe' | 'waverows' | 'checkerboard' | 'helix' | 'fillsweep' | 'diagswipe' | 'arc' | 'halfmoon' | 'line' | 'blocks';
12
+ type BrailleSpinnerName = 'braille' | 'braillewave' | 'dna' | 'scan' | 'rain' | 'scanline' | 'pulse' | 'snake' | 'sparkle' | 'cascade' | 'columns' | 'orbit' | 'breathe' | 'waverows' | 'checkerboard' | 'helix' | 'fillsweep' | 'diagswipe';
13
13
  /**
14
14
  * Convert a 2D boolean grid into a braille string.
15
15
  * grid[row][col] = true means dot is raised.