inup 1.5.4 → 1.5.5

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.
@@ -470,8 +470,18 @@ class InteractiveUI {
470
470
  }
471
471
  process.stdout.write(lines.map((line) => applyBackgroundToLine(line, bgCode)).join('\n'));
472
472
  };
473
+ const key = (text) => chalk_1.default.bold.white(text);
474
+ const hint = (text) => chalk_1.default.gray(text);
475
+ const sep = hint(' · ');
476
+ const SHORTCUTS = {
477
+ scroll: key('↑/↓ ') + hint('Scroll'),
478
+ version: key('←/→ ') + hint('Version'),
479
+ switchTab: key('Tab ') + hint('Switch tab'),
480
+ closeInfo: key('I / Esc ') + hint('Close'),
481
+ closeTheme: key('T / Esc ') + hint('Close'),
482
+ };
473
483
  const buildModalHeaderLines = (shortcutLabel) => [
474
- ' ' + chalk_1.default.bold.magenta('🚀 inup'),
484
+ ' ' + chalk_1.default.bold('🚀 ') + (0, themes_colors_1.coloredInupLogo)(),
475
485
  '',
476
486
  ' ' + shortcutLabel,
477
487
  '',
@@ -503,11 +513,24 @@ class InteractiveUI {
503
513
  process.off('SIGWINCH', handleResize);
504
514
  this.refreshView = undefined;
505
515
  };
516
+ // Safety net: restore terminal if the process exits without going through finalizeSelection.
517
+ // Only synchronous writes work in an 'exit' handler, but that's all we need here.
518
+ const emergencyCleanup = () => {
519
+ if (ownsAlternateScreen) {
520
+ process.stdout.write('\x1b[?1049l');
521
+ }
522
+ process.stdout.write('\x1b[?25h');
523
+ if (process.stdin.setRawMode) {
524
+ process.stdin.setRawMode(false);
525
+ }
526
+ };
527
+ process.on('exit', emergencyCleanup);
506
528
  const finalizeSelection = (selectedStates) => {
507
529
  isResolved = true;
508
530
  this.packageInfoModalController.cancel();
509
531
  releaseInteractiveScreen();
510
532
  cleanupInteractiveSession();
533
+ process.off('exit', emergencyCleanup);
511
534
  resolve(selectedStates);
512
535
  };
513
536
  const renderInterface = () => {
@@ -530,7 +553,7 @@ class InteractiveUI {
530
553
  const terminalHeight = this.getTerminalHeight();
531
554
  const themeManager = stateManager.getThemeManager();
532
555
  const modalLines = this.renderer.renderThemeSelectorModal(themeManager.getCurrentTheme(), themeManager.getPreviewTheme(), terminalWidth, terminalHeight);
533
- renderModalViewport('theme-modal', chalk_1.default.bold.white('T ') + chalk_1.default.gray('/ Esc Exit theme selector'), modalLines, terminalWidth, terminalHeight, bgCode);
556
+ renderModalViewport('theme-modal', SHORTCUTS.closeTheme, modalLines, terminalWidth, terminalHeight, bgCode);
534
557
  }
535
558
  else if (uiState.showDebugModal) {
536
559
  const terminalWidth = process.stdout.columns || 80;
@@ -539,10 +562,11 @@ class InteractiveUI {
539
562
  const result = (0, debug_1.renderPerformanceModal)(snapshot, terminalWidth, Math.max(8, terminalHeight - 4), uiState.debugModalScrollOffset);
540
563
  debugModalMaxScrollOffset = result.maxScrollOffset;
541
564
  stateManager.clampDebugModalScrollOffset(debugModalMaxScrollOffset);
542
- const scrollHint = result.usesInternalScroll && result.maxScrollOffset > 0
543
- ? chalk_1.default.bold.white('↑/↓ ') + chalk_1.default.gray('Scroll · ')
544
- : '';
545
- renderModalViewport('info-modal', scrollHint + chalk_1.default.bold.white('! / Esc ') + chalk_1.default.gray('Close'), result.lines, terminalWidth, terminalHeight, bgCode);
565
+ const debugHints = [
566
+ result.usesInternalScroll && result.maxScrollOffset > 0 ? SHORTCUTS.scroll : '',
567
+ SHORTCUTS.closeInfo,
568
+ ].filter(Boolean).join(sep);
569
+ renderModalViewport('info-modal', debugHints, result.lines, terminalWidth, terminalHeight, bgCode);
546
570
  }
547
571
  else if (uiState.showInfoModal &&
548
572
  uiState.infoModalRow >= 0 &&
@@ -554,7 +578,7 @@ class InteractiveUI {
554
578
  // Show loading state
555
579
  const result = this.renderer.renderPackageInfoLoading(selectedState, terminalWidth, Math.max(8, terminalHeight - 4));
556
580
  infoModalMaxScrollOffset = result.maxScrollOffset;
557
- renderModalViewport('info-modal', chalk_1.default.bold.white('I / Esc ') + chalk_1.default.gray('Exit this view'), result.lines, terminalWidth, terminalHeight, bgCode);
581
+ renderModalViewport('info-modal', SHORTCUTS.closeInfo, result.lines, terminalWidth, terminalHeight, bgCode);
558
582
  }
559
583
  else {
560
584
  // Show full info with scroll support
@@ -562,18 +586,13 @@ class InteractiveUI {
562
586
  const result = this.renderer.renderPackageInfoModal(selectedState, terminalWidth, Math.max(8, terminalHeight - 4), uiState.infoModalScrollOffset, activeTab);
563
587
  infoModalMaxScrollOffset = result.maxScrollOffset;
564
588
  stateManager.clampInfoModalScrollOffset(infoModalMaxScrollOffset);
565
- const scrollHint = result.usesInternalScroll && result.maxScrollOffset > 0
566
- ? chalk_1.default.bold.white('↑/↓ ') + chalk_1.default.gray('Scroll · ')
567
- : '';
568
- const versionHint = activeTab === 'info'
569
- ? chalk_1.default.bold.white('←/→ ') + chalk_1.default.gray('Version · ')
570
- : '';
571
- renderModalViewport('info-modal', scrollHint +
572
- versionHint +
573
- chalk_1.default.bold.white('Tab ') +
574
- chalk_1.default.gray('Switch tab · ') +
575
- chalk_1.default.bold.white('I / Esc ') +
576
- chalk_1.default.gray('Exit this view'), result.lines, terminalWidth, terminalHeight, bgCode);
589
+ const hints = [
590
+ result.usesInternalScroll && result.maxScrollOffset > 0 ? SHORTCUTS.scroll : '',
591
+ activeTab === 'info' ? SHORTCUTS.version : '',
592
+ SHORTCUTS.switchTab,
593
+ SHORTCUTS.closeInfo,
594
+ ].filter(Boolean).join(sep);
595
+ renderModalViewport('info-modal', hints, result.lines, terminalWidth, terminalHeight, bgCode);
577
596
  }
578
597
  }
579
598
  else {
@@ -631,6 +650,7 @@ class InteractiveUI {
631
650
  this.enqueueSecurityAudit(states);
632
651
  }
633
652
  catch (error) {
653
+ process.off('exit', emergencyCleanup);
634
654
  releaseInteractiveScreen();
635
655
  // Reset terminal colors
636
656
  process.stdout.write((0, themes_colors_1.getTerminalResetCode)());
@@ -651,7 +671,22 @@ class InteractiveUI {
651
671
  cleanupConfirmationSession();
652
672
  resolve(confirmed);
653
673
  };
654
- const inputHandler = new ui_1.ConfirmationInputHandler(handleConfirm);
674
+ // Safety net for the same reason as selectPackages — synchronous restore on exit.
675
+ // The confirmation screen does not enter the alternate screen, so alt-screen
676
+ // restoration is intentionally omitted here. If that ever changes, mirror the
677
+ // ownsAlternateScreen-gated pattern from selectPackages.
678
+ const confirmEmergencyCleanup = () => {
679
+ process.stdout.write('\x1b[?25h');
680
+ if (process.stdin.setRawMode) {
681
+ process.stdin.setRawMode(false);
682
+ }
683
+ };
684
+ process.on('exit', confirmEmergencyCleanup);
685
+ const handleConfirmWithCleanup = (confirmed) => {
686
+ handleConfirm(confirmed);
687
+ process.off('exit', confirmEmergencyCleanup);
688
+ };
689
+ const inputHandler = new ui_1.ConfirmationInputHandler(handleConfirmWithCleanup);
655
690
  const keypressHandler = (str, key) => inputHandler.handleKeypress(str, key);
656
691
  // Setup keypress handling
657
692
  try {
@@ -663,6 +698,7 @@ class InteractiveUI {
663
698
  ui_1.CursorUtils.hide();
664
699
  }
665
700
  catch (error) {
701
+ process.off('exit', confirmEmergencyCleanup);
666
702
  ui_1.TerminalInput.promptForConfirmation('Proceed with upgrade? [Y/n] ')
667
703
  .then(resolve)
668
704
  .catch(() => resolve(false));
@@ -15,7 +15,7 @@ class InputHandler {
15
15
  return;
16
16
  }
17
17
  if (key && key.ctrl && key.name === 'c') {
18
- utils_1.CursorUtils.show();
18
+ this.onCancel();
19
19
  process.exit(0);
20
20
  }
21
21
  const uiState = this.stateManager.getUIState();
@@ -267,7 +267,9 @@ class ConfirmationInputHandler {
267
267
  return;
268
268
  }
269
269
  if (key && key.ctrl && key.name === 'c') {
270
- utils_1.CursorUtils.show();
270
+ // onConfirm runs the normal cleanup path (cursor show, raw-mode off, listener
271
+ // removal). The 'exit' listener registered by confirmUpgrade is a final backstop.
272
+ this.onConfirm(false);
271
273
  process.exit(0);
272
274
  }
273
275
  if (!key) {
@@ -281,7 +281,8 @@ function buildUsedBySections(state, modalWidth) {
281
281
  ];
282
282
  }
283
283
  function buildPackageInfoSections(state, modalWidth, activeTab) {
284
- const title = chalk_1.default.cyan.bold(`Package: ${state.name}`) +
284
+ const title = chalk_1.default.bold('Package: ') +
285
+ (0, themes_colors_1.getThemeColor)('packageName')(state.name) +
285
286
  buildTabBarSuffix(activeTab, getUsedByPaths(state).length);
286
287
  const authorLicense = chalk_1.default.gray(`${state.author || 'Unknown'} • ${state.license || 'MIT'}`);
287
288
  const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
@@ -223,12 +223,10 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
223
223
  };
224
224
  const pmColor = colorMap[packageManager.name] || packageManager.color;
225
225
  // Each character in "inup" gets a different color
226
- const inupColors = [chalk_1.default.red, chalk_1.default.yellow, chalk_1.default.blue, chalk_1.default.magenta];
227
- const coloredInup = inupColors.map((color, i) => color.bold('inup'[i])).join('');
228
226
  const headerLine = ' ' +
229
227
  chalk_1.default.bold(pmColor('🚀')) +
230
228
  ' ' +
231
- coloredInup +
229
+ (0, themes_colors_1.coloredInupLogo)() +
232
230
  (0, themes_colors_1.getThemeColor)('textSecondary')(` (${packageManager.displayName})`);
233
231
  // Show filter state (always show, including "All")
234
232
  const fullHeaderLine = activeFilterLabel
@@ -241,12 +239,7 @@ function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forc
241
239
  output.push(fullHeaderLine + ' '.repeat(headerPadding));
242
240
  }
243
241
  else {
244
- const headerLine = ' ' +
245
- chalk_1.default.bold.blue('🚀 ') +
246
- chalk_1.default.bold.red('i') +
247
- chalk_1.default.bold.yellow('n') +
248
- chalk_1.default.bold.blue('u') +
249
- chalk_1.default.bold.magenta('p');
242
+ const headerLine = ' ' + chalk_1.default.bold.blue('🚀 ') + (0, themes_colors_1.coloredInupLogo)();
250
243
  // Show filter state (always show, including "All")
251
244
  const fullHeaderLine = activeFilterLabel
252
245
  ? headerLine +
@@ -8,6 +8,7 @@ exports.getThemeColor = getThemeColor;
8
8
  exports.getThemeBgColor = getThemeBgColor;
9
9
  exports.getTerminalBgColorCode = getTerminalBgColorCode;
10
10
  exports.getTerminalResetCode = getTerminalResetCode;
11
+ exports.coloredInupLogo = coloredInupLogo;
11
12
  const chalk_1 = __importDefault(require("chalk"));
12
13
  const theme_manager_1 = require("./state/theme-manager");
13
14
  // Centralized theme color definitions - single source of truth
@@ -193,6 +194,10 @@ function getTerminalBgColorCode() {
193
194
  function getTerminalResetCode() {
194
195
  return '\x1b[0m';
195
196
  }
197
+ const BRAND_COLORS = [chalk_1.default.red, chalk_1.default.yellow, chalk_1.default.blue, chalk_1.default.magenta];
198
+ function coloredInupLogo() {
199
+ return BRAND_COLORS.map((color, i) => color.bold('inup'[i])).join('');
200
+ }
196
201
  exports.themeColors = {
197
202
  primary: () => getThemeColor('primary'),
198
203
  secondary: () => getThemeColor('secondary'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inup",
3
- "version": "1.5.4",
3
+ "version": "1.5.5",
4
4
  "description": "Interactive dependency upgrader for npm, yarn, pnpm & bun. Zero-config, monorepo-ready. Upgrade-interactive for every package manager.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -43,20 +43,20 @@
43
43
  "README.md"
44
44
  ],
45
45
  "devDependencies": {
46
- "@types/node": "^24.12.2",
46
+ "@types/node": "^24.12.4",
47
47
  "@types/semver": "^7.7.1",
48
- "@vitest/coverage-v8": "^4.1.4",
49
- "prettier": "^3.8.2",
50
- "typescript": "^6.0.2",
51
- "vitest": "^4.1.4"
48
+ "@vitest/coverage-v8": "^4.1.6",
49
+ "prettier": "^3.8.3",
50
+ "typescript": "^6.0.3",
51
+ "vitest": "^4.1.6"
52
52
  },
53
53
  "dependencies": {
54
54
  "chalk": "^5.6.2",
55
55
  "commander": "^14.0.3",
56
56
  "env-paths": "^4.0.0",
57
57
  "nanospinner": "^1.2.2",
58
- "semver": "^7.7.4",
59
- "undici": "^8.1.0"
58
+ "semver": "^7.8.0",
59
+ "undici": "^8.3.0"
60
60
  },
61
61
  "engines": {
62
62
  "node": ">=20.0.0"