yechancho 1.0.8 → 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/index.js +85 -177
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -6,78 +6,83 @@ import chalk from 'chalk'
|
|
|
6
6
|
import gradient from 'gradient-string'
|
|
7
7
|
import figlet from 'figlet'
|
|
8
8
|
import terminalLink from 'terminal-link'
|
|
9
|
+
import logUpdate from 'log-update'
|
|
9
10
|
import ansiEscapes from 'ansi-escapes'
|
|
10
11
|
import cliCursor from 'cli-cursor'
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Flags:
|
|
14
|
-
* --static no animation, show card only
|
|
15
|
-
* --duration=2500 intro animation length in ms (default 2.5s)
|
|
16
|
-
* --fps=30 animation fps (default 30)
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const args = new Map(
|
|
20
|
-
process.argv.slice(2).map((a) => {
|
|
21
|
-
const [k, v] = a.split('=')
|
|
22
|
-
return [k, v ?? true]
|
|
23
|
-
})
|
|
24
|
-
)
|
|
25
|
-
|
|
26
13
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
|
27
14
|
|
|
15
|
+
// Sakura palette
|
|
28
16
|
const sakura = {
|
|
29
|
-
soft: chalk.hex('#FFD7E5'),
|
|
30
17
|
pink: chalk.hex('#FFB7C5'),
|
|
18
|
+
blossom: chalk.hex('#FFD7E5'),
|
|
31
19
|
rose: chalk.hex('#FF6FAE'),
|
|
32
20
|
lilac: chalk.hex('#C7A2FF'),
|
|
33
21
|
sky: chalk.hex('#7FD1FF'),
|
|
34
22
|
mint: chalk.hex('#8CFFDB'),
|
|
35
23
|
dim: chalk.hex('#9AA0A6'),
|
|
36
|
-
white: chalk.hex('#F8F8FF'),
|
|
37
24
|
}
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
// Links you want
|
|
27
|
+
const links = [
|
|
28
|
+
{
|
|
29
|
+
label: 'LinkedIn',
|
|
30
|
+
url: 'https://www.linkedin.com/in/yechancho5',
|
|
31
|
+
icon: '❀',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: 'GitHub',
|
|
35
|
+
url: 'https://github.com/yechancho5',
|
|
36
|
+
icon: '✿',
|
|
37
|
+
},
|
|
44
38
|
]
|
|
45
39
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
function clamp(n, a, b) {
|
|
49
|
-
return Math.max(a, Math.min(b, n))
|
|
50
|
-
}
|
|
40
|
+
const supportsLinks = terminalLink.isSupported
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
const text = figlet.textSync('YECHAN', {
|
|
54
|
-
font: 'ANSI Shadow',
|
|
55
|
-
horizontalLayout: 'default',
|
|
56
|
-
})
|
|
42
|
+
const makeHeader = () => {
|
|
43
|
+
const text = figlet.textSync('YECHAN', { font: 'ANSI Shadow', horizontalLayout: 'default' })
|
|
57
44
|
return gradient(['#FFB7C5', '#FFD7E5', '#C7A2FF'])(text)
|
|
58
45
|
}
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
sakura.
|
|
67
|
-
sakura.
|
|
68
|
-
sakura.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
47
|
+
const makeBody = () => {
|
|
48
|
+
const bio =
|
|
49
|
+
sakura.blossom("hi, i'm yechan ") +
|
|
50
|
+
sakura.dim('— ') +
|
|
51
|
+
sakura.pink('i build things ') +
|
|
52
|
+
sakura.dim('• ') +
|
|
53
|
+
sakura.lilac('full-stack') +
|
|
54
|
+
sakura.dim(' • ') +
|
|
55
|
+
sakura.sky('systems') +
|
|
56
|
+
'\n'
|
|
57
|
+
|
|
58
|
+
const line = sakura.dim('─'.repeat(36))
|
|
59
|
+
|
|
60
|
+
const linkLines = links
|
|
61
|
+
.map(({ label, url, icon }) => {
|
|
62
|
+
const left = sakura.rose(` ${icon} `) + sakura.pink(`${label}:`).padEnd(12)
|
|
63
|
+
const right = supportsLinks ? terminalLink(sakura.sky(url), url) : sakura.sky(url)
|
|
64
|
+
return left + ' ' + right
|
|
65
|
+
})
|
|
66
|
+
.join('\n')
|
|
67
|
+
|
|
68
|
+
const now =
|
|
69
|
+
sakura.rose(' now ') +
|
|
70
|
+
sakura.dim('· ') +
|
|
71
|
+
sakura.blossom('building ') +
|
|
72
|
+
sakura.mint('and looking for an internship')
|
|
73
|
+
|
|
74
|
+
const footer =
|
|
75
|
+
'\n' +
|
|
76
|
+
sakura.dim('say hi: ') +
|
|
77
|
+
sakura.mint('yechancho') +
|
|
78
|
+
sakura.dim(' @ npm') +
|
|
79
|
+
' ' +
|
|
80
|
+
sakura.rose('🌸')
|
|
81
|
+
|
|
82
|
+
return [bio, line, linkLines, now, footer].join('\n')
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
|
|
85
|
+
const makeCard = () => {
|
|
81
86
|
const content = `${makeHeader()}\n\n${makeBody()}`
|
|
82
87
|
return boxen(content, {
|
|
83
88
|
padding: 1,
|
|
@@ -87,152 +92,55 @@ function makeCard() {
|
|
|
87
92
|
})
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const r1 = rand(i * 13.37)
|
|
104
|
-
const r2 = rand(i * 42.42)
|
|
105
|
-
const r3 = rand(i * 7.77)
|
|
106
|
-
petals.push({
|
|
107
|
-
x: Math.floor(r1 * width),
|
|
108
|
-
y: Math.floor(r2 * height),
|
|
109
|
-
vy: 0.22 + r3 * 0.75,
|
|
110
|
-
drift: (r2 - 0.5) * 0.45,
|
|
111
|
-
sway: 0.7 + r1 * 1.2,
|
|
112
|
-
ch: PETALS[i % PETALS.length],
|
|
113
|
-
})
|
|
95
|
+
// Simple falling petals animation overlay
|
|
96
|
+
function renderPetalsFrame(card, frame, width = 60, height = 12) {
|
|
97
|
+
const petals = ['🌸', '✿', '❀', '·']
|
|
98
|
+
const cols = width
|
|
99
|
+
const rows = height
|
|
100
|
+
|
|
101
|
+
// deterministic-ish positions
|
|
102
|
+
const points = []
|
|
103
|
+
for (let i = 0; i < 18; i++) {
|
|
104
|
+
const x = (i * 7 + frame * 3) % cols
|
|
105
|
+
const y = (i * 3 + frame) % rows
|
|
106
|
+
const ch = petals[(i + frame) % petals.length]
|
|
107
|
+
points.push({ x, y, ch })
|
|
114
108
|
}
|
|
115
|
-
return petals
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function renderOverlay(petals, t, width, height) {
|
|
119
|
-
const grid = Array.from({ length: height }, () => Array.from({ length: width }, () => ' '))
|
|
120
|
-
|
|
121
|
-
for (const p of petals) {
|
|
122
|
-
const sway = Math.sin((t / 240) * p.sway) * 1.2
|
|
123
|
-
const x = Math.round(p.x + sway)
|
|
124
|
-
const y = Math.round(p.y)
|
|
125
109
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
110
|
+
// Build overlay grid
|
|
111
|
+
const grid = Array.from({ length: rows }, () => Array.from({ length: cols }, () => ' '))
|
|
112
|
+
for (const p of points) grid[p.y][p.x] = p.ch
|
|
130
113
|
|
|
131
|
-
|
|
132
|
-
|
|
114
|
+
const overlay = grid.map((r) => r.join('')).join('\n')
|
|
115
|
+
const tinted = sakura.blossom(overlay)
|
|
133
116
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
p.y += p.vy
|
|
137
|
-
p.x += p.drift
|
|
138
|
-
|
|
139
|
-
if (p.y >= height) {
|
|
140
|
-
p.y = 0
|
|
141
|
-
p.x = Math.floor(Math.random() * width)
|
|
142
|
-
}
|
|
143
|
-
if (p.x < 0) p.x = width - 1
|
|
144
|
-
if (p.x >= width) p.x = 0
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Render without log-update: write to (0,0) and erase down. No scrolling.
|
|
149
|
-
function drawFrame(frame) {
|
|
150
|
-
process.stdout.write(ansiEscapes.cursorTo(0, 0) + ansiEscapes.eraseDown + frame)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function setupCleanup() {
|
|
154
|
-
const cleanup = () => {
|
|
155
|
-
try {
|
|
156
|
-
cliCursor.show()
|
|
157
|
-
process.stdout.write(ansiEscapes.cursorTo(0, 0) + ansiEscapes.eraseDown)
|
|
158
|
-
} catch {}
|
|
159
|
-
}
|
|
160
|
-
process.on('SIGINT', () => {
|
|
161
|
-
cleanup()
|
|
162
|
-
process.exit(0)
|
|
163
|
-
})
|
|
164
|
-
process.on('SIGTERM', () => {
|
|
165
|
-
cleanup()
|
|
166
|
-
process.exit(0)
|
|
167
|
-
})
|
|
168
|
-
process.on('exit', cleanup)
|
|
169
|
-
return cleanup
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
async function runIntroAnimation(card, durationMs, fps) {
|
|
173
|
-
const frameMs = Math.round(1000 / fps)
|
|
174
|
-
let petals = []
|
|
175
|
-
let overlayW = 60
|
|
176
|
-
let overlayH = 10
|
|
177
|
-
|
|
178
|
-
const computeSizes = () => {
|
|
179
|
-
const termW = process.stdout.columns ?? 80
|
|
180
|
-
const termH = process.stdout.rows ?? 24
|
|
181
|
-
|
|
182
|
-
// Important: keep overlay small enough so overlay + card fits without scrolling.
|
|
183
|
-
overlayW = clamp(termW - 2, 40, 110)
|
|
184
|
-
overlayH = clamp(Math.floor(termH * 0.35), 6, 14)
|
|
185
|
-
|
|
186
|
-
const petalCount = clamp(Math.floor(overlayW / 5), 10, 32)
|
|
187
|
-
petals = initPetals(petalCount, overlayW, overlayH)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
computeSizes()
|
|
191
|
-
|
|
192
|
-
// If user resizes terminal, recompute overlay
|
|
193
|
-
const onResize = () => computeSizes()
|
|
194
|
-
process.stdout.on('resize', onResize)
|
|
195
|
-
|
|
196
|
-
const start = Date.now()
|
|
197
|
-
let t = 0
|
|
198
|
-
|
|
199
|
-
while (Date.now() - start < durationMs) {
|
|
200
|
-
const overlay = renderOverlay(petals, t, overlayW, overlayH)
|
|
201
|
-
const frame = `${overlay}\n\n${card}`
|
|
202
|
-
drawFrame(frame)
|
|
203
|
-
|
|
204
|
-
stepPetals(petals, overlayW, overlayH)
|
|
205
|
-
t += frameMs
|
|
206
|
-
await sleep(frameMs)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
process.stdout.off('resize', onResize)
|
|
117
|
+
// Put overlay ABOVE the card
|
|
118
|
+
return tinted + '\n' + card
|
|
210
119
|
}
|
|
211
120
|
|
|
212
121
|
async function main() {
|
|
213
122
|
const card = makeCard()
|
|
214
123
|
|
|
215
|
-
|
|
124
|
+
// If user runs with --static, just print once.
|
|
125
|
+
if (process.argv.includes('--static')) {
|
|
216
126
|
console.log(card)
|
|
217
127
|
return
|
|
218
128
|
}
|
|
219
129
|
|
|
220
|
-
|
|
221
|
-
const durationArg = args.get('--duration')
|
|
222
|
-
const durationMs = durationArg === true ? 2500 : parseInt(durationArg ?? '2500', 10) || 2500
|
|
223
|
-
|
|
224
|
-
setupCleanup()
|
|
130
|
+
// Animated intro (brief)
|
|
225
131
|
cliCursor.hide()
|
|
226
|
-
|
|
227
|
-
// Clear once, then render frames from top-left
|
|
228
132
|
process.stdout.write(ansiEscapes.clearScreen)
|
|
229
133
|
|
|
230
|
-
|
|
134
|
+
const frames = 28
|
|
135
|
+
for (let f = 0; f < frames; f++) {
|
|
136
|
+
logUpdate(renderPetalsFrame(card, f))
|
|
137
|
+
await sleep(45)
|
|
138
|
+
}
|
|
231
139
|
|
|
232
|
-
|
|
233
|
-
process.stdout.write(ansiEscapes.cursorTo(0, 0) + ansiEscapes.eraseDown)
|
|
140
|
+
logUpdate.clear()
|
|
234
141
|
cliCursor.show()
|
|
235
142
|
console.log(card)
|
|
143
|
+
console.log(sakura.dim('Tip: run with ') + sakura.pink('--static') + sakura.dim(' to disable animation.'))
|
|
236
144
|
}
|
|
237
145
|
|
|
238
146
|
main().catch((e) => {
|