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.
Files changed (2) hide show
  1. package/index.js +85 -177
  2. 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
- const supportsLinks = terminalLink.isSupported
40
-
41
- const LINKS = [
42
- { label: 'LinkedIn', url: 'https://www.linkedin.com/in/yechancho5', icon: '🌸' },
43
- { label: 'GitHub', url: 'https://github.com/yechancho5', icon: '✿' },
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 NOW = sakura.soft('building ') + sakura.mint('and looking for an internship')
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
- function makeHeader() {
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
- function sectionTitle(t) {
61
- return sakura.rose('✿ ') + sakura.white.bold(t)
62
- }
63
-
64
- function makeBody() {
65
- const about =
66
- sakura.white("hi, i'm ") +
67
- sakura.pink.bold('yechan') +
68
- sakura.dim('') +
69
- sakura.soft('i like building useful things & hanging with friends.')
70
-
71
- const linkLines = LINKS.map(({ label, url, icon }) => {
72
- const left = sakura.pink(`${icon} ${label}:`).padEnd(14)
73
- const right = supportsLinks ? terminalLink(sakura.sky(url), url) : sakura.sky(url)
74
- return left + right
75
- }).join('\n')
76
-
77
- return [about, '', sectionTitle('links'), linkLines, '', sectionTitle('now'), NOW].join('\n')
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
- function makeCard() {
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
- /* ---------------- Intro Petal Animation (stable render) ---------------- */
91
-
92
- // Use mostly single-width glyphs to avoid emoji-width jitter in overlay
93
- const PETALS = ['✿', '❀', '·', '•', '*']
94
-
95
- function rand(seed) {
96
- const x = Math.sin(seed) * 10000
97
- return x - Math.floor(x)
98
- }
99
-
100
- function initPetals(count, width, height) {
101
- const petals = []
102
- for (let i = 0; i < count; i++) {
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
- if (y >= 0 && y < height && x >= 0 && x < width) {
127
- grid[y][x] = p.ch
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
- return sakura.soft(grid.map((r) => r.join('')).join('\n'))
132
- }
114
+ const overlay = grid.map((r) => r.join('')).join('\n')
115
+ const tinted = sakura.blossom(overlay)
133
116
 
134
- function stepPetals(petals, width, height) {
135
- for (const p of petals) {
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
- if (args.get('--static')) {
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
- const fps = clamp(parseInt(args.get('--fps') ?? '30', 10) || 30, 10, 60)
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
- await runIntroAnimation(card, durationMs, fps)
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
- // Final static render (clean)
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yechancho",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "yechancho's terminal card",
5
5
  "main": "index.js",
6
6
  "type": "module",