kandown 0.15.3 → 0.15.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.
package/bin/kandown.js CHANGED
@@ -199,15 +199,14 @@ async function checkForUpdate(argv = process.argv) {
199
199
 
200
200
  if (!latest || semverGt(current, latest) >= 0) return; // up to date or offline
201
201
 
202
- log('');
203
- log(`${c.yellow}⚡ Update available:${c.reset} kandown ${c.dim}${current}${c.reset} → ${c.green}${latest}${c.reset}`);
204
- info('Auto-updating…');
202
+ tuiDone('', `Update available: ${c.dim}kandown ${current}${c.reset} → ${c.green}${latest}${c.reset}`);
205
203
 
206
204
  // 📖 Step 2: Create lock file to prevent concurrent updates.
207
205
  try { writeFileSync(lockFile, `${process.pid}\n${now}`, 'utf8'); } catch { /* ignore */ }
208
206
 
209
- // 📖 Step 3: Run the update via npm or pnpm.
210
- // Try npm first, fall back to pnpm.
207
+ // 📖 Step 3: Run the update via npm or pnpm with animated progress.
208
+ tuiProgress(`Updating to ${latest}…`, 25);
209
+
211
210
  const updateOk = await new Promise((resolve) => {
212
211
  const tryInstall = (cmd) => {
213
212
  return new Promise((res) => {
@@ -233,7 +232,7 @@ async function checkForUpdate(argv = process.argv) {
233
232
  try { if (existsSync(lockFile)) unlinkSync(lockFile); } catch { /* ignore */ }
234
233
 
235
234
  if (!updateOk) {
236
- warn('Auto-update failed — continuing with current version');
235
+ tuiDone('✗', `${c.yellow}Auto-update failed${c.reset} — continuing with current version`);
237
236
  log(` Run ${c.cyan}npm install -g kandown${c.reset} to upgrade manually`);
238
237
  log('');
239
238
  return;
@@ -257,14 +256,13 @@ async function checkForUpdate(argv = process.argv) {
257
256
  });
258
257
 
259
258
  if (!postVersion || semverGt(postVersion, latest) < 0) {
260
- // Install claimed success but version didn't change probably a permissions issue.
261
- warn('Update did not apply — continuing with current version');
259
+ tuiDone('✗', `${c.yellow}Update did not apply${c.reset}continuing with current version`);
262
260
  log(` Run ${c.cyan}npm install -g kandown${c.reset} to upgrade manually`);
263
261
  log('');
264
262
  return;
265
263
  }
266
264
 
267
- success(`Updated to v${postVersion} — restarting…`);
265
+ tuiDone('✓', `${c.green}Updated to v${postVersion}${c.reset} — restarting…`);
268
266
  log('');
269
267
 
270
268
  // 📖 Step 5: Respawn with the new version.
@@ -309,6 +307,64 @@ const info = (msg) => log(`${c.cyan}→${c.reset} ${msg}`);
309
307
  const warn = (msg) => log(`${c.yellow}⚠${c.reset} ${msg}`);
310
308
  const err = (msg) => log(`${c.red}✗${c.reset} ${msg}`);
311
309
 
310
+ // ─── TUI spinner & progress bar ─────────────────────────────────────────────
311
+ // 📖 Lightweight terminal animation helpers for the auto-updater.
312
+ // Uses ANSI escape codes (carriage return + clear line) to redraw in-place.
313
+ // Only animate when stdout is a TTY; otherwise fall back to plain text.
314
+
315
+ const _SP_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
316
+ let _spTimer = null;
317
+ let _spIdx = 0;
318
+ const _isTTY = process.stdout.isTTY;
319
+
320
+ function _tuiClear() {
321
+ process.stdout.write('\r\x1b[K');
322
+ }
323
+
324
+ /** Start an animated spinner with the given text. */
325
+ function tuiSpinner(text) {
326
+ if (_spTimer) clearInterval(_spTimer);
327
+ _spIdx = 0;
328
+ _tuiClear();
329
+ if (!_isTTY) { process.stdout.write(` ${text}\n`); return; }
330
+ _spTimer = setInterval(() => {
331
+ process.stdout.write(`\r\x1b[K${_SP_FRAMES[_spIdx % _SP_FRAMES.length]} ${text}`);
332
+ _spIdx++;
333
+ }, 100);
334
+ }
335
+
336
+ /**
337
+ * 📖 Start a time-estimated progress bar + spinner.
338
+ * Shows a filling bar with percentage based on elapsed time.
339
+ * Caps at 95% until the caller calls tuiDone().
340
+ */
341
+ function tuiProgress(text, estimateSec = 25, barWidth = 15) {
342
+ if (_spTimer) clearInterval(_spTimer);
343
+ _spIdx = 0;
344
+ _tuiClear();
345
+ if (!_isTTY) { process.stdout.write(` ${text}\n`); return; }
346
+ const start = Date.now();
347
+ _spTimer = setInterval(() => {
348
+ const elapsed = (Date.now() - start) / 1000;
349
+ const pct = Math.min(Math.round((elapsed / estimateSec) * 100), 95);
350
+ const filled = Math.round(barWidth * pct / 100);
351
+ const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
352
+ process.stdout.write(`\r\x1b[K${_SP_FRAMES[_spIdx % _SP_FRAMES.length]} ${text} ${bar} ${pct}%`);
353
+ _spIdx++;
354
+ }, 120);
355
+ }
356
+
357
+ /** Stop the current animation and print a final status line. */
358
+ function tuiDone(symbol, text) {
359
+ if (_spTimer) { clearInterval(_spTimer); _spTimer = null; }
360
+ if (_isTTY) {
361
+ _tuiClear();
362
+ process.stdout.write(`${symbol} ${text}\n`);
363
+ } else {
364
+ log(`${symbol} ${text}`);
365
+ }
366
+ }
367
+
312
368
  function help() {
313
369
  const v = getCurrentVersion() ?? '?';
314
370
  log(`