inup 1.4.10 → 1.5.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 (50) hide show
  1. package/README.md +1 -7
  2. package/dist/cli.js +2 -1
  3. package/dist/config/constants.js +1 -2
  4. package/dist/config/project-config.js +6 -0
  5. package/dist/core/package-detector.js +163 -89
  6. package/dist/core/upgrade-runner.js +68 -16
  7. package/dist/features/changelog/clients/github-client.js +134 -0
  8. package/dist/features/changelog/clients/npm-registry-client.js +53 -0
  9. package/dist/features/changelog/index.js +19 -0
  10. package/dist/features/changelog/parsers/changelog-parser.js +68 -0
  11. package/dist/features/changelog/parsers/github-release-html-parser.js +61 -0
  12. package/dist/features/changelog/parsers/package-metadata.js +34 -0
  13. package/dist/features/changelog/parsers/repository-ref.js +26 -0
  14. package/dist/features/changelog/services/changelog-service.js +30 -0
  15. package/dist/features/changelog/services/package-metadata-service.js +108 -0
  16. package/dist/features/changelog/services/release-notes-service.js +180 -0
  17. package/dist/features/changelog/types/changelog.types.js +3 -0
  18. package/dist/interactive-ui.js +343 -161
  19. package/dist/services/background-audit.js +60 -0
  20. package/dist/services/index.js +3 -3
  21. package/dist/services/jsdelivr-registry.js +92 -176
  22. package/dist/services/npm-registry.js +97 -27
  23. package/dist/services/vulnerability-checker.js +133 -0
  24. package/dist/ui/controllers/index.js +8 -0
  25. package/dist/ui/controllers/package-info-modal-controller.js +237 -0
  26. package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
  27. package/dist/ui/index.js +3 -0
  28. package/dist/ui/input-handler.js +41 -10
  29. package/dist/ui/modal/index.js +22 -0
  30. package/dist/ui/modal/layout.js +84 -0
  31. package/dist/ui/modal/package-info-sections.js +327 -0
  32. package/dist/ui/modal/package-info.js +147 -0
  33. package/dist/ui/modal/theme-selector.js +46 -0
  34. package/dist/ui/modal/types.js +3 -0
  35. package/dist/ui/presenters/index.js +11 -0
  36. package/dist/ui/presenters/vulnerability.js +76 -0
  37. package/dist/ui/renderer/index.js +9 -11
  38. package/dist/ui/renderer/package-list.js +166 -66
  39. package/dist/ui/state/filter-manager.js +17 -2
  40. package/dist/ui/state/modal-manager.js +48 -6
  41. package/dist/ui/state/state-manager.js +49 -12
  42. package/dist/ui/utils/cursor.js +18 -0
  43. package/dist/ui/utils/index.js +8 -1
  44. package/dist/ui/utils/terminal-input.js +82 -0
  45. package/dist/ui/utils/text.js +75 -0
  46. package/dist/ui/utils/version.js +3 -2
  47. package/package.json +7 -11
  48. package/dist/services/changelog-fetcher.js +0 -190
  49. package/dist/ui/renderer/modal.js +0 -190
  50. package/dist/ui/renderer/theme-selector.js +0 -83
@@ -5,6 +5,18 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ConsoleUtils = exports.CursorUtils = void 0;
7
7
  exports.CursorUtils = {
8
+ /**
9
+ * Switch to the terminal alternate screen buffer.
10
+ */
11
+ enterAlternateScreen() {
12
+ process.stdout.write('\x1b[?1049h');
13
+ },
14
+ /**
15
+ * Return to the terminal primary screen buffer.
16
+ */
17
+ exitAlternateScreen() {
18
+ process.stdout.write('\x1b[?1049l');
19
+ },
8
20
  /**
9
21
  * Hide the cursor in the terminal
10
22
  */
@@ -23,6 +35,12 @@ exports.CursorUtils = {
23
35
  moveToHome() {
24
36
  process.stdout.write('\x1b[H');
25
37
  },
38
+ /**
39
+ * Clear the full screen and move the cursor to the top-left corner.
40
+ */
41
+ clearScreen() {
42
+ process.stdout.write('\x1b[2J\x1b[H');
43
+ },
26
44
  /**
27
45
  * Clear display from cursor to end of screen
28
46
  */
@@ -1,9 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConsoleUtils = exports.CursorUtils = exports.VersionUtils = void 0;
3
+ exports.wrapPlainText = exports.truncatePlainText = exports.stripAnsi = exports.getVisualLength = exports.TerminalInput = exports.ConsoleUtils = exports.CursorUtils = exports.VersionUtils = void 0;
4
4
  var version_1 = require("./version");
5
5
  Object.defineProperty(exports, "VersionUtils", { enumerable: true, get: function () { return version_1.VersionUtils; } });
6
6
  var cursor_1 = require("./cursor");
7
7
  Object.defineProperty(exports, "CursorUtils", { enumerable: true, get: function () { return cursor_1.CursorUtils; } });
8
8
  Object.defineProperty(exports, "ConsoleUtils", { enumerable: true, get: function () { return cursor_1.ConsoleUtils; } });
9
+ var terminal_input_1 = require("./terminal-input");
10
+ Object.defineProperty(exports, "TerminalInput", { enumerable: true, get: function () { return terminal_input_1.TerminalInput; } });
11
+ var text_1 = require("./text");
12
+ Object.defineProperty(exports, "getVisualLength", { enumerable: true, get: function () { return text_1.getVisualLength; } });
13
+ Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return text_1.stripAnsi; } });
14
+ Object.defineProperty(exports, "truncatePlainText", { enumerable: true, get: function () { return text_1.truncatePlainText; } });
15
+ Object.defineProperty(exports, "wrapPlainText", { enumerable: true, get: function () { return text_1.wrapPlainText; } });
9
16
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TerminalInput = void 0;
37
+ const readline = __importStar(require("node:readline"));
38
+ const ESCAPE_CODE_TIMEOUT_MS = 25;
39
+ exports.TerminalInput = {
40
+ startKeypressSession(onKeypress) {
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ escapeCodeTimeout: ESCAPE_CODE_TIMEOUT_MS,
44
+ terminal: true,
45
+ });
46
+ readline.emitKeypressEvents(process.stdin, rl);
47
+ if (process.stdin.setRawMode) {
48
+ process.stdin.setRawMode(true);
49
+ }
50
+ process.stdin.resume();
51
+ process.stdin.on('keypress', onKeypress);
52
+ return {
53
+ close: () => {
54
+ process.stdin.off('keypress', onKeypress);
55
+ rl.close();
56
+ if (process.stdin.setRawMode) {
57
+ process.stdin.setRawMode(false);
58
+ }
59
+ process.stdin.pause();
60
+ },
61
+ };
62
+ },
63
+ promptForConfirmation(prompt) {
64
+ return new Promise((resolve) => {
65
+ const rl = readline.createInterface({
66
+ input: process.stdin,
67
+ output: process.stdout,
68
+ terminal: true,
69
+ });
70
+ const finish = (value) => {
71
+ rl.close();
72
+ resolve(value);
73
+ };
74
+ rl.question(prompt, (answer) => {
75
+ const normalizedAnswer = answer.trim().toLowerCase();
76
+ finish(normalizedAnswer === '' || normalizedAnswer === 'y' || normalizedAnswer === 'yes');
77
+ });
78
+ rl.on('SIGINT', () => finish(false));
79
+ });
80
+ },
81
+ };
82
+ //# sourceMappingURL=terminal-input.js.map
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stripAnsi = stripAnsi;
4
+ exports.getVisualLength = getVisualLength;
5
+ exports.truncatePlainText = truncatePlainText;
6
+ exports.wrapPlainText = wrapPlainText;
7
+ const version_1 = require("./version");
8
+ function stripAnsi(text) {
9
+ return version_1.VersionUtils.stripAnsi(text);
10
+ }
11
+ function getVisualLength(text) {
12
+ const cleaned = stripAnsi(text);
13
+ const SegmenterCtor = Intl.Segmenter;
14
+ let length = 0;
15
+ const segments = SegmenterCtor
16
+ ? SegmenterCtor.prototype.segment.call(new SegmenterCtor(undefined, { granularity: 'grapheme' }), cleaned)
17
+ : cleaned;
18
+ for (const item of segments) {
19
+ const segment = typeof item === 'string' ? item : item.segment;
20
+ if (/\p{Extended_Pictographic}/u.test(segment) || segment.includes('\uFE0F')) {
21
+ length += 2;
22
+ }
23
+ else {
24
+ for (const char of segment) {
25
+ const codePoint = char.codePointAt(0) ?? 0;
26
+ if ((codePoint >= 0xfe00 && codePoint <= 0xfe0f) ||
27
+ (codePoint >= 0x0300 && codePoint <= 0x036f)) {
28
+ continue;
29
+ }
30
+ length += 1;
31
+ }
32
+ }
33
+ }
34
+ return length;
35
+ }
36
+ function truncatePlainText(text, maxWidth) {
37
+ if (maxWidth <= 0) {
38
+ return '';
39
+ }
40
+ if (getVisualLength(text) <= maxWidth) {
41
+ return text;
42
+ }
43
+ if (maxWidth <= 3) {
44
+ return '.'.repeat(maxWidth);
45
+ }
46
+ return text.substring(0, maxWidth - 3) + '...';
47
+ }
48
+ function wrapPlainText(text, maxWidth) {
49
+ if (!text) {
50
+ return [];
51
+ }
52
+ if (maxWidth <= 0 || getVisualLength(text) <= maxWidth) {
53
+ return [text];
54
+ }
55
+ const lines = [];
56
+ let current = '';
57
+ const words = text.split(' ');
58
+ for (const word of words) {
59
+ const candidate = (current + ' ' + word).trim();
60
+ if (getVisualLength(candidate) > maxWidth) {
61
+ if (current) {
62
+ lines.push(current);
63
+ }
64
+ current = word;
65
+ }
66
+ else {
67
+ current = current ? `${current} ${word}` : word;
68
+ }
69
+ }
70
+ if (current) {
71
+ lines.push(current);
72
+ }
73
+ return lines;
74
+ }
75
+ //# sourceMappingURL=text.js.map
@@ -8,7 +8,8 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  /**
9
9
  * ANSI escape code pattern for stripping terminal colors
10
10
  */
11
- const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
11
+ const ANSI_PATTERN = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
12
+ const OSC8_PATTERN = /\u001b]8;;.*?(?:\u0007|\u001b\\)|\u001b]8;;(?:\u0007|\u001b\\)/g;
12
13
  class VersionUtils {
13
14
  static applyVersionPrefix(originalSpecifier, targetVersion) {
14
15
  // Extract prefix from original specifier (^ or ~)
@@ -21,7 +22,7 @@ class VersionUtils {
21
22
  * Strip ANSI escape codes from a string
22
23
  */
23
24
  static stripAnsi(str) {
24
- return str.replace(ANSI_PATTERN, '');
25
+ return str.replace(OSC8_PATTERN, '').replace(ANSI_PATTERN, '');
25
26
  }
26
27
  /**
27
28
  * Get the visual length of a string (excluding ANSI codes)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inup",
3
- "version": "1.4.10",
3
+ "version": "1.5.0",
4
4
  "description": "Interactive CLI tool for upgrading dependencies with ease. Auto-detects and works with npm, yarn, pnpm, and bun. Inspired by yarn upgrade-interactive. Supports monorepos, workspaces, and batch upgrades.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -36,24 +36,20 @@
36
36
  "README.md"
37
37
  ],
38
38
  "devDependencies": {
39
- "@types/inquirer": "^9.0.9",
40
- "@types/keypress.js": "^2.1.3",
41
- "@types/node": "^24.12.0",
39
+ "@types/node": "^24.12.2",
42
40
  "@types/semver": "^7.7.1",
43
- "@vitest/coverage-v8": "^4.1.0",
44
- "prettier": "^3.8.1",
45
- "typescript": "^5.9.3",
46
- "vitest": "^4.1.0"
41
+ "@vitest/coverage-v8": "^4.1.4",
42
+ "prettier": "^3.8.2",
43
+ "typescript": "^6.0.2",
44
+ "vitest": "^4.1.4"
47
45
  },
48
46
  "dependencies": {
49
47
  "chalk": "^5.6.2",
50
48
  "commander": "^14.0.3",
51
49
  "env-paths": "^4.0.0",
52
- "inquirer": "^13.3.2",
53
- "keypress": "^0.2.1",
54
50
  "nanospinner": "^1.2.2",
55
51
  "semver": "^7.7.4",
56
- "undici": "^7.24.4"
52
+ "undici": "^8.1.0"
57
53
  },
58
54
  "engines": {
59
55
  "node": ">=20.0.0"
@@ -1,190 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.changelogFetcher = exports.ChangelogFetcher = void 0;
4
- const constants_1 = require("../config/constants");
5
- /**
6
- * Fetches package metadata from npm registry
7
- * Includes description, repository info, and basic metadata
8
- */
9
- class ChangelogFetcher {
10
- constructor() {
11
- this.cache = new Map();
12
- this.failureCache = new Set(); // Track packages that failed to fetch
13
- }
14
- /**
15
- * Fetch package metadata from npm registry
16
- * Uses a cached approach to avoid repeated requests
17
- */
18
- async fetchPackageMetadata(packageName) {
19
- // Check if we already have this in cache
20
- if (this.cache.has(packageName)) {
21
- return this.cache.get(packageName);
22
- }
23
- // Check if we already failed to fetch this
24
- if (this.failureCache.has(packageName)) {
25
- return null;
26
- }
27
- try {
28
- // Fetch from npm registry
29
- const response = await this.fetchFromRegistry(packageName);
30
- if (!response) {
31
- this.failureCache.add(packageName);
32
- return null;
33
- }
34
- const repositoryUrl = this.extractRepositoryUrl(response.repository?.url || '');
35
- const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
36
- const issuesUrl = repositoryUrl ? `${repositoryUrl}/issues` : undefined;
37
- const metadata = {
38
- description: response.description || 'No description available',
39
- homepage: response.homepage,
40
- repository: response.repository,
41
- bugs: response.bugs,
42
- keywords: response.keywords || [],
43
- author: response.author?.name || response.author,
44
- license: response.license,
45
- repositoryUrl,
46
- npmUrl,
47
- issuesUrl,
48
- };
49
- // Try to extract release notes/changelog info
50
- if (repositoryUrl) {
51
- metadata.releaseNotes = `${repositoryUrl}/releases`;
52
- }
53
- // Try to get weekly download count
54
- try {
55
- const downloadsData = await this.fetchDownloadStats(packageName);
56
- if (downloadsData) {
57
- metadata.weeklyDownloads = downloadsData.downloads;
58
- }
59
- }
60
- catch {
61
- // Ignore download stats errors - optional data
62
- }
63
- this.cache.set(packageName, metadata);
64
- return metadata;
65
- }
66
- catch (error) {
67
- // Cache the failure to avoid retrying
68
- this.failureCache.add(packageName);
69
- return null;
70
- }
71
- }
72
- /**
73
- * Fetch data from jsdelivr CDN
74
- * Returns the package data by fetching package.json directly from jsdelivr
75
- */
76
- async fetchFromRegistry(packageName) {
77
- try {
78
- // Fetch package.json directly from jsdelivr CDN (resolves to latest automatically)
79
- const response = await fetch(`${constants_1.JSDELIVR_CDN_URL}/${encodeURIComponent(packageName)}@latest/package.json`, {
80
- method: 'GET',
81
- headers: {
82
- accept: 'application/json',
83
- },
84
- });
85
- if (!response.ok) {
86
- return null;
87
- }
88
- const pkgData = (await response.json());
89
- return {
90
- description: pkgData.description,
91
- homepage: pkgData.homepage,
92
- repository: pkgData.repository,
93
- bugs: pkgData.bugs,
94
- keywords: (pkgData.keywords || []),
95
- author: pkgData.author,
96
- license: pkgData.license,
97
- };
98
- }
99
- catch {
100
- return null;
101
- }
102
- }
103
- /**
104
- * Extract GitHub URL from repository URL for easier access to releases
105
- */
106
- extractRepositoryUrl(repoUrl) {
107
- if (!repoUrl)
108
- return '';
109
- // Handle various repository URL formats
110
- // git+https://github.com/user/repo.git -> https://github.com/user/repo/releases
111
- // https://github.com/user/repo.git -> https://github.com/user/repo/releases
112
- // github:user/repo -> https://github.com/user/repo/releases
113
- let cleanUrl = repoUrl
114
- .replace(/^git\+/, '') // Remove git+ prefix
115
- .replace(/\.git$/, '') // Remove .git suffix
116
- .replace(/^github:/, 'https://github.com/'); // Convert github: format
117
- // Ensure it's a proper URL
118
- if (!cleanUrl.startsWith('http')) {
119
- cleanUrl = 'https://github.com/' + cleanUrl;
120
- }
121
- return cleanUrl;
122
- }
123
- /**
124
- * Fetch weekly download statistics from npm
125
- */
126
- async fetchDownloadStats(packageName) {
127
- try {
128
- const response = await fetch(`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(packageName)}`, {
129
- method: 'GET',
130
- headers: {
131
- accept: 'application/json',
132
- },
133
- });
134
- if (!response.ok) {
135
- return null;
136
- }
137
- const data = (await response.json());
138
- return {
139
- downloads: data.downloads || 0,
140
- };
141
- }
142
- catch {
143
- return null;
144
- }
145
- }
146
- /**
147
- * Get repository release URL for a package
148
- */
149
- getRepositoryReleaseUrl(packageName, version) {
150
- const metadata = this.cache.get(packageName);
151
- if (!metadata || !metadata.releaseNotes) {
152
- return null;
153
- }
154
- return `${metadata.releaseNotes}/releases/tag/v${version}`;
155
- }
156
- /**
157
- * Cache package metadata directly (used by utils to avoid duplicate fetches)
158
- */
159
- cacheMetadata(packageName, rawData) {
160
- const repositoryUrl = this.extractRepositoryUrl(rawData.repository?.url || '');
161
- const npmUrl = `https://www.npmjs.com/package/${encodeURIComponent(packageName)}`;
162
- const issuesUrl = repositoryUrl ? `${repositoryUrl}/issues` : undefined;
163
- const metadata = {
164
- description: rawData.description || 'No description available',
165
- homepage: rawData.homepage,
166
- repository: rawData.repository,
167
- bugs: rawData.bugs,
168
- keywords: rawData.keywords || [],
169
- author: rawData.author?.name || rawData.author,
170
- license: rawData.license,
171
- repositoryUrl,
172
- npmUrl,
173
- issuesUrl,
174
- };
175
- if (repositoryUrl) {
176
- metadata.releaseNotes = `${repositoryUrl}/releases`;
177
- }
178
- this.cache.set(packageName, metadata);
179
- }
180
- /**
181
- * Clear the cache (useful for testing)
182
- */
183
- clearCache() {
184
- this.cache.clear();
185
- this.failureCache.clear();
186
- }
187
- }
188
- exports.ChangelogFetcher = ChangelogFetcher;
189
- exports.changelogFetcher = new ChangelogFetcher();
190
- //# sourceMappingURL=changelog-fetcher.js.map
@@ -1,190 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderPackageInfoLoading = renderPackageInfoLoading;
7
- exports.renderPackageInfoModal = renderPackageInfoModal;
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const themes_colors_1 = require("../themes-colors");
10
- const utils_1 = require("../utils");
11
- // Use shared ANSI stripping utility
12
- const stripAnsi = utils_1.VersionUtils.stripAnsi;
13
- /**
14
- * Format a number for display (e.g., 1000000 -> "1M", 1000 -> "1K")
15
- */
16
- function formatNumber(num) {
17
- if (!num)
18
- return 'N/A';
19
- if (num >= 1000000)
20
- return (num / 1000000).toFixed(1) + 'M';
21
- if (num >= 1000)
22
- return (num / 1000).toFixed(1) + 'K';
23
- return num.toString();
24
- }
25
- /**
26
- * Wrap text to fit within max width
27
- */
28
- function wrapText(text, maxWidth) {
29
- if (text.length <= maxWidth) {
30
- return [text];
31
- }
32
- const lines = [];
33
- let current = '';
34
- const words = text.split(' ');
35
- for (const word of words) {
36
- if ((current + ' ' + word).length > maxWidth) {
37
- if (current)
38
- lines.push(current);
39
- current = word;
40
- }
41
- else {
42
- current = current ? current + ' ' + word : word;
43
- }
44
- }
45
- if (current)
46
- lines.push(current);
47
- return lines;
48
- }
49
- /**
50
- * Render a loading state for the info modal
51
- */
52
- function renderPackageInfoLoading(state, terminalWidth = 80, terminalHeight = 24) {
53
- const modalWidth = Math.min(terminalWidth - 6, 120);
54
- const padding = Math.floor((terminalWidth - modalWidth) / 2);
55
- const lines = [];
56
- // Top padding to center vertically
57
- const topPadding = Math.max(1, Math.floor((terminalHeight - 10) / 2));
58
- for (let i = 0; i < topPadding; i++) {
59
- lines.push('');
60
- }
61
- // Modal border
62
- lines.push(' '.repeat(padding) + chalk_1.default.gray('╭' + '─'.repeat(modalWidth - 2) + '╮'));
63
- // Loading message
64
- const loadingMsg = '⏳ Loading package info...';
65
- const msgPadding = modalWidth - 4 - stripAnsi(loadingMsg).length;
66
- lines.push(' '.repeat(padding) +
67
- chalk_1.default.gray('│') +
68
- ' ' +
69
- chalk_1.default.cyan(loadingMsg) +
70
- ' '.repeat(Math.max(0, msgPadding)) +
71
- chalk_1.default.gray('│'));
72
- // Package name
73
- const nameMsg = `${state.name}`;
74
- const namePadding = modalWidth - 4 - nameMsg.length;
75
- lines.push(' '.repeat(padding) +
76
- chalk_1.default.gray('│') +
77
- ' ' +
78
- chalk_1.default.white(nameMsg) +
79
- ' '.repeat(Math.max(0, namePadding)) +
80
- chalk_1.default.gray('│'));
81
- lines.push(' '.repeat(padding) + chalk_1.default.gray('╰' + '─'.repeat(modalWidth - 2) + '╯'));
82
- return lines;
83
- }
84
- /**
85
- * Render a full-screen modal overlay showing package information
86
- * Similar to Turbo's help menu - centered with disabled background
87
- */
88
- function renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24) {
89
- const modalWidth = Math.min(terminalWidth - 6, 120); // Leave margins
90
- const padding = Math.floor((terminalWidth - modalWidth) / 2);
91
- const lines = [];
92
- // Top padding to center vertically
93
- const topPadding = Math.max(1, Math.floor((terminalHeight - 20) / 2));
94
- for (let i = 0; i < topPadding; i++) {
95
- lines.push('');
96
- }
97
- // Modal border and header
98
- lines.push(' '.repeat(padding) + chalk_1.default.gray('╭' + '─'.repeat(modalWidth - 2) + '╮'));
99
- // Title with package name
100
- const title = ` ℹ️ ${state.name}`;
101
- const titleLength = stripAnsi(title).length;
102
- const titlePadding = Math.max(0, modalWidth - 2 - titleLength);
103
- lines.push(' '.repeat(padding) +
104
- chalk_1.default.gray('│') +
105
- chalk_1.default.cyan.bold(title) +
106
- ' '.repeat(titlePadding) +
107
- chalk_1.default.gray('│'));
108
- // License and author line
109
- const authorLicense = `${state.author || 'Unknown'} • ${state.license || 'MIT'}`;
110
- const authorLength = authorLicense.length;
111
- const authorPadding = Math.max(0, modalWidth - 3 - authorLength);
112
- lines.push(' '.repeat(padding) +
113
- chalk_1.default.gray('│') +
114
- ' ' +
115
- chalk_1.default.gray(authorLicense) +
116
- ' '.repeat(authorPadding) +
117
- chalk_1.default.gray('│'));
118
- lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
119
- // Current and target versions
120
- const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
121
- const targetVersion = chalk_1.default.green(state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion);
122
- const versionText = `Current: ${currentVersion} → Target: ${targetVersion}`;
123
- const versionLength = stripAnsi(versionText).length;
124
- const versionPadding = Math.max(0, modalWidth - 3 - versionLength);
125
- lines.push(' '.repeat(padding) +
126
- chalk_1.default.gray('│') +
127
- ' ' +
128
- versionText +
129
- ' '.repeat(versionPadding) +
130
- chalk_1.default.gray('│'));
131
- // Weekly downloads
132
- if (state.weeklyDownloads !== undefined) {
133
- const downloadsText = `📊 ${formatNumber(state.weeklyDownloads)} downloads/week`;
134
- const downloadsLength = stripAnsi(downloadsText).length;
135
- const downloadsPadding = Math.max(0, modalWidth - 3 - downloadsLength);
136
- lines.push(' '.repeat(padding) +
137
- chalk_1.default.gray('│') +
138
- ' ' +
139
- (0, themes_colors_1.getThemeColor)('primary')(downloadsText) +
140
- ' '.repeat(downloadsPadding) +
141
- chalk_1.default.gray('│'));
142
- }
143
- // Description
144
- if (state.description) {
145
- lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
146
- const descriptionLines = wrapText(state.description, modalWidth - 4);
147
- for (const descLine of descriptionLines) {
148
- const descLength = descLine.length;
149
- const descPadding = Math.max(0, modalWidth - 3 - descLength);
150
- lines.push(' '.repeat(padding) +
151
- chalk_1.default.gray('│') +
152
- ' ' +
153
- chalk_1.default.white(descLine) +
154
- ' '.repeat(descPadding) +
155
- chalk_1.default.gray('│'));
156
- }
157
- }
158
- // Changelog/Releases section (moved to middle)
159
- if (state.repository) {
160
- lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
161
- const repoLabel = 'Changelog:';
162
- const repoUrl = state.repository.substring(0, modalWidth - 20);
163
- const repoText = ` ${repoLabel} ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')(repoUrl))}`;
164
- const repoLength = stripAnsi(repoText).length;
165
- const repoPadding = Math.max(0, modalWidth - 3 - repoLength);
166
- lines.push(' '.repeat(padding) +
167
- chalk_1.default.gray('│') +
168
- repoText +
169
- ' '.repeat(repoPadding) +
170
- chalk_1.default.gray('│'));
171
- }
172
- // Links section
173
- if (state.homepage) {
174
- lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
175
- const homeLabel = 'Homepage:';
176
- const homeUrl = state.homepage.substring(0, modalWidth - 20);
177
- const homeText = ` ${homeLabel} ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')(homeUrl))}`;
178
- const homeLength = stripAnsi(homeText).length;
179
- const homePadding = Math.max(0, modalWidth - 3 - homeLength);
180
- lines.push(' '.repeat(padding) +
181
- chalk_1.default.gray('│') +
182
- homeText +
183
- ' '.repeat(homePadding) +
184
- chalk_1.default.gray('│'));
185
- }
186
- // Footer
187
- lines.push(' '.repeat(padding) + chalk_1.default.gray('╰' + '─'.repeat(modalWidth - 2) + '╯'));
188
- return lines;
189
- }
190
- //# sourceMappingURL=modal.js.map