@nangabhaalu/andu 1.1.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 ADDED
@@ -0,0 +1,72 @@
1
+ # โšก Retro Portfolio CLI
2
+
3
+ A **cyber-arcade style terminal portfolio** you can run instantly with `npx`.
4
+ Built with Node.js โ€” retro neon aesthetics, smooth animations, and interactive navigation.
5
+
6
+ ![retro](https://img.shields.io/badge/aesthetic-retro-ff00ff?style=for-the-badge)
7
+ ![node](https://img.shields.io/badge/node-%3E%3D18-00ffff?style=for-the-badge)
8
+ ![license](https://img.shields.io/badge/license-MIT-39ff14?style=for-the-badge)
9
+
10
+ ## ๐Ÿš€ Quick Start
11
+
12
+ ```bash
13
+ npx retro-portfolio
14
+ ```
15
+
16
+ That's it. No install, no setup, no config.
17
+
18
+ ## ๐Ÿ–ฅ What You Get
19
+
20
+ - **Boot animation** โ€” scanline flicker + loading spinner
21
+ - **ASCII art banner** โ€” your name in massive gradient text
22
+ - **Interactive menu** โ€” arrow-key navigation across 5 sections
23
+ - **Projects gallery** โ€” browse & open GitHub repos in your browser
24
+ - **Social links** โ€” Instagram, X, LinkedIn, Discord, GitHub
25
+ - **Spotify playlists** โ€” curated mood playlists, open with one click
26
+ - **About page** โ€” bio, skills, interests, current project
27
+ - **Retro styling** โ€” neon cyan/magenta/green/amber on dark backgrounds
28
+
29
+ ## ๐Ÿ›  Local Development
30
+
31
+ ```bash
32
+ git clone https://github.com/yourusername/retro-portfolio-cli.git
33
+ cd retro-portfolio-cli
34
+ npm install
35
+ npm start
36
+ ```
37
+
38
+ ## โœ๏ธ Customisation
39
+
40
+ Edit `src/data.js` to replace placeholder content with your own:
41
+
42
+ - `profile` โ€” name, tagline, bio, skills, interests
43
+ - `projects` โ€” GitHub repos with descriptions
44
+ - `socials` โ€” social media links
45
+ - `playlists` โ€” Spotify playlist URLs
46
+
47
+ ## ๐Ÿ“ฆ Publishing to npm
48
+
49
+ ```bash
50
+ npm login
51
+ npm publish
52
+ ```
53
+
54
+ Users can then run `npx retro-portfolio` to experience your portfolio.
55
+
56
+ ## ๐Ÿ“‚ Project Structure
57
+
58
+ ```
59
+ retro-portfolio-cli/
60
+ โ”œโ”€โ”€ package.json # npm manifest & bin entry
61
+ โ”œโ”€โ”€ .gitignore
62
+ โ”œโ”€โ”€ README.md
63
+ โ””โ”€โ”€ src/
64
+ โ”œโ”€โ”€ cli.js # entry point โ€” boot, banner, menu loop
65
+ โ”œโ”€โ”€ data.js # placeholder portfolio data
66
+ โ”œโ”€โ”€ screens.js # section renderers (home, projects, etc.)
67
+ โ””โ”€โ”€ ui.js # colour helpers, dividers, animations
68
+ ```
69
+
70
+ ## License
71
+
72
+ MIT
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@nangabhaalu/andu",
3
+ "version": "1.1.0",
4
+ "description": "โšก ANDU CLI โ€” PROMPT & LET AI THINK V1.1 โ€” Interactive Terminal Experience",
5
+ "bin": {
6
+ "andu": "src/cli.js"
7
+ },
8
+ "main": "src/cli.js",
9
+ "homepage": "https://github.com/AnujDubeyy/andu-cli",
10
+ "author": "Anuj Dubey",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "cli",
14
+ "portfolio",
15
+ "terminal",
16
+ "npx",
17
+ "interactive",
18
+ "anuj-dubey",
19
+ "andu"
20
+ ],
21
+ "scripts": {
22
+ "start": "node src/cli.js"
23
+ },
24
+ "type": "module",
25
+ "dependencies": {
26
+ "boxen": "^7.1.1",
27
+ "chalk": "^5.3.0",
28
+ "figlet": "^1.7.0",
29
+ "gradient-string": "^2.0.2",
30
+ "inquirer": "^9.2.12",
31
+ "nanospinner": "^1.1.0",
32
+ "open": "^10.1.0",
33
+ "ora": "^7.0.1"
34
+ }
35
+ }
package/src/cli.js ADDED
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+
3
+ import gradient from 'gradient-string';
4
+ import figlet from 'figlet';
5
+ import { createSpinner } from 'nanospinner';
6
+ import inquirer from 'inquirer';
7
+
8
+ import { t, sleep, typeText, clearScreen, checkTerminalSize, DIVIDER, currentTheme, setTheme } from './ui.js';
9
+ import { projects, socials, playlists, profile, quotes } from './data.js';
10
+ import { showProjects, showSocials, showSpotify, showAbout } from './screens.js';
11
+
12
+ function setupExitHandlers() {
13
+ process.on('SIGINT', () => {
14
+ console.log(t.dim('\n\n โœฆ Signal received. Shutting down gracefully...\n'));
15
+ process.exit(0);
16
+ });
17
+ process.on('uncaughtException', (err) => {
18
+ console.error(t.secondary(`\n โš  Unexpected error: ${err.message}\n`));
19
+ process.exit(1);
20
+ });
21
+ }
22
+
23
+ const getGradient = () => {
24
+ return currentTheme === 'loki'
25
+ ? gradient([{ color: '#39FF14', pos: 0 }, { color: '#FFD700', pos: 1 }])
26
+ : gradient([{ color: '#FF0000', pos: 0 }, { color: '#0000FF', pos: 1 }]);
27
+ };
28
+
29
+ async function bootSequence() {
30
+ clearScreen();
31
+ checkTerminalSize();
32
+
33
+ console.log();
34
+ for (let i = 0; i < 3; i++) {
35
+ process.stdout.write(t.dim(' โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ AWAKENING GOD PROTOCOL โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘'));
36
+ await sleep(120);
37
+ process.stdout.write('\r' + ' '.repeat(60) + '\r');
38
+ await sleep(80);
39
+ }
40
+
41
+ const gColor = (text) => t.primary(text);
42
+ const spinner = createSpinner(gColor(' Requesting God api credits...')).start();
43
+ await sleep(600);
44
+ spinner.update({ text: gColor(' Forging timelines...') });
45
+ await sleep(500);
46
+ spinner.update({ text: gColor(' Injecting free-thinking logic...') });
47
+ await sleep(500);
48
+ spinner.update({ text: gColor(' Rendering interface...') });
49
+ await sleep(400);
50
+ spinner.success({ text: t.primary(' Protocol Online. Realizing god doesnt exist...\n') });
51
+
52
+ await sleep(300);
53
+ }
54
+
55
+ async function showBanner() {
56
+ const bannerText = figlet.textSync(profile.name, {
57
+ font: 'ANSI Shadow',
58
+ horizontalLayout: 'fitted',
59
+ });
60
+
61
+ console.log(getGradient().multiline(bannerText));
62
+ console.log();
63
+ console.log(t.dim(' โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—'));
64
+ console.log(t.dim(' โ•‘') + t.primary(' โšก ANDU CLI - PROMPT & LET AI THINK V1.1 ') + t.dim('โ•‘'));
65
+ console.log(t.dim(' โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'));
66
+ console.log();
67
+ await typeText(t.neutral(' > ' + profile.tagline), 20);
68
+ console.log();
69
+ }
70
+
71
+ async function mainMenu() {
72
+ while (true) {
73
+ clearScreen();
74
+ await showBanner();
75
+
76
+ const q = quotes[Math.floor(Math.random() * quotes.length)] + " ~ Anuj Dubey";
77
+
78
+ const choices = [
79
+ { name: `${t.primary('โ—ˆ')} ${t.bold('Projects')} ${t.dim('โ€” crewmap & the future')}`, value: 'projects' },
80
+ { name: `${t.primary('โ—‰')} ${t.bold('Socials')} ${t.dim('โ€” x / blog / behance')}`, value: 'socials' },
81
+ { name: `${t.primary('โ™ซ')} ${t.bold('Spotify')} ${t.dim('โ€” curated playlists')}`, value: 'spotify' },
82
+ { name: `${t.primary('โ—Ž')} ${t.bold('About me')} ${t.dim('โ€” ahh geez')}`, value: 'about' },
83
+ new inquirer.Separator(DIVIDER()),
84
+ { name: `${t.secondary('โ˜ฏ')} ${t.bold('Different Shaders')} ${t.dim('โ€” Toggle theme colors')}`, value: 'theme' },
85
+ { name: `${t.secondary('โป')} ${t.bold('Exit')} ${t.dim('โ€” see you later')}`, value: 'exit' },
86
+ new inquirer.Separator(' '),
87
+ new inquirer.Separator(t.dim(' ใ€Œ ' + q + ' ใ€'))
88
+ ];
89
+
90
+ const { section } = await inquirer.prompt([
91
+ {
92
+ type: 'list',
93
+ name: 'section',
94
+ message: getGradient()(' โ—„ MAIN MENU โ–บ'),
95
+ choices,
96
+ pageSize: 15,
97
+ loop: false,
98
+ },
99
+ ]);
100
+
101
+ clearScreen();
102
+
103
+ switch (section) {
104
+ case 'projects':
105
+ await showProjects(projects);
106
+ break;
107
+ case 'socials':
108
+ await showSocials(socials);
109
+ break;
110
+ case 'spotify':
111
+ await showSpotify(playlists);
112
+ break;
113
+ case 'about':
114
+ await showAbout();
115
+ break;
116
+ case 'theme':
117
+ setTheme(currentTheme === 'loki' ? 'spider' : 'loki');
118
+ continue;
119
+ case 'exit':
120
+ console.log();
121
+ // 1. Scanline glitch effect (mirrors bootSequence)
122
+ for (let i = 0; i < 3; i++) {
123
+ process.stdout.write(t.dim(' โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘ TERMINATING PROTOCOLS โ–‘โ–’โ–“โ–ˆโ–“โ–’โ–‘'));
124
+ await sleep(120);
125
+ process.stdout.write('\r' + ' '.repeat(60) + '\r');
126
+ await sleep(80);
127
+ }
128
+
129
+ const crashSequence = [
130
+ "tryin to log out...",
131
+ "rolling out...",
132
+ "1 2 3 4 5...",
133
+ "67!",
134
+ " *LAUGHS*",
135
+ " *HAHAH*",
136
+ " *DIES*"
137
+ ];
138
+
139
+ for (let i = 0; i < crashSequence.length; i++) {
140
+ const text = crashSequence[i];
141
+ const isError = i === crashSequence.length - 1;
142
+ const display = isError ? t.primary(` โœ–${text}`) : t.secondary(` โ ง${text}`);
143
+
144
+ process.stdout.write('\r' + ' '.repeat(50)); // clear current line space
145
+ process.stdout.write('\r' + display);
146
+ await sleep(400);
147
+ }
148
+
149
+ console.log('\n');
150
+ process.exit(0);
151
+ }
152
+
153
+ console.log();
154
+ console.log(DIVIDER());
155
+ console.log();
156
+ }
157
+ }
158
+
159
+ async function main() {
160
+ setupExitHandlers();
161
+ await bootSequence();
162
+ await mainMenu();
163
+ }
164
+
165
+ main();
package/src/data.js ADDED
@@ -0,0 +1,69 @@
1
+ export const profile = {
2
+ name: 'ANUJ DUBEY',
3
+ tagline: 'Performative Engg, Free-Thinker',
4
+ bio: [
5
+ 'i am 20yo performative engg who gambles on LLM to spit out right code.',
6
+ 'i am also a full stack developer and tech enthusiat until my api credits resist.',
7
+ 'i am moved by current developments in ai world (quite literally).',
8
+ 'my hobbies include watching ai code, crying about job market, crying about my current degree,',
9
+ 'planning for my master to delay unemployment and being scared the shit out of everything',
10
+ '24*7 while still doing nothing.',
11
+ ],
12
+ currentlyBuilding: 'CLI, CrewMap... (2026 is gonna be my year)',
13
+ skills: [
14
+ 'JavaScript / TypeScript',
15
+ 'React ยท Next.js ยท Node',
16
+ 'Thinking Outside The Box',
17
+ 'Mischief & Magic'
18
+ ],
19
+ interests: [
20
+ 'Open Source Contribution where I open and you contribute.',
21
+ 'Free thinkers - made to ragebait two polar opposite groups on the internet.',
22
+ 'Cinephile - has seen Interstellar twice, I have a Letterboxd account.',
23
+ 'Designer - I use Pinterest.',
24
+ 'Philosopher - born to be Diogenes, forced to be Sisyphus.'
25
+ ],
26
+ };
27
+
28
+ export const quotes = [
29
+ '"i lowkey hate myself"',
30
+ '"love is like fart when released u realize it\'s shit"',
31
+ ];
32
+
33
+ export const projects = [
34
+ {
35
+ name: 'CrewMap',
36
+ description: 'Collaborative team mapping and management web app.',
37
+ url: 'https://github.com/AnujDubeyy/crewmap',
38
+ tags: ['React', 'Collaboration'],
39
+ },
40
+ {
41
+ name: 'Bluecheck Advisory',
42
+ description: 'Advisory and consulting platform architecture.',
43
+ url: 'https://github.com/AnujDubeyy',
44
+ tags: ['Web', 'Advisory'],
45
+ },
46
+ {
47
+ name: 'Rastogi',
48
+ description: 'Custom client implementation platform.',
49
+ url: 'https://github.com/AnujDubeyy',
50
+ tags: ['Client', 'Full-Stack'],
51
+ },
52
+ {
53
+ name: 'More to come...',
54
+ description: '2026 is gonna be my year.',
55
+ url: 'https://github.com/AnujDubeyy',
56
+ tags: ['Future', '2026'],
57
+ },
58
+ ];
59
+
60
+ export const socials = [
61
+ { name: 'X (Twitter)', icon: '๐• ', url: 'https://x.com/AnujDubey186224' },
62
+ { name: 'Behance', icon: '๐ŸŽจ', url: 'https://www.behance.net/anujdubey9' },
63
+ { name: 'Blog', icon: '๐Ÿ“', url: 'https://jainaloo.blogspot.com/2025/04/who-tf-r-umaybe-part-1.html' },
64
+ ];
65
+
66
+ export const playlists = [
67
+ { name: '๐ŸŽง Playlist 1', mood: '', url: 'https://open.spotify.com/playlist/2d0iemtr4B3G4XZBGnsdDr?si=OF40YORTTLq2OB8Uxn6QSQ' },
68
+ { name: '๐ŸŽง Playlist 2', mood: '', url: 'https://open.spotify.com/playlist/2d0iemtr4B3G4XZBGnsdDr?si=OF40YORTTLq2OB8Uxn6QSQ' },
69
+ ];
package/src/screens.js ADDED
@@ -0,0 +1,201 @@
1
+ import { t, sectionTitle, DIVIDER, sleep, typeText, currentTheme } from './ui.js';
2
+ import { profile } from './data.js';
3
+ import boxen from 'boxen';
4
+ import open from 'open';
5
+ import inquirer from 'inquirer';
6
+
7
+ // โ”€โ”€โ”€ Projects โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
8
+ export async function showProjects(projects) {
9
+ console.log(sectionTitle('PROJECTS'));
10
+ console.log();
11
+
12
+ const choices = projects.map((p, i) => ({
13
+ name: `${t.primary(p.name)} ${t.dim('โ€”')} ${t.neutral(p.description)} ${t.dim(p.tags.map(tag => `[${tag}]`).join(' '))}`,
14
+ value: i,
15
+ }));
16
+ choices.push(new inquirer.Separator(DIVIDER()));
17
+ choices.push({ name: t.secondary('โ† Back to menu'), value: -1 });
18
+
19
+ const { idx } = await inquirer.prompt([
20
+ {
21
+ type: 'list',
22
+ name: 'idx',
23
+ message: t.secondary('Select a project to view details:'),
24
+ choices,
25
+ pageSize: 10,
26
+ loop: false,
27
+ },
28
+ ]);
29
+
30
+ if (idx === -1) return;
31
+
32
+ const project = projects[idx];
33
+ console.log();
34
+ console.log(
35
+ boxen(
36
+ t.bold(t.primary(project.name)) + '\n\n' +
37
+ t.neutral(project.description) + '\n\n' +
38
+ t.dim('URL: ') + t.secondary(project.url) + '\n' +
39
+ t.dim('Tags: ') + project.tags.map(tg => t.primary(`[${tg}]`)).join(' '),
40
+ {
41
+ padding: 1,
42
+ margin: { left: 2 },
43
+ borderStyle: 'round',
44
+ borderColor: currentTheme === 'loki' ? 'green' : 'blue',
45
+ title: '๐Ÿ“‚ PROJECT DETAIL',
46
+ titleAlignment: 'center',
47
+ }
48
+ )
49
+ );
50
+
51
+ const { action } = await inquirer.prompt([
52
+ {
53
+ type: 'list',
54
+ name: 'action',
55
+ message: t.secondary('What next?'),
56
+ choices: [
57
+ { name: t.primary('๐ŸŒ Open in browser'), value: 'open' },
58
+ { name: t.secondary('โ† Back to projects'), value: 'back' },
59
+ { name: t.secondary('โ† Back to menu'), value: 'menu' },
60
+ ],
61
+ },
62
+ ]);
63
+
64
+ if (action === 'open') {
65
+ try {
66
+ await open(project.url);
67
+ console.log(t.primary(` โœ“ Opened ${project.url}`));
68
+ } catch {
69
+ console.log(t.secondary(` โš  Could not open URL: ${project.url}`));
70
+ }
71
+ await sleep(800);
72
+ }
73
+
74
+ if (action === 'back') {
75
+ await showProjects(projects);
76
+ }
77
+ }
78
+
79
+ // โ”€โ”€โ”€ Socials โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
80
+ export async function showSocials(socials) {
81
+ console.log(sectionTitle('SOCIALS'));
82
+ console.log();
83
+
84
+ const choices = socials.map(s => ({
85
+ name: `${s.icon} ${t.primary(s.name)} ${t.dim('โ†’')} ${t.dim(s.url)}`,
86
+ value: s.url,
87
+ }));
88
+ choices.push(new inquirer.Separator(DIVIDER()));
89
+ choices.push({ name: t.secondary('โ† Back to menu'), value: 'back' });
90
+
91
+ const { url } = await inquirer.prompt([
92
+ {
93
+ type: 'list',
94
+ name: 'url',
95
+ message: t.secondary('Open a social profile:'),
96
+ choices,
97
+ loop: false,
98
+ },
99
+ ]);
100
+
101
+ if (url === 'back') return;
102
+
103
+ try {
104
+ await open(url);
105
+ console.log(t.primary(` โœ“ Opened ${url}`));
106
+ } catch {
107
+ console.log(t.secondary(` โš  Could not open URL: ${url}`));
108
+ }
109
+ await sleep(600);
110
+ await showSocials(socials);
111
+ }
112
+
113
+ // โ”€โ”€โ”€ Spotify โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
114
+ export async function showSpotify(playlists) {
115
+ console.log(sectionTitle('SPOTIFY'));
116
+ console.log();
117
+
118
+ const choices = playlists.map(p => ({
119
+ name: t.primary(p.name),
120
+ value: p.url,
121
+ }));
122
+ choices.push(new inquirer.Separator(DIVIDER()));
123
+ choices.push({ name: t.secondary('โ† Back to menu'), value: 'back' });
124
+
125
+ const { url } = await inquirer.prompt([
126
+ {
127
+ type: 'list',
128
+ name: 'url',
129
+ message: t.secondary('Pick a playlist to open:'),
130
+ choices,
131
+ loop: false,
132
+ },
133
+ ]);
134
+
135
+ if (url === 'back') return;
136
+
137
+ try {
138
+ await open(url);
139
+ console.log(t.primary(` โœ“ Opening playlist in Spotify...`));
140
+ } catch {
141
+ console.log(t.secondary(` โš  Could not open URL: ${url}`));
142
+ }
143
+ await sleep(600);
144
+ await showSpotify(playlists);
145
+ }
146
+
147
+ // โ”€โ”€โ”€ About โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
148
+ export async function showAbout() {
149
+ console.log(sectionTitle('ABOUT ME'));
150
+ console.log();
151
+
152
+ // Combine bio and "Currently building"
153
+ const bioText = profile.bio.map(l => ' > ' + l).join('\n');
154
+ const loreContent = bioText + '\n\n' +
155
+ t.dim(' Currently building: ') + t.primary(profile.currentlyBuilding);
156
+
157
+ const loreBox = boxen(loreContent, {
158
+ padding: 1,
159
+ margin: { left: 2 },
160
+ borderStyle: 'round',
161
+ borderColor: currentTheme === 'loki' ? 'green' : 'red',
162
+ title: 'THE LORE',
163
+ titleAlignment: 'center',
164
+ });
165
+
166
+ // Combine interests
167
+ const interestsText = profile.interests.map(i => ` ${t.primary('ยป')} ${t.neutral(i)}`).join('\n');
168
+ const interestsBox = boxen(interestsText, {
169
+ padding: 1,
170
+ margin: { left: 2 },
171
+ borderStyle: 'round',
172
+ borderColor: currentTheme === 'loki' ? 'yellow' : 'blue',
173
+ title: 'INTERESTS',
174
+ titleAlignment: 'center',
175
+ });
176
+
177
+ const loreLines = loreBox.split('\n');
178
+ for (const line of loreLines) {
179
+ console.log(line);
180
+ await sleep(25);
181
+ }
182
+ console.log();
183
+
184
+ const intLines = interestsBox.split('\n');
185
+ for (const line of intLines) {
186
+ console.log(line);
187
+ await sleep(25);
188
+ }
189
+ console.log();
190
+
191
+ await inquirer.prompt([
192
+ {
193
+ type: 'list',
194
+ name: 'action',
195
+ message: t.secondary('What next?'),
196
+ choices: [
197
+ { name: t.secondary('โ† Back to menu'), value: 'back' },
198
+ ],
199
+ },
200
+ ]);
201
+ }
package/src/ui.js ADDED
@@ -0,0 +1,58 @@
1
+ import chalk from 'chalk';
2
+
3
+ export let currentTheme = 'loki'; // 'loki' or 'spider'
4
+
5
+ export const t = {
6
+ primary: (text) => {
7
+ if (currentTheme === 'loki') return chalk.hex('#39FF14')(text); // Green
8
+ if (currentTheme === 'spider') return chalk.hex('#FF0000')(text); // Red
9
+ },
10
+ secondary: (text) => {
11
+ if (currentTheme === 'loki') return chalk.hex('#FFD700')(text); // Gold
12
+ if (currentTheme === 'spider') return chalk.hex('#0000FF')(text); // Blue
13
+ },
14
+ accent: (text) => {
15
+ if (currentTheme === 'loki') return chalk.hex('#00FF00').bold(text);
16
+ if (currentTheme === 'spider') return chalk.hex('#FF0000').bold(text);
17
+ },
18
+ neutral: (text) => chalk.hex('#E0E0E0')(text),
19
+ dim: (text) => chalk.hex('#555566')(text),
20
+ bold: (text) => chalk.bold(text)
21
+ };
22
+
23
+ export function setTheme(themeName) {
24
+ currentTheme = themeName;
25
+ }
26
+
27
+ export const DIVIDER = () => t.dim('โ”€'.repeat(60));
28
+ export const SCANLINE = () => t.dim('โ–‘'.repeat(60));
29
+
30
+ export function sectionTitle(text) {
31
+ return `\n${DIVIDER()}\n ${t.secondary('โ–บ')} ${t.bold(t.primary(text))}\n${DIVIDER()}`;
32
+ }
33
+
34
+ export function sleep(ms) {
35
+ return new Promise(resolve => setTimeout(resolve, ms));
36
+ }
37
+
38
+ export async function typeText(text, delay = 18) {
39
+ for (const char of text) {
40
+ process.stdout.write(char);
41
+ await sleep(delay);
42
+ }
43
+ process.stdout.write('\n');
44
+ }
45
+
46
+ export function checkTerminalSize() {
47
+ const cols = process.stdout.columns || 80;
48
+ const rows = process.stdout.rows || 24;
49
+ if (cols < 60 || rows < 20) {
50
+ console.log(
51
+ t.secondary('\nโš  Your terminal is a bit small (' + cols + 'ร—' + rows + ').')
52
+ );
53
+ }
54
+ }
55
+
56
+ export function clearScreen() {
57
+ process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
58
+ }