omjoshi 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/bin/index.js +217 -0
- package/package.json +27 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import boxen from 'boxen';
|
|
5
|
+
import readline from 'readline/promises';
|
|
6
|
+
import { stdin as input, stdout as output } from 'process';
|
|
7
|
+
import figlet from 'figlet';
|
|
8
|
+
import gradient from 'gradient-string';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import Table from 'cli-table3';
|
|
11
|
+
|
|
12
|
+
// --- DATA ---
|
|
13
|
+
const data = {
|
|
14
|
+
name: chalk.white.bold('Om Joshi'),
|
|
15
|
+
handle: chalk.redBright('@omjoshi'),
|
|
16
|
+
work: chalk.white('Software Engineer'),
|
|
17
|
+
github: chalk.gray('https://github.com/') + chalk.red('omjoshi'),
|
|
18
|
+
linkedin: chalk.gray('https://linkedin.com/in/') + chalk.red('omjoshi'),
|
|
19
|
+
web: chalk.redBright('https://omjoshi.com'),
|
|
20
|
+
|
|
21
|
+
labelWork: chalk.white.bold(' Work:'),
|
|
22
|
+
labelGitHub: chalk.white.bold(' GitHub:'),
|
|
23
|
+
labelLinkedIn: chalk.white.bold(' LinkedIn:'),
|
|
24
|
+
labelWeb: chalk.white.bold(' Web:')
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const projects = [
|
|
28
|
+
{ name: 'Bhakti Link', desc: 'Cross-platform mobile application built using Flutter' },
|
|
29
|
+
{ name: 'AI Tradesperson Assistant', desc: 'Mobile-first, voice-integrated AI brain for trade businesses' },
|
|
30
|
+
{ name: 'ML Trading Bot', desc: 'Local financial trading bot using machine learning and Alpaca API' }
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const commands = ['help', 'about', 'projects', 'skills', 'clear', 'exit', 'quit'];
|
|
34
|
+
|
|
35
|
+
// Tab completion feature
|
|
36
|
+
function completer(line) {
|
|
37
|
+
const hits = commands.filter((c) => c.startsWith(line));
|
|
38
|
+
return [hits.length ? hits : commands, line];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const rl = readline.createInterface({ input, output, completer });
|
|
42
|
+
|
|
43
|
+
// --- UTILS ---
|
|
44
|
+
const sleep = (ms = 1000) => new Promise((r) => setTimeout(r, ms));
|
|
45
|
+
|
|
46
|
+
// Strip ANSI color codes to accurately measure string width in terminal
|
|
47
|
+
const ansiRegex = /\x1B\[[0-9;]*m/g;
|
|
48
|
+
function getVisibleLength(str) {
|
|
49
|
+
return str.replace(ansiRegex, '').length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Center text in the terminal horizontally
|
|
53
|
+
function center(text) {
|
|
54
|
+
const termWidth = process.stdout.columns || 80;
|
|
55
|
+
const lines = text.split('\n');
|
|
56
|
+
|
|
57
|
+
return lines.map(line => {
|
|
58
|
+
const visibleLength = getVisibleLength(line);
|
|
59
|
+
if (visibleLength >= termWidth) return line;
|
|
60
|
+
const padding = Math.max(0, Math.floor((termWidth - visibleLength) / 2));
|
|
61
|
+
return ' '.repeat(padding) + line;
|
|
62
|
+
}).join('\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function showTitle() {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
figlet('Om Joshi', (err, data) => {
|
|
68
|
+
const redGradient = gradient(['#ff4d4d', '#cc0000', '#660000']);
|
|
69
|
+
// Color it first, then center the colored text block
|
|
70
|
+
console.log(center(redGradient.multiline(data)));
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function printAboutBox() {
|
|
77
|
+
const newline = '\n';
|
|
78
|
+
const heading = `${data.name} / ${data.handle}`;
|
|
79
|
+
const working = `${data.labelWork} ${data.work}`;
|
|
80
|
+
const githubing = `${data.labelGitHub} ${data.github}`;
|
|
81
|
+
const linkedining = `${data.labelLinkedIn} ${data.linkedin}`;
|
|
82
|
+
const webing = `${data.labelWeb} ${data.web}`;
|
|
83
|
+
|
|
84
|
+
const outputStr = heading +
|
|
85
|
+
newline + newline +
|
|
86
|
+
working + newline +
|
|
87
|
+
githubing + newline +
|
|
88
|
+
linkedining + newline +
|
|
89
|
+
webing;
|
|
90
|
+
|
|
91
|
+
const options = {
|
|
92
|
+
padding: 1,
|
|
93
|
+
margin: 1,
|
|
94
|
+
borderStyle: 'double',
|
|
95
|
+
borderColor: 'red'
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Create the box, then center the final rendered box string
|
|
99
|
+
const renderedBox = chalk.red(boxen(outputStr, options));
|
|
100
|
+
console.log(center(renderedBox));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function printProjects() {
|
|
104
|
+
const table = new Table({
|
|
105
|
+
head: [chalk.redBright('Project Name'), chalk.redBright('Description')],
|
|
106
|
+
colWidths: [30, 60],
|
|
107
|
+
chars: {
|
|
108
|
+
'top': '═', 'top-mid': '╤', 'top-left': '╔', 'top-right': '╗',
|
|
109
|
+
'bottom': '═', 'bottom-mid': '╧', 'bottom-left': '╚', 'bottom-right': '╝',
|
|
110
|
+
'left': '║', 'left-mid': '╟', 'mid': '─', 'mid-mid': '┼',
|
|
111
|
+
'right': '║', 'right-mid': '╢', 'middle': '│'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
projects.forEach(p => table.push([chalk.white.bold(p.name), chalk.gray(p.desc)]));
|
|
116
|
+
console.log(center(table.toString()));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function printSkills() {
|
|
120
|
+
const table = new Table({
|
|
121
|
+
head: [chalk.redBright('Category'), chalk.redBright('Technologies')],
|
|
122
|
+
colWidths: [20, 70],
|
|
123
|
+
style: { head: [], border: [] }
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
table.push(
|
|
127
|
+
[chalk.white('Languages'), chalk.gray('JavaScript, TypeScript, Python, Dart, HTML, CSS')],
|
|
128
|
+
[chalk.white('Frameworks'), chalk.gray('Node.js, React, Next.js, Flutter')],
|
|
129
|
+
[chalk.white('Tools'), chalk.gray('Git, Docker, Machine Learning, npm')]
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
console.log(center(table.toString()));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function main() {
|
|
136
|
+
console.clear();
|
|
137
|
+
await showTitle();
|
|
138
|
+
|
|
139
|
+
console.log(center(chalk.gray('Welcome to the interactive terminal.')));
|
|
140
|
+
console.log(center(chalk.gray('Hit [TAB] to autocomplete, type "help" for commands, or "exit" to quit.')));
|
|
141
|
+
console.log();
|
|
142
|
+
|
|
143
|
+
while (true) {
|
|
144
|
+
// Generate centered prompt
|
|
145
|
+
const promptChar = '❯ ';
|
|
146
|
+
const termWidth = process.stdout.columns || 80;
|
|
147
|
+
// We add 3 spaces to roughly keep the typed text centered as well
|
|
148
|
+
const padding = Math.max(0, Math.floor((termWidth - promptChar.length) / 2) - 10);
|
|
149
|
+
const centeredPromptStr = ' '.repeat(padding) + chalk.redBright(promptChar);
|
|
150
|
+
|
|
151
|
+
const answer = await rl.question(centeredPromptStr);
|
|
152
|
+
const command = answer.trim().toLowerCase();
|
|
153
|
+
|
|
154
|
+
if (!command) continue;
|
|
155
|
+
|
|
156
|
+
if (command === 'exit' || command === 'quit') {
|
|
157
|
+
console.log(center(chalk.gray('Goodbye!')));
|
|
158
|
+
rl.close();
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (command === 'clear') {
|
|
163
|
+
console.clear();
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Determine left padding for spinner to keep it roughly centered
|
|
168
|
+
const spinnerPadding = ' '.repeat(padding);
|
|
169
|
+
|
|
170
|
+
// Cool spinner effect
|
|
171
|
+
const spinner = ora({
|
|
172
|
+
text: chalk.red(`Executing ${command}...`),
|
|
173
|
+
spinner: 'dots',
|
|
174
|
+
color: 'red',
|
|
175
|
+
prefixText: spinnerPadding
|
|
176
|
+
}).start();
|
|
177
|
+
|
|
178
|
+
await sleep(400);
|
|
179
|
+
|
|
180
|
+
if (command === 'help') {
|
|
181
|
+
spinner.stop();
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(center(chalk.white.bold('Available Commands:')));
|
|
184
|
+
console.log(center(chalk.redBright('about ') + chalk.gray(' - Read about me and find my links')));
|
|
185
|
+
console.log(center(chalk.redBright('projects') + chalk.gray(' - View my recent work')));
|
|
186
|
+
console.log(center(chalk.redBright('skills ') + chalk.gray(' - See my technical arsenal')));
|
|
187
|
+
console.log(center(chalk.redBright('clear ') + chalk.gray(' - Clear the terminal output')));
|
|
188
|
+
console.log(center(chalk.redBright('exit ') + chalk.gray(' - Leave the terminal')));
|
|
189
|
+
console.log();
|
|
190
|
+
} else if (command === 'about') {
|
|
191
|
+
spinner.succeed(chalk.red('Profile loaded\n'));
|
|
192
|
+
printAboutBox();
|
|
193
|
+
console.log();
|
|
194
|
+
} else if (command === 'projects') {
|
|
195
|
+
spinner.succeed(chalk.red('Projects fetched\n'));
|
|
196
|
+
printProjects();
|
|
197
|
+
console.log();
|
|
198
|
+
} else if (command === 'skills') {
|
|
199
|
+
spinner.succeed(chalk.red('Skills compiled\n'));
|
|
200
|
+
printSkills();
|
|
201
|
+
console.log();
|
|
202
|
+
} else {
|
|
203
|
+
spinner.fail(chalk.white(`Command not found: "${command}"`));
|
|
204
|
+
console.log(center(chalk.gray('Type "help" to see available commands.')));
|
|
205
|
+
console.log();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Handle ctrl+c gracefully
|
|
211
|
+
rl.on('SIGINT', () => {
|
|
212
|
+
console.log(center(chalk.gray('\nGoodbye!')));
|
|
213
|
+
rl.close();
|
|
214
|
+
process.exit(0);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "omjoshi",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI Portfolio for Om Joshi",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"omjoshi": "bin/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cli",
|
|
15
|
+
"portfolio"
|
|
16
|
+
],
|
|
17
|
+
"author": "Om Joshi",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"boxen": "^8.0.1",
|
|
21
|
+
"chalk": "^5.6.2",
|
|
22
|
+
"cli-table3": "^0.6.5",
|
|
23
|
+
"figlet": "^1.11.0",
|
|
24
|
+
"gradient-string": "^3.0.0",
|
|
25
|
+
"ora": "^9.4.1"
|
|
26
|
+
}
|
|
27
|
+
}
|