sunmeat 1.0.3 → 1.0.4

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 (3) hide show
  1. package/index.js +504 -288
  2. package/package.json +1 -1
  3. package/update.txt +8 -0
package/index.js CHANGED
@@ -1,289 +1,505 @@
1
- #!/usr/bin/env node
2
-
3
- const readline = require("readline");
4
- const boxen = require("boxen");
5
- const chalk = require("chalk");
6
-
7
- const c = chalk;
8
-
9
- // ─── COLOUR PALETTE ───────────────────────────────────────────────────────────
10
- const accent = c.hex("#5BB0F7").bold;
11
- const dim = c.gray;
12
- const hi = c.white.bold;
13
- const muted = c.hex("#8ab4f8");
14
- const ok = c.green.bold;
15
- const warn = c.yellow;
16
- const err = c.red.bold;
17
- const ukraine1 = c.hex("#005BBB").bold;
18
- const ukraine2 = c.hex("#FFD500").bold;
19
-
20
- // ─── HELPERS ──────────────────────────────────────────────────────────────────
21
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22
-
23
- async function typewrite(text, delay = 18) {
24
- process.stdout.write("\n");
25
- for (const ch of text) {
26
- process.stdout.write(ch);
27
- await sleep(delay);
28
- }
29
- process.stdout.write("\n");
30
- }
31
-
32
- function bar(filled, total = 20, filledChar = "█", emptyChar = "░") {
33
- return (
34
- accent(filledChar.repeat(filled)) +
35
- dim(emptyChar.repeat(total - filled))
36
- );
37
- }
38
-
39
- function section(title) {
40
- console.log("\n" + accent("┌─ ") + hi(title) + accent(" " + "─".repeat(42 - title.length)));
41
- }
42
-
43
- // ─── DATA ─────────────────────────────────────────────────────────────────────
44
- const profile = {
45
- name: "Oleksandr Zahoruiko",
46
- alias: "sunmeat",
47
- age: 36,
48
- location: "Odesa, Ukraine 🇺🇦",
49
- role: "Software Engineer & Lecturer",
50
- org: "IT Academy Step — Leading Teacher Specialist",
51
- since: "2007",
52
- website: "http://sunmeat.site/",
53
- linkedin: "https://www.linkedin.com/in/sunmeat/",
54
- github: "https://github.com/sunmeat",
55
- npx: "npx sunmeat@latest",
56
- };
57
-
58
- const languages = [
59
- { name: "C++", level: 18, label: "Systems & Performance" },
60
- { name: "C#", level: 18, label: ".NET / ASP.NET Core" },
61
- { name: "Java", level: 16, label: "Android & Enterprise" },
62
- { name: "JavaScript", level: 17, label: "Fullstack Web" },
63
- { name: "Python", level: 15, label: "Backend & Automation" },
64
- { name: "SQL", level: 17, label: "Databases" },
65
- { name: "PHP", level: 13, label: "Server-side Web" },
66
- { name: "Kotlin", level: 12, label: "Android" },
67
- ];
68
-
69
- const frameworks = {
70
- backend: ["ASP.NET Core", "EF Core", "Dapper", "Spring", "Django", "Node.js"],
71
- frontend: ["React", "Angular", "Electron.js"],
72
- mobile: ["Java Android", "Kotlin Android", "MAUI"],
73
- };
74
-
75
- const hobbies = [
76
- { icon: "🎹", name: "Music", detail: "accordion · piano · melodica · sopilka · flute · drymba · kalimba · ukulele · guitar" },
77
- { icon: "🎤", name: "Karaoke", detail: "syntax errors don't exist in song" },
78
- { icon: "📷", name: "Photography", detail: "Carpathians, Bulgaria, Amsterdam..." },
79
- { icon: "🌿", name: "Gardening", detail: "same patience as debugging" },
80
- { icon: "🪙", name: "Coin Collecting",detail: "history in the palm of your hand" },
81
- { icon: "🎲", name: "Board Games", detail: "Munchkin — playing like a Boss" },
82
- { icon: "🎨", name: "Acrylic Paint", detail: "creative chaos, proudly committed" },
83
- { icon: "✈️", name: "Travel", detail: "waiting for Ukraine's victory to explore the world again" },
84
- ];
85
-
86
- const quotes = [
87
- { text: "The unreal is more powerful than the real…", author: "Chuck Palahniuk" },
88
- { text: "Stay hungry. Stay foolish.", author: "Steve Jobs" },
89
- { text: "Great minds discuss ideas; average minds discuss events; small minds discuss people.", author: "Eleanor Roosevelt" },
90
- { text: "The best error message is the one that never shows up.", author: "Thomas Fuchs" },
91
- { text: "Code is like humour. When you have to explain it, it's bad.", author: "Cory House" },
92
- ];
93
-
94
- // ─── SCREENS ──────────────────────────────────────────────────────────────────
95
- function showWelcome() {
96
- console.clear();
97
- const banner = [
98
- ukraine1(" █████╗ ██╗ ███████╗██╗ ██╗ "),
99
- ukraine2(" ██╔══██╗██║ ██╔════╝╚██╗██╔╝ "),
100
- ukraine1(" ███████║██║ █████╗ ╚███╔╝ "),
101
- ukraine2(" ██╔══██║██║ ██╔══╝ ██╔██╗ "),
102
- ukraine1(" ██║ ██║███████╗███████╗██╔╝ ██╗ "),
103
- ukraine2(" ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ "),
104
- ].join("\n");
105
-
106
- const card = `
107
- ${banner}
108
-
109
- ${hi("Oleksandr Zahoruiko")} ${dim("aka")} ${accent("sunmeat")}
110
- ${muted("Software Engineer · Lecturer · Developer")}
111
- ${dim("📍 Odesa, Ukraine • IT Academy Step since 2007")}
112
-
113
- ${dim("website →")} ${muted(profile.website)}
114
- ${dim("github →")} ${muted(profile.github)}
115
- ${dim("linkedin ")} ${muted(profile.linkedin)}
116
- `;
117
-
118
- console.log(
119
- boxen(card, {
120
- padding: 1,
121
- margin: { top: 1, left: 2 },
122
- borderStyle: "double",
123
- borderColor: "#5BB0F7",
124
- })
125
- );
126
- }
127
-
128
- function showAbout() {
129
- console.clear();
130
- section("About Me");
131
- console.log(
132
- boxen(
133
- `
134
- ${hi("Oleksandr Zahoruiko")} ${dim("·")} ${accent("age 36")} ${dim("·")} ${muted("Odesa, Ukraine 🇺🇦")}
135
-
136
- ${dim("I'm an educator, lecturer and software engineer.")}
137
- ${dim("Graduated with honours in 2010 (Economics & Programming).")}
138
- ${dim("Since 2007 — Leading Teacher Specialist at IT Academy Step.")}
139
-
140
- ${c.italic.gray('"engaging conversationalist, responsible, hardworking,')}
141
- ${c.italic.gray('optimistic, cheerful, creative, friendly, and modestly humble"')}
142
- ${dim(" — friends & colleagues")}
143
-
144
- ${ukraine1("🇺🇦")} ${warn("Waiting for Ukraine's victory and borders to reopen.")}
145
- ${dim(" Travel is one of my greatest passions.")}
146
- `,
147
- { padding: 1, borderStyle: "round", borderColor: "#5BB0F7" }
148
- )
149
- );
150
- }
151
-
152
- function showSkills() {
153
- console.clear();
154
- section("Skills — Languages");
155
- console.log();
156
- for (const lang of languages) {
157
- const namepad = lang.name.padEnd(13);
158
- console.log(
159
- ` ${accent(namepad)} ${bar(lang.level)} ${dim(" " + lang.label)}`
160
- );
161
- }
162
-
163
- section("Frameworks & Libraries");
164
- console.log();
165
- console.log(` ${hi("Backend :")} ${muted(frameworks.backend.join(" · "))}`);
166
- console.log(` ${hi("Frontend:")} ${muted(frameworks.frontend.join(" · "))}`);
167
- console.log(` ${hi("Mobile :")} ${muted(frameworks.mobile.join(" · "))}`);
168
- }
169
-
170
- function showHobbies() {
171
- console.clear();
172
- section("Hobbies & Passions");
173
- console.log();
174
- for (const h of hobbies) {
175
- console.log(
176
- ` ${h.icon} ${hi(h.name.padEnd(18))} ${dim(h.detail)}`
177
- );
178
- }
179
- }
180
-
181
- function showQuote() {
182
- console.clear();
183
- const q = quotes[Math.floor(Math.random() * quotes.length)];
184
- section("Random Quote");
185
- console.log(
186
- boxen(
187
- `\n ${c.italic.white('"' + q.text + '"')}\n\n ${dim("— " + q.author)}\n`,
188
- { padding: 1, borderStyle: "round", borderColor: "#FFD500" }
189
- )
190
- );
191
- console.log(dim(" (press Enter for another one from the menu)"));
192
- }
193
-
194
- async function showContact() {
195
- console.clear();
196
- section("Contact");
197
- await typewrite(
198
- ` Say hi 👋 → ${profile.website}`,
199
- 20
200
- );
201
- console.log(
202
- boxen(
203
- `
204
- ${hi("🌐 Website ")} ${muted(profile.website)}
205
- ${hi("🐱 GitHub ")} ${muted(profile.github)}
206
- ${hi("💼 LinkedIn ")} ${muted(profile.linkedin)}
207
-
208
- ${dim("npx card ")} ${accent(profile.npx)}
209
- `,
210
- { padding: 1, borderStyle: "round", borderColor: "green" }
211
- )
212
- );
213
- }
214
-
215
- // ─── MENU ─────────────────────────────────────────────────────────────────────
216
- const MENU = [
217
- { key: "1", label: "About Me", fn: showAbout },
218
- { key: "2", label: "Skills & Tech Stack", fn: showSkills },
219
- { key: "3", label: "Hobbies & Passions", fn: showHobbies },
220
- { key: "4", label: "Random Quote", fn: showQuote },
221
- { key: "5", label: "Contact & Links", fn: showContact },
222
- { key: "0", label: "Exit", fn: null },
223
- ];
224
-
225
- function renderMenu() {
226
- console.log("\n" + accent("─".repeat(46)));
227
- console.log(hi(" Choose a section:"));
228
- console.log(accent("".repeat(46)));
229
- for (const item of MENU) {
230
- if (item.key === "0") {
231
- console.log(dim(`\n [${item.key}] ${item.label}`));
232
- } else {
233
- console.log(` ${accent("[" + item.key + "]")} ${muted(item.label)}`);
234
- }
235
- }
236
- console.log(accent("─".repeat(46)));
237
- process.stdout.write(hi("\n ❯ "));
238
- }
239
-
240
- // ─── MAIN LOOP ────────────────────────────────────────────────────────────────
241
- async function main() {
242
- showWelcome();
243
-
244
- const rl = readline.createInterface({
245
- input: process.stdin,
246
- output: process.stdout,
247
- terminal: false,
248
- });
249
-
250
- // Make keypress work properly
251
- if (process.stdin.isTTY) {
252
- process.stdin.setRawMode(false);
253
- }
254
-
255
- const askMenu = () => {
256
- renderMenu();
257
- rl.once("line", async (input) => {
258
- const choice = input.trim();
259
- const item = MENU.find((m) => m.key === choice);
260
-
261
- if (!item) {
262
- console.log(err("\n Invalid choice. Try again."));
263
- return askMenu();
264
- }
265
-
266
- if (item.key === "0") {
267
- console.log(
268
- "\n" +
269
- boxen(
270
- ` ${ukraine1("🇺🇦")} ${hi("Glory to Ukraine!")} ${ukraine2("Glory to the Heroes!")} \n\n ${dim("Thanks for visiting — ")}${accent("sunmeat")}`,
271
- { padding: 1, borderStyle: "round", borderColor: "#FFD500", margin: 1 }
272
- )
273
- );
274
- rl.close();
275
- process.exit(0);
276
- }
277
-
278
- await item.fn();
279
- askMenu();
280
- });
281
- };
282
-
283
- askMenu();
284
- }
285
-
286
- main().catch((e) => {
287
- console.error(e);
288
- process.exit(1);
1
+ #!/usr/bin/env node
2
+
3
+ const readline = require("readline");
4
+ const boxen = require("boxen");
5
+ const chalk = require("chalk");
6
+
7
+ const c = chalk;
8
+ const accent = c.hex("#5BB0F7").bold;
9
+ const dim = c.hex("#4a5568");
10
+ const hi = c.hex("#e8eaf0").bold;
11
+ const muted = c.hex("#8ab4f8");
12
+ const ok = c.hex("#4ade80").bold;
13
+ const warn = c.hex("#facc15");
14
+ const err = c.hex("#f87171").bold;
15
+ const gold = c.hex("#FFD500").bold;
16
+ const steel = c.hex("#94a3b8");
17
+ const soft = c.hex("#cbd5e1");
18
+ const teal = c.hex("#2dd4bf");
19
+
20
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
21
+
22
+ function cls() {
23
+ process.stdout.write("\x1Bc");
24
+ }
25
+
26
+ async function typewrite(text, delay = 13) {
27
+ for (const ch of text) {
28
+ process.stdout.write(ch);
29
+ await sleep(delay);
30
+ }
31
+ process.stdout.write("\n");
32
+ }
33
+
34
+ function bar(filled, total = 20) {
35
+ return accent("█".repeat(filled)) + dim("░".repeat(total - filled));
36
+ }
37
+
38
+ function stripAnsi(str) {
39
+ return str.replace(/\x1B\[[0-9;]*m/g, "").replace(/\x1B\[[\d;]*[A-Za-z]/g, "");
40
+ }
41
+
42
+ function wrapText(text, maxWidth) {
43
+ const words = text.split(" ");
44
+ const lines = [];
45
+ let cur = "";
46
+ for (const w of words) {
47
+ const test = cur ? cur + " " + w : w;
48
+ if (test.length > maxWidth) { if (cur) lines.push(cur); cur = w; }
49
+ else cur = test;
50
+ }
51
+ if (cur) lines.push(cur);
52
+ return lines;
53
+ }
54
+
55
+ const profile = {
56
+ website: "http://sunmeat.site/",
57
+ linkedin: "https://www.linkedin.com/in/sunmeat/",
58
+ github: "https://github.com/sunmeat",
59
+ npx: "npx sunmeat@latest",
60
+ };
61
+
62
+ const languages = [
63
+ { name: "C++", level: 18, label: "Systems & Performance" },
64
+ { name: "C#", level: 18, label: ".NET / ASP.NET Core" },
65
+ { name: "JavaScript", level: 17, label: "Fullstack Web" },
66
+ { name: "SQL", level: 17, label: "Databases" },
67
+ { name: "Java", level: 16, label: "Android & Enterprise" },
68
+ { name: "Python", level: 15, label: "Backend & Automation" },
69
+ { name: "PHP", level: 13, label: "Server-side Web" },
70
+ { name: "Kotlin", level: 12, label: "Android" },
71
+ ];
72
+
73
+ const frameworks = {
74
+ backend: ["ASP.NET Core", "EF Core", "Dapper", "Spring", "Django", "Node.js"],
75
+ frontend: ["React", "Angular", "Electron.js"],
76
+ mobile: ["Java Android", "Kotlin Android", "MAUI"],
77
+ };
78
+
79
+ const devtools = ["Git", "Docker", "VS Code", "Rider", "IntelliJ IDEA", "PostgreSQL", "MySQL", "MongoDB"];
80
+
81
+ const hobbies = [
82
+ { icon: "🎹", name: "Music", detail: "accordion, piano, melodica, sopilka, flute, kalimba, ukulele, guitar" },
83
+ { icon: "🎤", name: "Karaoke", detail: "syntax errors don't exist in song" },
84
+ { icon: "📷", name: "Photography", detail: "Carpathians, Bulgaria, Amsterdam - through the lens" },
85
+ { icon: "🌿", name: "Gardening", detail: "same patience as debugging, more butterflies" },
86
+ { icon: "🎲", name: "Board Games", detail: "Munchkin - playing like a Boss" },
87
+ { icon: "🎨", name: "Acrylic Paint", detail: "creative chaos, proudly committed to every stroke" },
88
+ { icon: "✈️", name: "Travel", detail: "waiting for Ukraine's victory to explore the world again" },
89
+ ];
90
+
91
+ const quotes = [
92
+ { text: "The unreal is more powerful than the real.", author: "Chuck Palahniuk" },
93
+ { text: "Stay hungry. Stay foolish.", author: "Steve Jobs" },
94
+ { text: "Great minds discuss ideas; average minds discuss events; small minds discuss people.", author: "Eleanor Roosevelt" },
95
+ { text: "The best error message is the one that never shows up.", author: "Thomas Fuchs" },
96
+ { text: "Code is like humour. When you have to explain it, it's bad.", author: "Cory House" },
97
+ { text: "Any fool can write code a computer understands. Good programmers write code humans understand.", author: "Martin Fowler" },
98
+ { text: "First, solve the problem. Then, write the code.", author: "John Johnson" },
99
+ { text: "Make it work, make it right, make it fast.", author: "Kent Beck" },
100
+ { text: "Simplicity is the soul of efficiency.", author: "Austin Freeman" },
101
+ { text: "Before software can be reusable it first has to be usable.", author: "Ralph Johnson" },
102
+ { text: "The most dangerous phrase in the language is: we've always done it this way.", author: "Grace Hopper" },
103
+ { text: "Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away.", author: "Antoine de Saint-Exupery"},
104
+ { text: "In theory, theory and practice are the same. In practice, they are not.", author: "Albert Einstein" },
105
+ { text: "Tell me and I forget. Teach me and I remember. Involve me and I learn.", author: "Benjamin Franklin" },
106
+ { text: "The mediocre teacher tells. The good teacher explains. The great teacher inspires.", author: "William Arthur Ward" },
107
+ { text: "Education is not the filling of a pail, but the lighting of a fire.", author: "W.B. Yeats" },
108
+ { text: "A teacher affects eternity; he can never tell where his influence stops.", author: "Henry Brooks Adams" },
109
+ { text: "The art of teaching is the art of assisting discovery.", author: "Mark Van Doren" },
110
+ { text: "It does not matter how slowly you go as long as you do not stop.", author: "Confucius" },
111
+ { text: "The only way to do great work is to love what you do.", author: "Steve Jobs" },
112
+ { text: "Programming isn't about what you know; it's about what you can figure out.", author: "Chris Pine" },
113
+ { text: "Software is a great combination of artistry and engineering.", author: "Bill Gates" },
114
+ { text: "Always code as if the person maintaining your code is a violent psychopath who knows where you live.", author: "Martin Golding" },
115
+ { text: "Debugging is twice as hard as writing the code in the first place.", author: "Brian W. Kernighan" },
116
+ { text: "Walking on water and developing software from a spec are easy if both are frozen.", author: "Edward V. Berard" },
117
+ { text: "Life is what happens when you're busy making other plans.", author: "John Lennon" },
118
+ { text: "In the middle of every difficulty lies opportunity.", author: "Albert Einstein" },
119
+ { text: "Imagination is more important than knowledge.", author: "Albert Einstein" },
120
+ { text: "The function of good software is to make the complex appear simple.", author: "Grady Booch" },
121
+ { text: "It always seems impossible until it's done.", author: "Nelson Mandela" },
122
+ { text: "The cave you fear to enter holds the treasure you seek.", author: "Joseph Campbell" },
123
+ { text: "We are what we repeatedly do. Excellence, then, is not an act but a habit.", author: "Aristotle" },
124
+ { text: "A person who never made a mistake never tried anything new.", author: "Albert Einstein" },
125
+ { text: "You can't connect the dots looking forward; you can only connect them looking backward.", author: "Steve Jobs" },
126
+ { text: "The secret of getting ahead is getting started.", author: "Mark Twain" },
127
+ { text: "The beautiful thing about learning is that nobody can take it away from you.", author: "B.B. King" },
128
+ { text: "Simplicity does not precede complexity, but follows it.", author: "Alan Perlis" },
129
+ { text: "Do not wait to strike till the iron is hot; make it hot by striking.", author: "W.B. Yeats" },
130
+ { text: "One looks back with appreciation to the brilliant teachers.", author: "Carl Jung" },
131
+ { text: "The best teacher is one who suggests rather than dogmatizes.", author: "Edward Bulwer-Lytton" },
132
+ ];
133
+
134
+ function bx(content, borderColor, title) {
135
+ const opts = {
136
+ padding: { top: 1, bottom: 1, left: 2, right: 2 },
137
+ margin: { top: 0, bottom: 1, left: 2, right: 0 },
138
+ borderStyle: "round",
139
+ borderColor,
140
+ };
141
+ if (title) { opts.title = title; opts.titleAlignment = "center"; }
142
+ return boxen(content, opts);
143
+ }
144
+
145
+ function showWelcome() {
146
+ cls();
147
+
148
+ const banner = [
149
+ c.hex("#3a8ee6").bold("██████╗ ██╗ ███████╗██╗ ██╗"),
150
+ c.hex("#4d9df0").bold("██╔══██╗██║ ██╔════╝╚██╗██╔╝"),
151
+ c.hex("#5faaff").bold("███████║██║ █████╗ ╚███╔╝ "),
152
+ c.hex("#79baff").bold("██╔══██║██║ ██╔══╝ ██╔██╗ "),
153
+ c.hex("#95caff").bold("██║ ██║███████╗███████╗██╔╝ ██╗"),
154
+ c.hex("#afd9ff").bold("╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝"),
155
+ ].join("\n");
156
+
157
+ const content = [
158
+ banner,
159
+ "",
160
+ dim("┄".repeat(36)),
161
+ "",
162
+ hi("Oleksandr Zahoruiko") + " " + dim("·") + " " + accent("@sunmeat"),
163
+ steel("Engineer · Lecturer · Developer"),
164
+ dim("Odesa, Ukraine · IT Academy Step · since 2007"),
165
+ "",
166
+ dim("".repeat(36)),
167
+ "",
168
+ dim("web → ") + muted("sunmeat.site"),
169
+ dim("github → ") + muted("github.com/sunmeat"),
170
+ dim("linkedin → ") + muted("linkedin.com/in/sunmeat"),
171
+ "",
172
+ dim("run card ") + accent("npx sunmeat@latest"),
173
+ ].join("\n");
174
+
175
+ console.log(boxen(content, {
176
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
177
+ margin: { top: 1, bottom: 1, left: 2, right: 0 },
178
+ borderStyle: "double",
179
+ borderColor: "#3d7fc9",
180
+ }));
181
+ }
182
+
183
+ function showAbout() {
184
+ cls();
185
+
186
+ const content = [
187
+ hi("Oleksandr Zahoruiko"),
188
+ steel("Age 37 · Odesa, Ukraine · since 2007"),
189
+ "",
190
+ dim("┄".repeat(48)),
191
+ "",
192
+ soft("Educator, lecturer, and software engineer."),
193
+ soft("Graduated with honours in 2010 (Economics & Programming)."),
194
+ soft("Leading Teacher Specialist at IT Academy Step."),
195
+ "",
196
+ soft("I genuinely love what I do - teaching isn't just a job,"),
197
+ soft("it's the way I make sense of the world."),
198
+ "",
199
+ dim("┄".repeat(48)),
200
+ "",
201
+ dim("[") + teal("personality") + dim("]"),
202
+ dim("engaging · responsible · optimistic · creative · friendly"),
203
+ "",
204
+ dim("[") + teal("currently") + dim("]"),
205
+ warn("Waiting for Ukraine's victory and open borders."),
206
+ dim("Travel is one of my greatest passions."),
207
+ ].join("\n");
208
+
209
+ console.log(bx(content, "#5BB0F7", " About Me "));
210
+ }
211
+
212
+ function showSkills() {
213
+ cls();
214
+
215
+ const langLines = languages.map((lang) => {
216
+ const nm = muted(lang.name.padEnd(13));
217
+ const b = bar(lang.level);
218
+ const lbl = dim(" " + lang.label);
219
+ return nm + " " + b + lbl;
220
+ });
221
+
222
+ console.log(bx(langLines.join("\n"), "#3d7fc9", " Languages & Proficiency "));
223
+
224
+ const dot = dim(" · ");
225
+ const fmtList = (arr) => arr.map((x) => teal(x)).join(dot);
226
+
227
+ const fwLines = [
228
+ hi("Backend ") + dim(" · ") + fmtList(frameworks.backend),
229
+ "",
230
+ hi("Frontend") + dim(" · ") + fmtList(frameworks.frontend),
231
+ "",
232
+ hi("Mobile ") + dim(" · ") + fmtList(frameworks.mobile),
233
+ "",
234
+ hi("Tools ") + dim(" · ") + fmtList(devtools),
235
+ ].join("\n");
236
+
237
+ console.log(bx(fwLines, "#2dd4bf", " Frameworks & Tools "));
238
+ }
239
+
240
+ function showHobbies() {
241
+ cls();
242
+
243
+ const lines = hobbies.map((h) => {
244
+ return h.icon + " " + hi(h.name.padEnd(16)) + " " + dim(h.detail);
245
+ });
246
+
247
+ console.log(bx(lines.join("\n"), "#fb7185", " Hobbies & Passions "));
248
+ }
249
+
250
+ function showQuote() {
251
+ cls();
252
+
253
+ const q = quotes[Math.floor(Math.random() * quotes.length)];
254
+ const lines = wrapText(q.text, 54);
255
+
256
+ const formatted = lines.map((l, i) => {
257
+ if (lines.length === 1) return c.italic.hex("#e2e8f0")('"' + l + '"');
258
+ if (i === 0) return c.italic.hex("#e2e8f0")('"' + l);
259
+ if (i === lines.length - 1) return c.italic.hex("#e2e8f0")(l + '"');
260
+ return c.italic.hex("#e2e8f0")(l);
261
+ });
262
+
263
+ const content = [
264
+ ...formatted,
265
+ "",
266
+ dim("") + steel(q.author),
267
+ ].join("\n");
268
+
269
+ console.log(bx(content, "#FFD500", " My favourite quotes "));
270
+ }
271
+
272
+ async function showContact() {
273
+ cls();
274
+
275
+ process.stdout.write("\n " + dim("opening browser... "));
276
+ await typewrite(accent("sunmeat.site"), 22);
277
+ console.log();
278
+
279
+ const content = [
280
+ ok("web ") + " " + muted("http://sunmeat.site/"),
281
+ ok("github ") + " " + muted("https://github.com/sunmeat"),
282
+ ok("linkedin") + " " + muted("https://www.linkedin.com/in/sunmeat/"),
283
+ "",
284
+ dim("┄".repeat(44)),
285
+ "",
286
+ dim("npx → ") + accent("npx sunmeat@latest"),
287
+ ].join("\n");
288
+
289
+ console.log(bx(content, "#4ade80", " Contact & Links "));
290
+
291
+ try {
292
+ const { exec } = require("child_process");
293
+ const url = "http://sunmeat.site/";
294
+ const cmd = process.platform === "win32"
295
+ ? `start "" "${url}"`
296
+ : process.platform === "darwin"
297
+ ? `open "${url}"`
298
+ : `xdg-open "${url}"`;
299
+ exec(cmd);
300
+ console.log(" " + ok("Opened: ") + muted("http://sunmeat.site/") + "\n");
301
+ } catch (_) {
302
+ console.log(" " + dim("Visit: ") + muted("http://sunmeat.site/") + "\n");
303
+ }
304
+ }
305
+
306
+ async function showMatrix(onExit) {
307
+ cls();
308
+ process.stdout.write("\x1B[?25l");
309
+
310
+ const termCols = process.stdout.columns || 80;
311
+ const termRows = process.stdout.rows || 24;
312
+ const colCount = Math.floor(termCols / 2);
313
+ const rowCount = termRows - 1;
314
+
315
+ const charset = "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン0123456789ABCDEF".split("");
316
+
317
+ const grid = Array.from({ length: rowCount }, () =>
318
+ Array.from({ length: colCount }, () => ({ ch: " ", age: 99 }))
319
+ );
320
+
321
+ const drops = Array.from({ length: colCount }, (_, i) => ({
322
+ col: i,
323
+ row: -(Math.random() * rowCount),
324
+ speed: 0.3 + Math.random() * 0.7,
325
+ len: 6 + Math.floor(Math.random() * 14),
326
+ }));
327
+
328
+ let running = true;
329
+
330
+ const rlMatrix = readline.createInterface({ input: process.stdin, terminal: false });
331
+ rlMatrix.once("line", () => { running = false; rlMatrix.close(); });
332
+
333
+ while (running) {
334
+ for (const drop of drops) {
335
+ const r = Math.floor(drop.row);
336
+ if (r >= 0 && r < rowCount && drop.col >= 0 && drop.col < colCount) {
337
+ grid[r][drop.col] = { ch: charset[Math.floor(Math.random() * charset.length)], age: 0 };
338
+ }
339
+
340
+ for (let i = 1; i <= drop.len; i++) {
341
+ const tr = r - i;
342
+ if (tr >= 0 && tr < rowCount && drop.col >= 0 && drop.col < colCount) {
343
+ if (grid[tr][drop.col].age > 18) grid[tr][drop.col] = { ch: " ", age: 99 };
344
+ }
345
+ }
346
+
347
+ drop.row += drop.speed;
348
+ if (drop.row - drop.len > rowCount) {
349
+ drop.row = -(Math.random() * rowCount * 0.5);
350
+ drop.len = 6 + Math.floor(Math.random() * 14);
351
+ drop.speed = 0.3 + Math.random() * 0.7;
352
+ }
353
+ }
354
+
355
+ for (let r = 0; r < rowCount; r++)
356
+ for (let col = 0; col < colCount; col++)
357
+ if (grid[r][col].age < 99) grid[r][col].age++;
358
+
359
+ process.stdout.write("\x1B[H");
360
+
361
+ const dropSet = new Set(drops.map((d) => Math.floor(d.row) * colCount + d.col));
362
+
363
+ const outputLines = [];
364
+ for (let r = 0; r < rowCount; r++) {
365
+ let line = "";
366
+ for (let col = 0; col < colCount; col++) {
367
+ const cell = grid[r][col];
368
+ if (cell.ch === " " || cell.age >= 99) {
369
+ line += " ";
370
+ } else if (dropSet.has(r * colCount + col)) {
371
+ line += c.hex("#ffffff").bold(cell.ch) + " ";
372
+ } else if (cell.age < 3) {
373
+ line += c.hex("#aaffcc")(cell.ch) + " ";
374
+ } else if (cell.age < 8) {
375
+ line += c.hex("#00ee44")(cell.ch) + " ";
376
+ } else if (cell.age < 16) {
377
+ line += c.hex("#007722")(cell.ch) + " ";
378
+ } else {
379
+ line += c.hex("#003311")(cell.ch) + " ";
380
+ }
381
+ }
382
+ outputLines.push(line);
383
+ }
384
+
385
+ const exitMsg = "[ Press Enter to exit ]";
386
+ const padLeft = Math.max(0, Math.floor(termCols / 2) - Math.ceil(exitMsg.length / 2));
387
+ outputLines.push(" ".repeat(padLeft) + c.hex("#ffffff").bold(exitMsg));
388
+
389
+ process.stdout.write(outputLines.join("\n"));
390
+ await sleep(70);
391
+ }
392
+
393
+ process.stdout.write("\x1B[?25h");
394
+ onExit();
395
+ }
396
+
397
+ const MENU = [
398
+ { key: "1", label: "About Me" },
399
+ { key: "2", label: "Skills & Tech Stack" },
400
+ { key: "3", label: "Hobbies & Passions" },
401
+ { key: "4", label: "Random Quote" },
402
+ { key: "5", label: "Contact & Links" },
403
+ { key: "6", label: "Matrix Mode" },
404
+ { key: "0", label: "Exit" },
405
+ ];
406
+
407
+ const SCREENS = {
408
+ "1": showAbout,
409
+ "2": showSkills,
410
+ "3": showHobbies,
411
+ "4": showQuote,
412
+ "5": showContact,
413
+ };
414
+
415
+ function renderMenu() {
416
+ const W = 36;
417
+ const bdr = c.hex("#3d7fc9");
418
+ const top = bdr("╭" + "─".repeat(W) + "╮");
419
+ const sep = bdr("├" + "─".repeat(W) + "┤");
420
+ const bot = bdr("╰" + "─".repeat(W) + "╯");
421
+
422
+ const ts = " Navigation ";
423
+ const tpad = Math.floor((W - ts.length) / 2);
424
+ const tline = bdr("│") + " ".repeat(tpad) + hi(ts) + " ".repeat(W - tpad - ts.length) + bdr("│");
425
+
426
+ console.log("\n " + top);
427
+ console.log(" " + tline);
428
+ console.log(" " + sep);
429
+
430
+ for (const item of MENU) {
431
+ const key = "[" + item.key + "]";
432
+ const rawLen = 2 + key.length + 2 + item.label.length;
433
+ const pad = " ".repeat(Math.max(0, W - rawLen));
434
+
435
+ if (item.key === "0") {
436
+ console.log(" " + bdr("│") + " " + dim(key) + " " + dim(item.label) + pad + bdr("│"));
437
+ } else if (item.key === "6") {
438
+ console.log(" " + bdr("│") + " " + c.hex("#00ff41").bold(key) + " " + c.hex("#00cc33")(item.label) + pad + bdr("│"));
439
+ } else {
440
+ console.log(" " + bdr("│") + " " + accent(key) + " " + muted(item.label) + pad + bdr("│"));
441
+ }
442
+ }
443
+
444
+ console.log(" " + bot);
445
+ process.stdout.write("\n " + hi("> "));
446
+ }
447
+
448
+ async function main() {
449
+ showWelcome();
450
+
451
+ const rl = readline.createInterface({
452
+ input: process.stdin,
453
+ output: process.stdout,
454
+ terminal: false,
455
+ });
456
+
457
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
458
+
459
+ const askMenu = () => {
460
+ renderMenu();
461
+ rl.once("line", async (input) => {
462
+ const choice = input.trim();
463
+ const item = MENU.find((m) => m.key === choice);
464
+
465
+ if (!item) {
466
+ console.log(err("\n Invalid choice. Please try again."));
467
+ return askMenu();
468
+ }
469
+
470
+ if (item.key === "0") {
471
+ cls();
472
+ const content = [
473
+ c.hex("#4488ff").bold("Glory to Ukraine!") + " " + gold("Glory to the Heroes!"),
474
+ "",
475
+ dim("Thanks for visiting - ") + accent("sunmeat"),
476
+ ].join("\n");
477
+ console.log(bx(content, "#FFD500"));
478
+ rl.close();
479
+ process.exit(0);
480
+ }
481
+
482
+ if (item.key === "6") {
483
+ rl.pause();
484
+ await showMatrix(() => {
485
+ rl.resume();
486
+ cls();
487
+ showWelcome();
488
+ askMenu();
489
+ });
490
+ return;
491
+ }
492
+
493
+ await SCREENS[item.key]();
494
+ askMenu();
495
+ });
496
+ };
497
+
498
+ askMenu();
499
+ }
500
+
501
+ main().catch((e) => {
502
+ process.stdout.write("\x1B[?25h");
503
+ console.error(e);
504
+ process.exit(1);
289
505
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sunmeat",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "author": "sunmeat",
package/update.txt ADDED
@@ -0,0 +1,8 @@
1
+ change version
2
+ change code
3
+ cd path to folder
4
+ npx . for local tests
5
+ npm login if needed
6
+ npm publish
7
+
8
+