yechancho 1.0.4 → 1.0.6

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 +29 -119
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -9,12 +9,11 @@ import terminalLink from 'terminal-link'
9
9
  import logUpdate from 'log-update'
10
10
  import ansiEscapes from 'ansi-escapes'
11
11
  import cliCursor from 'cli-cursor'
12
- import readline from 'node:readline'
13
12
 
14
13
  /**
15
14
  * Flags:
16
15
  * --static no animation, show card only
17
- * --duration=2500 opening animation length in ms (default 2.5s, then overlay clears)
16
+ * --duration=2500 intro animation length in ms (default 2.5s)
18
17
  * --fps=30 animation fps (default 30)
19
18
  */
20
19
 
@@ -46,8 +45,7 @@ const LINKS = [
46
45
  { label: 'GitHub', url: 'https://github.com/yechancho5', icon: '✿' },
47
46
  ]
48
47
 
49
- const TECH = ['React', 'TypeScript', 'FastAPI', 'Postgres', 'AWS', 'RAG']
50
- const NOW = sakura.soft('building: ') + sakura.mint('cool products + shipping fast')
48
+ const NOW = sakura.soft('building ') + sakura.mint('and looking for an internship')
51
49
 
52
50
  function clamp(n, a, b) {
53
51
  return Math.max(a, Math.min(b, n))
@@ -61,36 +59,10 @@ function makeHeader() {
61
59
  return gradient(['#FFB7C5', '#FFD7E5', '#C7A2FF'])(text)
62
60
  }
63
61
 
64
- function makeBranchArt() {
65
- const cols = process.stdout.columns ?? 80
66
- if (cols < 70) return ''
67
-
68
- const art = [
69
- " .::' '::..",
70
- " .:' `:.",
71
- " :: _.._ ::",
72
- " `:..-' `-..:'",
73
- " \\ 🌸 /",
74
- " _.-'\\ /`-._",
75
- " .' \\__/ `.",
76
- " / ✿ .-..-. ❀ \\",
77
- " \\ ( ) /",
78
- " `-._ `-..-' _.-'",
79
- " `--. .--'",
80
- " \\/",
81
- ].join('\n')
82
-
83
- return gradient(['#FFD7E5', '#FFB7C5', '#C7A2FF'])(art)
84
- }
85
-
86
62
  function sectionTitle(t) {
87
63
  return sakura.rose('✿ ') + sakura.white.bold(t)
88
64
  }
89
65
 
90
- function pill(text) {
91
- return chalk.hex('#1A1A1A').bgHex('#FFD7E5')(` ${text} `)
92
- }
93
-
94
66
  function makeBody() {
95
67
  const about =
96
68
  sakura.white("hi, i'm ") +
@@ -104,27 +76,19 @@ function makeBody() {
104
76
  return left + right
105
77
  }).join('\n')
106
78
 
107
- const techLine = TECH.map((t) => pill(t)).join(' ')
108
-
109
79
  return [
110
80
  about,
111
81
  '',
112
82
  sectionTitle('links'),
113
83
  linkLines,
114
84
  '',
115
- sectionTitle('stack'),
116
- techLine,
117
- '',
118
85
  sectionTitle('now'),
119
86
  NOW,
120
- '',
121
- sakura.dim('run: ') + sakura.pink('npx @yechancho/terminal-card') + ' ' + sakura.rose('🌸'),
122
87
  ].join('\n')
123
88
  }
124
89
 
125
90
  function makeCard() {
126
- const branch = makeBranchArt()
127
- const content = `${makeHeader()}\n${branch ? branch + '\n' : ''}\n${makeBody()}`
91
+ const content = `${makeHeader()}\n\n${makeBody()}`
128
92
  return boxen(content, {
129
93
  padding: 1,
130
94
  margin: 1,
@@ -133,10 +97,11 @@ function makeCard() {
133
97
  })
134
98
  }
135
99
 
100
+ /* ---------------- Short Intro Petal Animation ---------------- */
101
+
136
102
  const PETALS = ['🌸', '❀', '✿', '·', '•']
137
103
 
138
104
  function rand(seed) {
139
- // tiny deterministic-ish PRNG
140
105
  const x = Math.sin(seed) * 10000
141
106
  return x - Math.floor(x)
142
107
  }
@@ -150,9 +115,9 @@ function initPetals(count, width, height) {
150
115
  petals.push({
151
116
  x: Math.floor(r1 * width),
152
117
  y: Math.floor(r2 * height),
153
- vy: 0.25 + r3 * 0.85, // fall speed
154
- drift: (r2 - 0.5) * 0.6, // horizontal drift
155
- sway: 0.6 + r1 * 1.2, // sway frequency
118
+ vy: 0.25 + r3 * 0.85,
119
+ drift: (r2 - 0.5) * 0.6,
120
+ sway: 0.6 + r1 * 1.2,
156
121
  ch: PETALS[i % PETALS.length],
157
122
  })
158
123
  }
@@ -181,7 +146,6 @@ function stepPetals(petals, width, height) {
181
146
  p.y += p.vy
182
147
  p.x += p.drift
183
148
 
184
- // wrap around
185
149
  if (p.y >= height) {
186
150
  p.y = 0
187
151
  p.x = Math.floor(Math.random() * width)
@@ -191,50 +155,28 @@ function stepPetals(petals, width, height) {
191
155
  }
192
156
  }
193
157
 
158
+ async function runIntroAnimation(card, durationMs, fps) {
159
+ const frameMs = Math.round(1000 / fps)
194
160
 
195
- function setupExitCleanup() {
196
- const cleanup = () => {
197
- try {
198
- cliCursor.show()
199
- logUpdate.clear()
200
- process.stdout.write(ansiEscapes.cursorShow)
201
- } catch {}
202
- }
203
-
204
- process.on('SIGINT', () => {
205
- cleanup()
206
- process.exit(0)
207
- })
208
- process.on('SIGTERM', () => {
209
- cleanup()
210
- process.exit(0)
211
- })
212
- process.on('exit', cleanup)
213
-
214
- return cleanup
215
- }
216
-
217
- async function waitForKeyOrDuration(durationMs) {
218
- return new Promise((resolve) => {
219
- const done = () => resolve()
161
+ const termW = process.stdout.columns ?? 80
162
+ const termH = process.stdout.rows ?? 24
220
163
 
221
- let timer = null
222
- if (Number.isFinite(durationMs) && durationMs > 0) {
223
- timer = setTimeout(done, durationMs)
224
- }
164
+ const overlayW = clamp(termW - 2, 40, 120)
165
+ const overlayH = clamp(Math.floor(termH / 2), 8, 18)
225
166
 
226
- readline.emitKeypressEvents(process.stdin)
227
- if (process.stdin.isTTY) process.stdin.setRawMode(true)
228
- process.stdin.resume()
167
+ const petalCount = clamp(Math.floor(overlayW / 4), 12, 40)
168
+ const petals = initPetals(petalCount, overlayW, overlayH)
229
169
 
230
- const onKey = () => {
231
- if (timer) clearTimeout(timer)
232
- process.stdin.off('keypress', onKey)
233
- done()
234
- }
170
+ const start = Date.now()
171
+ let t = 0
235
172
 
236
- process.stdin.on('keypress', onKey)
237
- })
173
+ while (Date.now() - start < durationMs) {
174
+ const overlay = renderOverlay(petals, t, overlayW, overlayH)
175
+ logUpdate(`${overlay}\n\n${card}`)
176
+ stepPetals(petals, overlayW, overlayH)
177
+ t += frameMs
178
+ await sleep(frameMs)
179
+ }
238
180
  }
239
181
 
240
182
  async function main() {
@@ -246,48 +188,16 @@ async function main() {
246
188
  }
247
189
 
248
190
  const fps = clamp(parseInt(args.get('--fps') ?? '30', 10) || 30, 10, 60)
249
- const frameMs = Math.round(1000 / fps)
250
-
251
- const cleanup = setupExitCleanup()
252
-
253
- cliCursor.hide()
254
- process.stdout.write(ansiEscapes.clearScreen)
255
-
256
- // Overlay size adapts to terminal
257
- const termW = process.stdout.columns ?? 80
258
- const termH = process.stdout.rows ?? 24
259
-
260
- const overlayW = clamp(termW - 2, 40, 120)
261
- const overlayH = clamp(Math.floor(termH / 2), 8, 18)
262
-
263
- // More petals on bigger screens
264
- const petalCount = clamp(Math.floor(overlayW / 4), 12, 40)
265
- const petals = initPetals(petalCount, overlayW, overlayH)
266
-
267
191
  const durationArg = args.get('--duration')
268
192
  const durationMs = durationArg === true ? 2500 : parseInt(durationArg ?? '2500', 10) || 2500
269
193
 
270
- let running = true
271
- const stopPromise = waitForKeyOrDuration(durationMs).then(() => (running = false))
272
-
273
- let t = 0
274
- while (running) {
275
- const overlay = renderOverlay(petals, t, overlayW, overlayH)
276
- const sparkleLine =
277
- sakura.rose('✿') +
278
- sakura.dim(' '.repeat(Math.max(0, overlayW - 4))) +
279
- sakura.rose('✿')
280
-
281
- logUpdate(`${overlay}\n${sparkleLine}\n${card}`)
282
- stepPetals(petals, overlayW, overlayH)
283
- t += frameMs
284
- await sleep(frameMs)
285
- }
194
+ cliCursor.hide()
195
+ process.stdout.write(ansiEscapes.clearScreen)
286
196
 
287
- await stopPromise
197
+ await runIntroAnimation(card, durationMs, fps)
288
198
 
289
199
  logUpdate.clear()
290
- cleanup()
200
+ cliCursor.show()
291
201
  console.log(card)
292
202
  }
293
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yechancho",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "yechancho's terminal card",
5
5
  "main": "index.js",
6
6
  "type": "module",