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.
- package/LICENSE +661 -21
- package/README.md +120 -86
- package/bin/index.js +8 -8
- package/package.json +70 -54
- package/src/args.js +57 -30
- package/src/banner.js +57 -57
- package/src/cli.js +166 -154
- package/src/constants.js +59 -23
- package/src/fs-utils.js +136 -85
- package/src/packageManager.js +18 -18
- package/src/prompts.js +102 -90
- package/src/runCommand.js +46 -46
- package/src/summary.js +43 -33
- package/src/textmodeVersion.js +57 -45
- package/src/usage.js +21 -18
- package/src/versions.js +56 -52
- package/templates/vanilla-js/.prettierrc +8 -8
- package/templates/vanilla-js/_gitignore +2 -2
- package/templates/vanilla-js/eslint.config.js +22 -22
- package/templates/vanilla-js/index.html +25 -25
- package/templates/vanilla-js/package.json +26 -26
- package/templates/vanilla-js/src/sketch.js +28 -28
- package/templates/vanilla-js/vite.config.js +7 -7
- package/templates/vanilla-js-tweakpane/_gitignore +2 -2
- package/templates/vanilla-js-tweakpane/eslint.config.js +22 -22
- package/templates/vanilla-js-tweakpane/index.html +37 -37
- package/templates/vanilla-js-tweakpane/package.json +27 -27
- package/templates/vanilla-js-tweakpane/src/sketch.js +62 -62
- package/templates/vanilla-js-tweakpane/vite.config.js +7 -7
- package/templates/vanilla-ts/.prettierrc +8 -8
- package/templates/vanilla-ts/_gitignore +1 -1
- package/templates/vanilla-ts/eslint.config.js +23 -23
- package/templates/vanilla-ts/index.html +26 -26
- package/templates/vanilla-ts/package.json +30 -30
- package/templates/vanilla-ts/src/sketch.ts +28 -28
- package/templates/vanilla-ts/tsconfig.json +15 -15
- package/templates/vanilla-ts/vite.config.ts +7 -7
- package/templates/vanilla-ts-tweakpane/_gitignore +2 -2
- package/templates/vanilla-ts-tweakpane/eslint.config.js +23 -23
- package/templates/vanilla-ts-tweakpane/index.html +37 -37
- package/templates/vanilla-ts-tweakpane/package.json +32 -32
- package/templates/vanilla-ts-tweakpane/src/sketch.ts +72 -72
- package/templates/vanilla-ts-tweakpane/tsconfig.json +15 -15
- package/templates/vanilla-ts-tweakpane/vite.config.ts +7 -7
- package/templates/vanilla-js-fxhash/_gitignore +0 -2
- package/templates/vanilla-js-fxhash/eslint.config.js +0 -22
- package/templates/vanilla-js-fxhash/index.html +0 -27
- package/templates/vanilla-js-fxhash/package.json +0 -26
- package/templates/vanilla-js-fxhash/public/fxhash.min.js +0 -1
- package/templates/vanilla-js-fxhash/src/sketch.js +0 -101
- package/templates/vanilla-js-fxhash/vite.config.js +0 -7
- package/templates/vanilla-ts-fxhash/_gitignore +0 -2
- package/templates/vanilla-ts-fxhash/eslint.config.js +0 -23
- package/templates/vanilla-ts-fxhash/index.html +0 -27
- package/templates/vanilla-ts-fxhash/package.json +0 -30
- package/templates/vanilla-ts-fxhash/public/fxhash.min.js +0 -1
- package/templates/vanilla-ts-fxhash/src/fxhash.d.ts +0 -54
- package/templates/vanilla-ts-fxhash/src/sketch.ts +0 -108
- package/templates/vanilla-ts-fxhash/tsconfig.json +0 -15
- 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
|
-
|
|
6
|
-
|
|
7
|
-
| [](https://developer.mozilla.org/docs/Web/JavaScript) [](https://vitejs.dev/) | [](https://github.com/humanbydefinition/textmode.js) [](https://code.textmode.art/) [](https://discord.gg/sjrw8QXNks) | [](https://ko-fi.com/V7V8JG2FY) [](https://developer.mozilla.org/docs/Web/JavaScript) [](https://vitejs.dev/) | [](https://github.com/humanbydefinition/textmode.js) [](https://code.textmode.art/) [](https://discord.gg/sjrw8QXNks) | [](https://ko-fi.com/V7V8JG2FY) [](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.
|
|
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": "
|
|
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:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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
|
+
}
|