create-textmode 1.0.6 → 1.0.7

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.
Files changed (60) hide show
  1. package/LICENSE +661 -21
  2. package/README.md +120 -86
  3. package/bin/index.js +8 -8
  4. package/package.json +70 -54
  5. package/src/args.js +57 -30
  6. package/src/banner.js +57 -57
  7. package/src/cli.js +166 -154
  8. package/src/constants.js +59 -23
  9. package/src/fs-utils.js +136 -85
  10. package/src/packageManager.js +18 -18
  11. package/src/prompts.js +102 -90
  12. package/src/runCommand.js +46 -46
  13. package/src/summary.js +43 -33
  14. package/src/textmodeVersion.js +57 -45
  15. package/src/usage.js +21 -18
  16. package/src/versions.js +56 -52
  17. package/templates/vanilla-js/.prettierrc +8 -8
  18. package/templates/vanilla-js/_gitignore +2 -2
  19. package/templates/vanilla-js/eslint.config.js +22 -22
  20. package/templates/vanilla-js/index.html +25 -25
  21. package/templates/vanilla-js/package.json +26 -26
  22. package/templates/vanilla-js/src/sketch.js +28 -28
  23. package/templates/vanilla-js/vite.config.js +7 -7
  24. package/templates/vanilla-js-tweakpane/_gitignore +2 -2
  25. package/templates/vanilla-js-tweakpane/eslint.config.js +22 -22
  26. package/templates/vanilla-js-tweakpane/index.html +37 -37
  27. package/templates/vanilla-js-tweakpane/package.json +27 -27
  28. package/templates/vanilla-js-tweakpane/src/sketch.js +62 -62
  29. package/templates/vanilla-js-tweakpane/vite.config.js +7 -7
  30. package/templates/vanilla-ts/.prettierrc +8 -8
  31. package/templates/vanilla-ts/_gitignore +1 -1
  32. package/templates/vanilla-ts/eslint.config.js +23 -23
  33. package/templates/vanilla-ts/index.html +26 -26
  34. package/templates/vanilla-ts/package.json +30 -30
  35. package/templates/vanilla-ts/src/sketch.ts +28 -28
  36. package/templates/vanilla-ts/tsconfig.json +15 -15
  37. package/templates/vanilla-ts/vite.config.ts +7 -7
  38. package/templates/vanilla-ts-tweakpane/_gitignore +2 -2
  39. package/templates/vanilla-ts-tweakpane/eslint.config.js +23 -23
  40. package/templates/vanilla-ts-tweakpane/index.html +37 -37
  41. package/templates/vanilla-ts-tweakpane/package.json +32 -32
  42. package/templates/vanilla-ts-tweakpane/src/sketch.ts +72 -72
  43. package/templates/vanilla-ts-tweakpane/tsconfig.json +15 -15
  44. package/templates/vanilla-ts-tweakpane/vite.config.ts +7 -7
  45. package/templates/vanilla-js-fxhash/_gitignore +0 -2
  46. package/templates/vanilla-js-fxhash/eslint.config.js +0 -22
  47. package/templates/vanilla-js-fxhash/index.html +0 -27
  48. package/templates/vanilla-js-fxhash/package.json +0 -26
  49. package/templates/vanilla-js-fxhash/public/fxhash.min.js +0 -1
  50. package/templates/vanilla-js-fxhash/src/sketch.js +0 -101
  51. package/templates/vanilla-js-fxhash/vite.config.js +0 -7
  52. package/templates/vanilla-ts-fxhash/_gitignore +0 -2
  53. package/templates/vanilla-ts-fxhash/eslint.config.js +0 -23
  54. package/templates/vanilla-ts-fxhash/index.html +0 -27
  55. package/templates/vanilla-ts-fxhash/package.json +0 -30
  56. package/templates/vanilla-ts-fxhash/public/fxhash.min.js +0 -1
  57. package/templates/vanilla-ts-fxhash/src/fxhash.d.ts +0 -54
  58. package/templates/vanilla-ts-fxhash/src/sketch.ts +0 -108
  59. package/templates/vanilla-ts-fxhash/tsconfig.json +0 -15
  60. package/templates/vanilla-ts-fxhash/vite.config.ts +0 -7
package/README.md CHANGED
@@ -1,86 +1,120 @@
1
- # create-textmode (✿◠‿◠)
2
-
3
- <div align="center">
4
-
5
- A zero-friction scaffolder for `textmode.js`.
6
-
7
- | [![JavaScript](https://img.shields.io/badge/JavaScript-323330?logo=javascript&logoColor=F7DF1E)](https://developer.mozilla.org/docs/Web/JavaScript) [![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/) | [![textmode.js](https://img.shields.io/badge/github-textmode.js-111827?logo=github&logoColor=white)](https://github.com/humanbydefinition/textmode.js) [![docs](https://img.shields.io/badge/docs-vitepress-646cff?logo=vitepress&logoColor=white)](https://code.textmode.art/) [![Discord](https://img.shields.io/discord/1357070706181017691?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/sjrw8QXNks) | [![ko-fi](https://shields.io/badge/ko--fi-donate-ff5f5f?logo=ko-fi)](https://ko-fi.com/V7V8JG2FY) [![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/humanbydefinition) |
8
- |:-------------|:-------------|:-------------|
9
-
10
- </div>
11
-
12
- `create-textmode.js` is a command-line tool to quickly scaffold new projects using `textmode.js` with sensible defaults and minimal friction.
13
-
14
- Just run the CLI via `npm create textmode@latest` or your package manager's equivalent command, answer a few prompts, and you're ready to start building!
15
-
16
- > [!NOTE]
17
- > The CLI is still fresh out of the oven, so please report any issues or feature requests on the GitHub repo!
18
-
19
- ## Requirements
20
-
21
- - Node.js 18+ *(per `engines`)*
22
- - One of: npm, pnpm, yarn, or bun on your PATH
23
-
24
- ## Quick start *(interactive)*
25
-
26
- ```bash
27
- npm create textmode@latest
28
- # or
29
- pnpm create textmode@latest
30
- # or
31
- yarn create textmode
32
- # ...
33
- ```
34
-
35
- The CLI will prompt for project name, template, textmode.js version, installing dependencies, and starting the dev server.
36
-
37
- ## Non-interactive examples
38
-
39
- ```bash
40
- # TypeScript template, pin textmode.js, auto-install, do not run dev server
41
- npm create textmode@latest my-textmode-app -- --template vanilla-ts --textmode-version 0.7.1 --install --no-run
42
-
43
- # JavaScript template with pnpm, skip install and run
44
- pnpm create textmode@latest demo -- --template vanilla-js --pm pnpm --no-install --no-run
45
- ```
46
-
47
- ## Options
48
-
49
- - `--template <name>`: choose a template *(prompts if omitted)*
50
- - `--name <projectName>` or first positional arg: directory/package name *(default suggestion if omitted)*
51
- - `--textmode-version <ver>`: pin textmode.js *(prompts from fetched stable versions; defaults to `latest`)*
52
- - `--pm <npm|pnpm|yarn|bun>`: force a package manager *(auto-detected otherwise)*
53
- - `--install` / `--no-install`: install dependencies after scaffold *(prompts if neither is provided)*
54
- - `--run` / `--no-run`: start the dev server after install *(prompts if neither is provided)*
55
- - `--force`: allow using a non-empty directory without prompting
56
- - `--help`: show usage
57
- - `--version`: show the CLI version
58
-
59
- ## Templates
60
-
61
- All templates ship with Vite, `textmode.js`, Prettier, ESLint, and a `.gitignore`. TypeScript variants add `tsconfig` and a `typecheck` script. Below are the extras that differ by template:
62
-
63
- | Template | Flavor | Extras |
64
- |:---------|:-------|:-------|
65
- | `vanilla-js` | JavaScript | Base starter sketch |
66
- | `vanilla-ts` | TypeScript | Base starter sketch + TS config |
67
- | `vanilla-js-fxhash` | JavaScript + fx(hash) | Bundled `fxhash.min.js`, starter sketch using `$fx` API and features |
68
- | `vanilla-ts-fxhash` | TypeScript + fx(hash) | Bundled `fxhash.min.js`, `$fx` typings, starter sketch using `$fx` API and features |
69
- | `vanilla-js-tweakpane` | JavaScript + Tweakpane | UI controls wired with Tweakpane |
70
- | `vanilla-ts-tweakpane` | TypeScript + Tweakpane | UI controls wired with Tweakpane + TS config |
71
-
72
- Adding new templates? Keep the shared tooling note above and only list what’s unique in the table.
73
-
74
- ## Local development
75
-
76
- ```bash
77
- npm install
78
- npm test # vitest
79
- npm run test:verbose
80
-
81
- # Run CLI directly (no npm-create shim); no "--" needed
82
- node bin/index.js demo --template vanilla-js --pm pnpm --no-install --no-run
83
- # or
84
- node bin/index.js
85
- # ...
86
- ```
1
+ # create-textmode (✿◠‿◠)
2
+
3
+ <div align="center">
4
+
5
+ <img alt="create-textmode scaffold textmode.js with zero friction" src=".github/assets/readme-og.png" />
6
+
7
+ | [![JavaScript](https://img.shields.io/badge/JavaScript-323330?logo=javascript&logoColor=F7DF1E)](https://developer.mozilla.org/docs/Web/JavaScript) [![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/) | [![textmode.js](https://img.shields.io/badge/github-textmode.js-111827?logo=github&logoColor=white)](https://github.com/humanbydefinition/textmode.js) [![docs](https://img.shields.io/badge/docs-vitepress-646cff?logo=vitepress&logoColor=white)](https://code.textmode.art/) [![Discord](https://img.shields.io/discord/1357070706181017691?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/sjrw8QXNks) | [![ko-fi](https://shields.io/badge/ko--fi-donate-ff5f5f?logo=ko-fi)](https://ko-fi.com/V7V8JG2FY) [![GitHub-sponsors](https://img.shields.io/badge/sponsor-30363D?logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/humanbydefinition) |
8
+ |:---|:---|:---|
9
+
10
+ </div>
11
+
12
+ `create-textmode` is a command-line tool to quickly scaffold new projects using `textmode.js` with sensible defaults and minimal friction.
13
+
14
+ Just run the CLI via `npm create textmode@latest` or your package manager's equivalent command, answer a few prompts, and you're ready to start building!
15
+
16
+ ## Features
17
+
18
+ - **One-command scaffolding** - Start a new project with `npm create textmode@latest` or your package manager's equivalent
19
+ - **Interactive prompts** - Choose project name, template, textmode.js version, official add-ons, dependency installation, and dev server startup
20
+ - **JavaScript and TypeScript templates** - Vanilla starters plus Tweakpane-controlled UI variants
21
+ - **Package-manager aware** - Auto-detect npm, pnpm, yarn, or bun
22
+ - **Non-interactive flags** - Full CLI options for scripts and CI
23
+ - **Sensible defaults** - Vite, `textmode.js`, Prettier, ESLint, and a `.gitignore`; TypeScript variants add `tsconfig` and a `typecheck` script
24
+
25
+ ## Try it online first
26
+
27
+ Open [editor.textmode.art](https://editor.textmode.art/), a browser-based live-coding environment for the complete official `textmode.js` ecosystem, to try textmode.js before or alongside scaffolding; sketches run as you edit, with no local toolchain required.
28
+
29
+ ## Requirements
30
+
31
+ - Node.js 18+ *(per `engines`)*
32
+ - One of: npm, pnpm, yarn, or bun on your PATH
33
+
34
+ ## Quick start
35
+
36
+ ```bash
37
+ npm create textmode@latest
38
+ # or
39
+ pnpm create textmode@latest
40
+ # or
41
+ yarn create textmode
42
+ # ...
43
+ ```
44
+
45
+ The CLI will prompt for project name, template, textmode.js version, official add-ons, installing dependencies, and starting the dev server.
46
+
47
+ ## Non-interactive examples
48
+
49
+ ```bash
50
+ # TypeScript template, pin textmode.js, auto-install, do not run dev server
51
+ npm create textmode@latest my-textmode-app -- --template vanilla-ts --textmode-version 0.17.1 --install --no-run
52
+
53
+ # JavaScript template with pnpm, skip install and run
54
+ pnpm create textmode@latest demo -- --template vanilla-js --pm pnpm --no-install --no-run
55
+
56
+ # Pre-install the synth and export add-ons and wire their plugins into the starter sketch
57
+ npm create textmode@latest audio-art -- --template vanilla-js --addons synth,export --install --no-run
58
+ ```
59
+
60
+ ## Options
61
+
62
+ - `--template <name>`: choose a template *(prompts if omitted)*
63
+ - `--addons <name1,name2,...>`: pre-install official textmode.js add-ons and wire their plugins into the starter sketch *(prompts via multi-select if omitted)*
64
+ - `--name <projectName>` or first positional arg: directory/package name *(default suggestion if omitted)*
65
+ - `--textmode-version <ver>`: pin textmode.js *(prompts from fetched stable versions >= 0.17.1; defaults to `latest`)*
66
+ - `--pm <npm|pnpm|yarn|bun>`: force a package manager *(auto-detected otherwise)*
67
+ - `--install` / `--no-install`: install dependencies after scaffold *(prompts if neither is provided)*
68
+ - `--run` / `--no-run`: start the dev server after install *(prompts if neither is provided)*
69
+ - `--force`: allow using a non-empty directory without prompting
70
+ - `--help`: show usage
71
+ - `--version`: show the CLI version
72
+
73
+ ## Templates
74
+
75
+ All templates ship with Vite, `textmode.js`, Prettier, ESLint, and a `.gitignore`. TypeScript variants add `tsconfig` and a `typecheck` script. Below are the extras that differ by template:
76
+
77
+ | Template | Flavor | Extras |
78
+ |:---------|:-------|:-------|
79
+ | `vanilla-js` | JavaScript | Base starter sketch |
80
+ | `vanilla-ts` | TypeScript | Base starter sketch + TS config |
81
+ | `vanilla-js-tweakpane` | JavaScript + Tweakpane | UI controls wired with Tweakpane |
82
+ | `vanilla-ts-tweakpane` | TypeScript + Tweakpane | UI controls wired with Tweakpane + TS config |
83
+
84
+ Adding new templates? Keep the shared tooling note above and only list what’s unique in the table.
85
+
86
+ ## Add-ons
87
+
88
+ All official textmode.js add-on libraries are supported. When selected, the CLI adds the package (always at its latest version) to your `dependencies` and registers its plugin on the `textmode.create({ ... })` call in the starter sketch.
89
+
90
+ | Id | Package | Latest plugin export |
91
+ |:---|:--------|:---------------------|
92
+ | `export` | `textmode.export.js` | `ExportPlugin` |
93
+ | `synth` | `textmode.synth.js` | `SynthPlugin` |
94
+ | `figlet` | `textmode.figlet.js` | `FigletPlugin` |
95
+ | `filters` | `textmode.filters.js` | `FiltersPlugin` |
96
+
97
+ ## Local development
98
+
99
+ ```bash
100
+ npm install # also installs git hooks (commitlint, lint-staged, pre-push protection)
101
+ npm run check # lint + test
102
+ npm run lint # node --check syntax gate
103
+ npm test # vitest (run once)
104
+
105
+ # Run CLI directly (no npm-create shim); no "--" needed
106
+ node bin/index.js demo --template vanilla-js --pm pnpm --no-install --no-run
107
+ # or
108
+ node bin/index.js
109
+ # ...
110
+ ```
111
+
112
+ ## Contributing
113
+
114
+ Thank you for considering contributing to this project! (✿◠‿◠)
115
+
116
+ Please read the [Contributing Guide](https://code.textmode.art/docs/contributing/code) to get started, and follow the [Code of Conduct](./CODE_OF_CONDUCT.md) in all community spaces.
117
+
118
+ ## License
119
+
120
+ `create-textmode` is licensed under the [AGPL-3.0 License](./LICENSE).
package/bin/index.js CHANGED
@@ -1,8 +1,8 @@
1
- #!/usr/bin/env node
2
- import kleur from 'kleur';
3
- import { run } from '../src/cli.js';
4
-
5
- run().catch((err) => {
6
- console.error(kleur.red(err.stack || err.message || err));
7
- process.exit(1);
8
- });
1
+ #!/usr/bin/env node
2
+ import kleur from 'kleur';
3
+ import { run } from '../src/cli.js';
4
+
5
+ run().catch((err) => {
6
+ console.error(kleur.red(err.stack || err.message || err));
7
+ process.exit(1);
8
+ });
package/package.json CHANGED
@@ -1,54 +1,70 @@
1
- {
2
- "name": "create-textmode",
3
- "version": "1.0.6",
4
- "description": "Scaffold textmode.js projects with zero friction",
5
- "type": "module",
6
- "bin": {
7
- "create-textmode": "bin/index.js"
8
- },
9
- "files": [
10
- "bin",
11
- "src",
12
- "templates",
13
- "README.md",
14
- "LICENSE"
15
- ],
16
- "engines": {
17
- "node": ">=18.0.0"
18
- },
19
- "keywords": [
20
- "textmode",
21
- "textmode.js",
22
- "create",
23
- "scaffold",
24
- "template"
25
- ],
26
- "author": "humanbydefinition",
27
- "license": "MIT",
28
- "homepage": "https://github.com/humanbydefinition/create-textmode#readme",
29
- "repository": {
30
- "type": "git",
31
- "url": "git+https://github.com/humanbydefinition/create-textmode.git"
32
- },
33
- "bugs": {
34
- "url": "https://github.com/humanbydefinition/create-textmode/issues"
35
- },
36
- "publishConfig": {
37
- "access": "public"
38
- },
39
- "scripts": {
40
- "test": "vitest",
41
- "test:verbose": "vitest run --reporter=verbose"
42
- },
43
- "dependencies": {
44
- "@clack/prompts": "^0.11.0",
45
- "boxen": "^7.1.1",
46
- "kleur": "^4.1.5",
47
- "minimist": "^1.2.8",
48
- "unique-names-generator": "^4.7.1"
49
- },
50
- "devDependencies": {
51
- "textmode.js": "^0.7.1",
52
- "vitest": "^4.0.15"
53
- }
54
- }
1
+ {
2
+ "name": "create-textmode",
3
+ "version": "1.0.7",
4
+ "description": "Scaffold textmode.js projects with zero friction",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-textmode": "bin/index.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "templates",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "keywords": [
20
+ "textmode",
21
+ "textmode.js",
22
+ "create",
23
+ "scaffold",
24
+ "template"
25
+ ],
26
+ "author": "humanbydefinition",
27
+ "license": "AGPL-3.0",
28
+ "homepage": "https://github.com/humanbydefinition/create-textmode#readme",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/humanbydefinition/create-textmode.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/humanbydefinition/create-textmode/issues"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "scripts": {
40
+ "test": "vitest run",
41
+ "test:watch": "vitest",
42
+ "test:verbose": "vitest run --reporter=verbose",
43
+ "lint": "node --check bin/index.js && node --check src/*.js && node --check tests/*.js",
44
+ "check": "npm run lint && npm run test",
45
+ "prepare": "install-git-hooks"
46
+ },
47
+ "dependencies": {
48
+ "@clack/prompts": "^0.11.0",
49
+ "boxen": "^7.1.1",
50
+ "kleur": "^4.1.5",
51
+ "minimist": "^1.2.8",
52
+ "unique-names-generator": "^4.7.1"
53
+ },
54
+ "devDependencies": {
55
+ "@commitlint/cli": "^21.2.1",
56
+ "@textmode/commitlint-config": "^0.1.1",
57
+ "@textmode/tooling-scripts": "^0.5.0",
58
+ "lint-staged": "^17.0.8",
59
+ "textmode.js": "^0.8.1",
60
+ "vitest": "^4.1.10"
61
+ },
62
+ "commitlint": {
63
+ "extends": [
64
+ "@textmode/commitlint-config"
65
+ ]
66
+ },
67
+ "lint-staged": {
68
+ "*.{js,mjs,cjs}": "node --check"
69
+ }
70
+ }
package/src/args.js CHANGED
@@ -1,30 +1,57 @@
1
- import minimist from 'minimist';
2
- import kleur from 'kleur';
3
- import { templates } from './constants.js';
4
- import { printUsage } from './usage.js';
5
-
6
- export function parseArgv(rawArgs) {
7
- return minimist(rawArgs, {
8
- alias: {
9
- h: 'help',
10
- t: 'template',
11
- n: 'name',
12
- f: 'force',
13
- v: 'version',
14
- tv: 'textmode-version'
15
- },
16
- string: ['template', 'name', 'pm', 'textmode-version'],
17
- boolean: ['help', 'force', 'version', 'install', 'run', 'no-install', 'no-run'],
18
- default: { install: null, run: null }
19
- });
20
- }
21
-
22
- export function ensureKnownTemplate(name) {
23
- if (!name) return;
24
- const found = templates.find((t) => t.name === name);
25
- if (found) return;
26
-
27
- console.error(kleur.red(`Unknown template: ${name}`));
28
- printUsage();
29
- process.exit(1);
30
- }
1
+ import minimist from 'minimist';
2
+ import kleur from 'kleur';
3
+ import { addons, templates } from './constants.js';
4
+ import { printUsage } from './usage.js';
5
+
6
+ export function parseArgv(rawArgs) {
7
+ return minimist(rawArgs, {
8
+ alias: {
9
+ h: 'help',
10
+ t: 'template',
11
+ n: 'name',
12
+ f: 'force',
13
+ v: 'version',
14
+ tv: 'textmode-version'
15
+ },
16
+ string: ['template', 'name', 'pm', 'textmode-version', 'addons'],
17
+ boolean: ['help', 'force', 'version', 'install', 'run', 'no-install', 'no-run'],
18
+ default: { install: null, run: null }
19
+ });
20
+ }
21
+
22
+ export function ensureKnownTemplate(name) {
23
+ if (!name) return;
24
+ const found = templates.find((t) => t.name === name);
25
+ if (found) return;
26
+
27
+ console.error(kleur.red(`Unknown template: ${name}`));
28
+ printUsage();
29
+ process.exit(1);
30
+ }
31
+
32
+ export function ensureKnownAddons(names) {
33
+ if (!names) return;
34
+ const requested = names.split(',').map((s) => s.trim()).filter(Boolean);
35
+ const unknown = requested.filter((name) => !addons.find((a) => a.name === name));
36
+ if (unknown.length === 0) return;
37
+
38
+ console.error(kleur.red(`Unknown add-on(s): ${unknown.join(', ')}`));
39
+ console.error(kleur.gray(`Valid add-ons: ${addons.map((a) => a.name).join(', ')}`));
40
+ printUsage();
41
+ process.exit(1);
42
+ }
43
+
44
+ export function parseAddons(names) {
45
+ if (!names) return [];
46
+
47
+ const uniqueNames = [...new Set(
48
+ names
49
+ .split(',')
50
+ .map((s) => s.trim())
51
+ .filter(Boolean)
52
+ )];
53
+
54
+ return uniqueNames
55
+ .map((name) => addons.find((a) => a.name === name))
56
+ .filter(Boolean);
57
+ }
package/src/banner.js CHANGED
@@ -1,57 +1,57 @@
1
- import kleur from 'kleur';
2
- import { HEADER } from './constants.js';
3
-
4
- const SPLIT_FLAP_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@%&/\\<>[]=+*';
5
-
6
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
-
8
- function randomFlapChar() {
9
- const idx = Math.floor(Math.random() * SPLIT_FLAP_CHARS.length);
10
- return SPLIT_FLAP_CHARS[idx];
11
- }
12
-
13
- async function animateSplitFlap(lines, { flips = 16, frameDelay = 32, columnStagger = 6 } = {}) {
14
- const canAnimate = process.stdout.isTTY && process.env.CI !== 'true';
15
- if (!canAnimate) {
16
- console.log(kleur.cyan(lines.join('\n')));
17
- return;
18
- }
19
-
20
- const lineCount = lines.length;
21
- const blankFrame = lines.map((line) => line.replace(/[^\s]/g, ' '));
22
- let firstRender = true;
23
-
24
- const render = (frame) => {
25
- if (!firstRender) {
26
- process.stdout.write(`\x1b[${lineCount}F`);
27
- } else {
28
- firstRender = false;
29
- }
30
- process.stdout.write(frame.join('\n'));
31
- process.stdout.write('\n');
32
- };
33
-
34
- render(blankFrame);
35
-
36
- for (let flip = 0; flip < flips; flip++) {
37
- const frameLines = lines.map((line) => {
38
- const chars = line.split('').map((ch, idx) => {
39
- if (ch === ' ') return ' ';
40
- const shouldSettle = flip >= flips - 2 || flip > Math.floor(idx / columnStagger);
41
- return shouldSettle ? ch : randomFlapChar();
42
- });
43
- return kleur.cyan(chars.join(''));
44
- });
45
- render(frameLines);
46
- await sleep(frameDelay + flip * 6);
47
- }
48
-
49
- render(lines.map((line) => kleur.cyan(line)));
50
- }
51
-
52
- export async function printHeader() {
53
- const headerLines = HEADER.trim().split('\n');
54
- console.log('');
55
- await animateSplitFlap(headerLines);
56
- console.log('');
57
- }
1
+ import kleur from 'kleur';
2
+ import { HEADER } from './constants.js';
3
+
4
+ const SPLIT_FLAP_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@%&/\\<>[]=+*';
5
+
6
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
+
8
+ function randomFlapChar() {
9
+ const idx = Math.floor(Math.random() * SPLIT_FLAP_CHARS.length);
10
+ return SPLIT_FLAP_CHARS[idx];
11
+ }
12
+
13
+ async function animateSplitFlap(lines, { flips = 16, frameDelay = 32, columnStagger = 6 } = {}) {
14
+ const canAnimate = process.stdout.isTTY && process.env.CI !== 'true';
15
+ if (!canAnimate) {
16
+ console.log(kleur.cyan(lines.join('\n')));
17
+ return;
18
+ }
19
+
20
+ const lineCount = lines.length;
21
+ const blankFrame = lines.map((line) => line.replace(/[^\s]/g, ' '));
22
+ let firstRender = true;
23
+
24
+ const render = (frame) => {
25
+ if (!firstRender) {
26
+ process.stdout.write(`\x1b[${lineCount}F`);
27
+ } else {
28
+ firstRender = false;
29
+ }
30
+ process.stdout.write(frame.join('\n'));
31
+ process.stdout.write('\n');
32
+ };
33
+
34
+ render(blankFrame);
35
+
36
+ for (let flip = 0; flip < flips; flip++) {
37
+ const frameLines = lines.map((line) => {
38
+ const chars = line.split('').map((ch, idx) => {
39
+ if (ch === ' ') return ' ';
40
+ const shouldSettle = flip >= flips - 2 || flip > Math.floor(idx / columnStagger);
41
+ return shouldSettle ? ch : randomFlapChar();
42
+ });
43
+ return kleur.cyan(chars.join(''));
44
+ });
45
+ render(frameLines);
46
+ await sleep(frameDelay + flip * 6);
47
+ }
48
+
49
+ render(lines.map((line) => kleur.cyan(line)));
50
+ }
51
+
52
+ export async function printHeader() {
53
+ const headerLines = HEADER.trim().split('\n');
54
+ console.log('');
55
+ await animateSplitFlap(headerLines);
56
+ console.log('');
57
+ }