inup 1.4.12 → 1.5.1

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/LICENSE +21 -0
  2. package/README.md +39 -30
  3. package/dist/cli.js +29 -14
  4. package/dist/config/project-config.js +6 -0
  5. package/dist/core/package-detector.js +3 -2
  6. package/dist/core/upgrade-runner.js +6 -3
  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 +242 -114
  19. package/dist/services/background-audit.js +60 -0
  20. package/dist/services/index.js +3 -1
  21. package/dist/services/vulnerability-checker.js +133 -0
  22. package/dist/ui/controllers/index.js +8 -0
  23. package/dist/ui/controllers/package-info-modal-controller.js +237 -0
  24. package/dist/ui/controllers/vulnerability-audit-controller.js +82 -0
  25. package/dist/ui/index.js +3 -0
  26. package/dist/ui/input-handler.js +40 -9
  27. package/dist/ui/modal/index.js +22 -0
  28. package/dist/ui/modal/layout.js +84 -0
  29. package/dist/ui/modal/package-info-sections.js +327 -0
  30. package/dist/ui/modal/package-info.js +147 -0
  31. package/dist/ui/modal/theme-selector.js +46 -0
  32. package/dist/ui/modal/types.js +3 -0
  33. package/dist/ui/presenters/index.js +11 -0
  34. package/dist/ui/presenters/vulnerability.js +76 -0
  35. package/dist/ui/renderer/index.js +9 -11
  36. package/dist/ui/renderer/package-list.js +135 -62
  37. package/dist/ui/state/filter-manager.js +17 -2
  38. package/dist/ui/state/modal-manager.js +48 -6
  39. package/dist/ui/state/state-manager.js +42 -7
  40. package/dist/ui/utils/cursor.js +18 -0
  41. package/dist/ui/utils/index.js +8 -1
  42. package/dist/ui/utils/terminal-input.js +125 -0
  43. package/dist/ui/utils/text.js +75 -0
  44. package/dist/ui/utils/version.js +3 -2
  45. package/dist/utils/git.js +33 -0
  46. package/dist/utils/index.js +1 -0
  47. package/package.json +22 -19
  48. package/dist/services/changelog-fetcher.js +0 -215
  49. package/dist/ui/renderer/modal.js +0 -190
  50. package/dist/ui/renderer/theme-selector.js +0 -83
@@ -0,0 +1,327 @@
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.buildReleaseNotesSections = buildReleaseNotesSections;
7
+ exports.buildPackageInfoSections = buildPackageInfoSections;
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const themes_colors_1 = require("../themes-colors");
10
+ const vulnerability_1 = require("../presenters/vulnerability");
11
+ const utils_1 = require("../utils");
12
+ function formatTerminalLink(label, url) {
13
+ return `\u001b]8;;${url}\u0007${label}\u001b]8;;\u0007`;
14
+ }
15
+ function getRepositoryBaseUrl(repositoryUrl) {
16
+ if (!repositoryUrl) {
17
+ return null;
18
+ }
19
+ return repositoryUrl.replace(/\/releases\/?$/, '');
20
+ }
21
+ function sanitizeMarkdownText(text) {
22
+ return text
23
+ .replace(/<[^>]+>/g, '')
24
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
25
+ .replace(/[`*_~]/g, '')
26
+ .replace(/\s+/g, ' ')
27
+ .trim();
28
+ }
29
+ function linkifyContributorMentions(text) {
30
+ return text.replace(/(^|[\s(])@([a-zA-Z0-9-]+)/g, (match, prefix, username) => {
31
+ return `${prefix}${formatTerminalLink(`@${username}`, `https://github.com/${username}`)}`;
32
+ });
33
+ }
34
+ function linkifyRepositoryReferences(text, repositoryUrl) {
35
+ const repoBaseUrl = getRepositoryBaseUrl(repositoryUrl);
36
+ if (!repoBaseUrl || !repoBaseUrl.includes('github.com')) {
37
+ return text;
38
+ }
39
+ return text
40
+ .replace(/(^|[\s(])#(\d+)\b/g, (match, prefix, number) => {
41
+ return `${prefix}${formatTerminalLink(`#${number}`, `${repoBaseUrl}/pull/${number}`)}`;
42
+ })
43
+ .replace(/(^|[\s(])([0-9a-f]{7,40})\b/gi, (match, prefix, hash) => {
44
+ return `${prefix}${formatTerminalLink(hash, `${repoBaseUrl}/commit/${hash}`)}`;
45
+ });
46
+ }
47
+ function linkifyMarkdownText(text, repositoryUrl) {
48
+ return linkifyRepositoryReferences(linkifyContributorMentions(text), repositoryUrl);
49
+ }
50
+ function pushWrappedLines(lines, text, width, firstPrefix, restPrefix = firstPrefix, style) {
51
+ const firstWidth = Math.max(1, width - (0, utils_1.getVisualLength)(firstPrefix));
52
+ const restWidth = Math.max(1, width - (0, utils_1.getVisualLength)(restPrefix));
53
+ const segments = (0, utils_1.wrapPlainText)(text, firstWidth);
54
+ if (segments.length === 0) {
55
+ lines.push(firstPrefix.trimEnd());
56
+ return;
57
+ }
58
+ lines.push(firstPrefix + (style ? style(segments[0]) : segments[0]));
59
+ for (let i = 1; i < segments.length; i++) {
60
+ const wrappedSegments = (0, utils_1.wrapPlainText)(segments[i], restWidth);
61
+ for (const segment of wrappedSegments) {
62
+ lines.push(restPrefix + (style ? style(segment) : segment));
63
+ }
64
+ }
65
+ }
66
+ function isLowSignalTrailerLine(text) {
67
+ const normalized = text.toLowerCase();
68
+ return (normalized.startsWith('compare') ||
69
+ normalized.startsWith('full changelog') ||
70
+ normalized.startsWith('see full changelog') ||
71
+ normalized.startsWith('release notes') ||
72
+ normalized.includes('/compare/') ||
73
+ /^\w+: https?:\/\//.test(normalized));
74
+ }
75
+ function formatReleaseNotesMarkdown(markdown, width, repositoryUrl) {
76
+ const lines = [];
77
+ const rawLines = markdown.split('\n');
78
+ let prevBlank = false;
79
+ for (const rawLine of rawLines) {
80
+ const trimmed = rawLine.trim();
81
+ if (trimmed === '') {
82
+ if (!prevBlank && lines.length > 0) {
83
+ lines.push('');
84
+ prevBlank = true;
85
+ }
86
+ continue;
87
+ }
88
+ prevBlank = false;
89
+ if (/^```/.test(trimmed) || /^---+$/.test(trimmed)) {
90
+ continue;
91
+ }
92
+ const cleaned = sanitizeMarkdownText(trimmed);
93
+ if (!cleaned) {
94
+ continue;
95
+ }
96
+ const quoteMatch = trimmed.match(/^>\s*(.+)/);
97
+ if (quoteMatch) {
98
+ const quoteBody = linkifyMarkdownText(sanitizeMarkdownText(quoteMatch[1]), repositoryUrl);
99
+ const admonitionMatch = quoteBody.match(/^\[!([A-Z]+)\]$/i);
100
+ if (admonitionMatch) {
101
+ const label = `${admonitionMatch[1][0]}${admonitionMatch[1].slice(1).toLowerCase()}`;
102
+ if (lines.length > 0 && lines[lines.length - 1] !== '') {
103
+ lines.push('');
104
+ }
105
+ lines.push(chalk_1.default.blue.bold(` ${label}`));
106
+ }
107
+ else {
108
+ pushWrappedLines(lines, quoteBody, width, ' ', ' ', chalk_1.default.gray);
109
+ }
110
+ continue;
111
+ }
112
+ const headerMatch = cleaned.match(/^(#{1,6})\s+(.+)/);
113
+ if (headerMatch) {
114
+ const title = sanitizeMarkdownText(headerMatch[2]);
115
+ const lower = title.toLowerCase();
116
+ let style = chalk_1.default.white.bold;
117
+ if (lower.includes('breaking')) {
118
+ style = chalk_1.default.red.bold;
119
+ }
120
+ else if (lower.includes('feature') ||
121
+ lower.includes('added') ||
122
+ lower.includes('improvement')) {
123
+ style = chalk_1.default.green.bold;
124
+ }
125
+ else if (lower.includes('fix') || lower.includes('bug')) {
126
+ style = chalk_1.default.yellow.bold;
127
+ }
128
+ else if (lower.includes('deprecat')) {
129
+ style = chalk_1.default.magenta.bold;
130
+ }
131
+ if (lines.length > 0 && lines[lines.length - 1] !== '') {
132
+ lines.push('');
133
+ }
134
+ lines.push(style(` ${title}`));
135
+ continue;
136
+ }
137
+ const bulletMatch = cleaned.match(/^(\s*)[*-]\s+(.+)/);
138
+ if (bulletMatch) {
139
+ const indentLevel = Math.min(2, Math.floor(bulletMatch[1].length / 2));
140
+ const prefix = ` ${' '.repeat(indentLevel)}${chalk_1.default.gray('•')} `;
141
+ const restPrefix = ` ${' '.repeat(indentLevel + 1)}`;
142
+ const style = /breaking/i.test(bulletMatch[2]) ? chalk_1.default.red : undefined;
143
+ pushWrappedLines(lines, linkifyMarkdownText(sanitizeMarkdownText(bulletMatch[2]), repositoryUrl), width, prefix, restPrefix, style);
144
+ continue;
145
+ }
146
+ const orderedMatch = cleaned.match(/^(\s*)(\d+)\.\s+(.+)/);
147
+ if (orderedMatch) {
148
+ const indentLevel = Math.min(2, Math.floor(orderedMatch[1].length / 2));
149
+ const marker = `${orderedMatch[2]}.`;
150
+ const prefix = ` ${' '.repeat(indentLevel)}${marker} `;
151
+ const restPrefix = ` ${' '.repeat(indentLevel)}${' '.repeat(marker.length + 1)}`;
152
+ pushWrappedLines(lines, linkifyMarkdownText(sanitizeMarkdownText(orderedMatch[3]), repositoryUrl), width, prefix, restPrefix);
153
+ continue;
154
+ }
155
+ const style = isLowSignalTrailerLine(cleaned) ? chalk_1.default.gray : undefined;
156
+ pushWrappedLines(lines, linkifyMarkdownText(cleaned, repositoryUrl), width, ' ', ' ', style);
157
+ }
158
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
159
+ lines.pop();
160
+ }
161
+ return lines;
162
+ }
163
+ /**
164
+ * Build release notes sections for the currently viewed version.
165
+ * Shows one version at a time with navigation indicators.
166
+ */
167
+ function buildReleaseNotesSections(state, modalWidth) {
168
+ const sections = [];
169
+ if (!state.releaseNotesVersions || state.releaseNotesVersions.length === 0) {
170
+ return sections;
171
+ }
172
+ const loaded = state.releaseNotesLoaded;
173
+ const viewIndex = state.releaseNotesViewIndex ?? 0;
174
+ const totalVersions = state.releaseNotesVersions.length;
175
+ const currentVersion = state.releaseNotesVersions[viewIndex];
176
+ if (!currentVersion)
177
+ return sections;
178
+ // Show loading state for the viewed version
179
+ if (state.releaseNotesLoadingVersion === currentVersion) {
180
+ sections.push({
181
+ key: 'release-loading',
182
+ rows: [chalk_1.default.gray(`Loading release notes for v${currentVersion}...`)],
183
+ behavior: 'status',
184
+ });
185
+ return sections;
186
+ }
187
+ // Version not yet loaded (and not currently loading)
188
+ if (!loaded || !loaded.has(currentVersion)) {
189
+ sections.push({
190
+ key: 'release-pending',
191
+ rows: [chalk_1.default.gray(`Press ←/→ to load release notes for v${currentVersion}`)],
192
+ behavior: 'status',
193
+ });
194
+ return sections;
195
+ }
196
+ const content = loaded.get(currentVersion);
197
+ if (!content) {
198
+ // Version was loaded but had no release notes
199
+ sections.push({
200
+ key: 'release-none',
201
+ rows: [chalk_1.default.gray.italic(`No release notes found for v${currentVersion}`)],
202
+ behavior: 'status',
203
+ });
204
+ }
205
+ else {
206
+ const versionHeader = (0, themes_colors_1.getThemeColor)('primary')(`Version ${currentVersion}`);
207
+ const navHint = totalVersions > 1 ? chalk_1.default.gray(` (${viewIndex + 1}/${totalVersions})`) : '';
208
+ const rows = [chalk_1.default.bold(versionHeader) + navHint];
209
+ const formatted = formatReleaseNotesMarkdown(content, modalWidth - 4, state.repository);
210
+ rows.push(...formatted);
211
+ sections.push({
212
+ key: `release-${currentVersion}`,
213
+ rows,
214
+ behavior: 'body',
215
+ });
216
+ }
217
+ // Navigation hints
218
+ const canGoNewer = viewIndex > 0;
219
+ const canGoOlder = viewIndex < totalVersions - 1;
220
+ if (canGoNewer || canGoOlder) {
221
+ const hints = [];
222
+ if (canGoNewer)
223
+ hints.push('← newer');
224
+ if (canGoOlder)
225
+ hints.push('→ older');
226
+ sections.push({
227
+ key: 'release-nav',
228
+ rows: [chalk_1.default.gray(hints.join(' · '))],
229
+ behavior: 'status',
230
+ });
231
+ }
232
+ return sections;
233
+ }
234
+ function formatNumber(num) {
235
+ if (!num)
236
+ return 'N/A';
237
+ if (num >= 1000000)
238
+ return (num / 1000000).toFixed(1) + 'M';
239
+ if (num >= 1000)
240
+ return (num / 1000).toFixed(1) + 'K';
241
+ return num.toString();
242
+ }
243
+ function buildPackageInfoSections(state, modalWidth) {
244
+ const title = chalk_1.default.cyan.bold(`Package: ${state.name}`);
245
+ const authorLicense = chalk_1.default.gray(`${state.author || 'Unknown'} • ${state.license || 'MIT'}`);
246
+ const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
247
+ const targetVersion = chalk_1.default.green(state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion);
248
+ const sections = [
249
+ {
250
+ key: 'header',
251
+ rows: [title, authorLicense],
252
+ required: true,
253
+ behavior: 'pinned',
254
+ },
255
+ {
256
+ key: 'meta',
257
+ rows: [
258
+ `Current: ${currentVersion} Target: ${targetVersion}`,
259
+ ...(state.weeklyDownloads !== undefined
260
+ ? [(0, themes_colors_1.getThemeColor)('primary')(`Downloads/week: ${formatNumber(state.weeklyDownloads)}`)]
261
+ : []),
262
+ ],
263
+ required: true,
264
+ behavior: 'pinned',
265
+ },
266
+ ];
267
+ if (state.homepage) {
268
+ sections.push({
269
+ key: 'homepage',
270
+ rows: [
271
+ `Homepage: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.homepage, modalWidth - 14)))}`,
272
+ ],
273
+ behavior: 'pinned',
274
+ });
275
+ }
276
+ if (state.description) {
277
+ sections.push({
278
+ key: 'description',
279
+ rows: (0, utils_1.wrapPlainText)(state.description, modalWidth - 4)
280
+ .slice(0, 4)
281
+ .map((line, index, rows) => index === rows.length - 1 && rows.length === 4
282
+ ? (0, utils_1.truncatePlainText)(line, modalWidth - 4)
283
+ : line),
284
+ behavior: 'pinned',
285
+ });
286
+ }
287
+ if (state.vulnerability && state.vulnerability.count > 0) {
288
+ const representative = (0, vulnerability_1.selectRepresentativeAdvisory)(state.vulnerability);
289
+ const severityColor = (0, vulnerability_1.getVulnerabilitySeverityColor)(state.vulnerability.highestSeverity);
290
+ const vulnerabilityRows = [
291
+ chalk_1.default.red.bold(`${state.vulnerability.count} known vulnerabilit${state.vulnerability.count === 1 ? 'y' : 'ies'} (${severityColor(state.vulnerability.highestSeverity.toUpperCase())})`),
292
+ ];
293
+ if (representative) {
294
+ const severityLabel = ` ${severityColor(`[${representative.severity.toUpperCase()}]`)} `;
295
+ const availableTitleWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(severityLabel));
296
+ vulnerabilityRows.push(`${severityLabel}${(0, utils_1.truncatePlainText)(representative.title, availableTitleWidth)}`);
297
+ }
298
+ const detailsUrl = state.vulnerability.detailsUrl || representative?.url;
299
+ if (detailsUrl) {
300
+ const linkPrefix = ` ${(0, vulnerability_1.getVulnerabilityLinkLabel)(detailsUrl)} `;
301
+ const availableLinkWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(linkPrefix));
302
+ vulnerabilityRows.push(`${linkPrefix}${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(detailsUrl, availableLinkWidth)))}`);
303
+ }
304
+ if (state.vulnerability.count > 1) {
305
+ vulnerabilityRows.push(chalk_1.default.gray(` ... and ${state.vulnerability.count - 1} more`));
306
+ }
307
+ sections.push({
308
+ key: 'vulnerability',
309
+ rows: vulnerabilityRows,
310
+ required: true,
311
+ behavior: 'pinned',
312
+ });
313
+ }
314
+ if (state.repository) {
315
+ sections.push({
316
+ key: 'changelog',
317
+ rows: [
318
+ `Changelog: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.repository, modalWidth - 15)))}`,
319
+ ],
320
+ behavior: 'pinned',
321
+ });
322
+ }
323
+ const releaseNotesSections = buildReleaseNotesSections(state, modalWidth);
324
+ sections.push(...releaseNotesSections);
325
+ return sections;
326
+ }
327
+ //# sourceMappingURL=package-info-sections.js.map
@@ -0,0 +1,147 @@
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 layout_1 = require("./layout");
10
+ const package_info_sections_1 = require("./package-info-sections");
11
+ function renderPackageInfoLoading(state, terminalWidth = 80, terminalHeight = 24) {
12
+ const sections = [
13
+ {
14
+ key: 'loading',
15
+ rows: [chalk_1.default.cyan('Loading package info'), chalk_1.default.white(state.name)],
16
+ required: true,
17
+ behavior: 'status',
18
+ },
19
+ ];
20
+ return {
21
+ lines: (0, layout_1.renderModalFrame)(sections, {
22
+ terminalWidth,
23
+ terminalHeight,
24
+ minWidth: 50,
25
+ maxWidth: 120,
26
+ }),
27
+ maxScrollOffset: 0,
28
+ totalContentRows: 2,
29
+ usesInternalScroll: false,
30
+ };
31
+ }
32
+ function renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24, scrollOffset = 0) {
33
+ const modalWidth = (0, layout_1.getModalWidth)(terminalWidth, 60, 120);
34
+ const allSections = (0, package_info_sections_1.buildPackageInfoSections)(state, modalWidth);
35
+ const maxHeight = Math.max(10, terminalHeight - 2);
36
+ const trimOrder = ['homepage', 'changelog', 'description'];
37
+ const compactSections = (0, layout_1.fitModalSections)(allSections, maxHeight, trimOrder);
38
+ const hasScrollableBody = allSections.some((section) => section.behavior === 'body');
39
+ if (!hasScrollableBody || (0, layout_1.getModalFrameHeight)(compactSections) <= maxHeight) {
40
+ return {
41
+ lines: (0, layout_1.renderModalFrame)(compactSections, {
42
+ terminalWidth,
43
+ terminalHeight,
44
+ minWidth: 60,
45
+ maxWidth: 120,
46
+ }),
47
+ maxScrollOffset: 0,
48
+ totalContentRows: 0,
49
+ usesInternalScroll: false,
50
+ };
51
+ }
52
+ const fixedModalHeight = maxHeight;
53
+ const padding = Math.floor((terminalWidth - modalWidth) / 2);
54
+ const pinnedSections = allSections.filter((section) => (section.behavior ?? 'pinned') === 'pinned');
55
+ const bodySections = allSections.filter((section) => (section.behavior ?? 'pinned') !== 'pinned');
56
+ const minBodyRows = 3;
57
+ const reservedBodyRows = minBodyRows + (bodySections.length > 0 ? 1 : 0);
58
+ const maxPinnedHeight = Math.max(6, fixedModalHeight - reservedBodyRows);
59
+ const fittedPinned = (0, layout_1.fitModalSections)(pinnedSections, maxPinnedHeight, trimOrder);
60
+ const pinnedRowCount = (0, layout_1.getModalSectionRowCount)(fittedPinned);
61
+ const availableForBody = Math.max(minBodyRows, fixedModalHeight - 2 - pinnedRowCount - (bodySections.length > 0 ? 1 : 0));
62
+ const bodyRows = [];
63
+ bodySections.forEach((section, index) => {
64
+ if (index > 0) {
65
+ bodyRows.push({ row: '__SEPARATOR__', sectionIndex: index });
66
+ }
67
+ for (const row of section.rows) {
68
+ bodyRows.push({ row, sectionIndex: index });
69
+ }
70
+ });
71
+ const totalScrollableRows = bodyRows.length;
72
+ const totalVersions = state.releaseNotesVersions?.length ?? 0;
73
+ const viewIndex = state.releaseNotesViewIndex ?? 0;
74
+ const canGoNewer = viewIndex > 0;
75
+ const canGoOlder = viewIndex < totalVersions - 1;
76
+ const footerStatus = state.releaseNotesLoadingVersion
77
+ ? chalk_1.default.gray(`Loading release notes for v${state.releaseNotesLoadingVersion}`)
78
+ : totalScrollableRows > availableForBody
79
+ ? chalk_1.default.gray('')
80
+ : canGoNewer || canGoOlder
81
+ ? chalk_1.default.gray([canGoNewer ? '← newer version' : null, canGoOlder ? '→ older version' : null]
82
+ .filter((hint) => Boolean(hint))
83
+ .join(' · '))
84
+ : null;
85
+ const visibleBodyRows = footerStatus ? Math.max(1, availableForBody - 1) : availableForBody;
86
+ const maxScroll = Math.max(0, totalScrollableRows - visibleBodyRows);
87
+ const clampedOffset = Math.min(scrollOffset, maxScroll);
88
+ const resolvedFooterStatus = state.releaseNotesLoadingVersion
89
+ ? chalk_1.default.gray(`Loading release notes for v${state.releaseNotesLoadingVersion}`)
90
+ : maxScroll > 0
91
+ ? clampedOffset < maxScroll
92
+ ? chalk_1.default.gray(`Lines ${clampedOffset + 1}-${Math.min(clampedOffset + visibleBodyRows, totalScrollableRows)} of ${totalScrollableRows}`)
93
+ : chalk_1.default.gray('End of release notes')
94
+ : canGoNewer || canGoOlder
95
+ ? chalk_1.default.gray([canGoNewer ? '← newer version' : null, canGoOlder ? '→ older version' : null]
96
+ .filter((hint) => Boolean(hint))
97
+ .join(' · '))
98
+ : null;
99
+ const visibleSlice = bodyRows.slice(clampedOffset, clampedOffset + visibleBodyRows);
100
+ const lines = [];
101
+ const topPadding = Math.max(0, Math.floor((terminalHeight - fixedModalHeight) / 2));
102
+ for (let i = 0; i < topPadding; i++) {
103
+ lines.push('');
104
+ }
105
+ lines.push(' '.repeat(padding) + chalk_1.default.gray('╭' + '─'.repeat(modalWidth - 2) + '╮'));
106
+ fittedPinned.forEach((section, sectionIndex) => {
107
+ if (sectionIndex > 0) {
108
+ lines.push((0, layout_1.renderModalSeparator)(padding, modalWidth));
109
+ }
110
+ for (const row of section.rows) {
111
+ lines.push((0, layout_1.renderModalRow)(padding, modalWidth, row));
112
+ }
113
+ });
114
+ if (bodySections.length > 0) {
115
+ lines.push((0, layout_1.renderModalSeparator)(padding, modalWidth));
116
+ }
117
+ let renderedScrollRows = 0;
118
+ for (const entry of visibleSlice) {
119
+ if (entry.row === '__SEPARATOR__') {
120
+ lines.push((0, layout_1.renderModalSeparator)(padding, modalWidth));
121
+ }
122
+ else {
123
+ lines.push((0, layout_1.renderModalRow)(padding, modalWidth, entry.row));
124
+ }
125
+ renderedScrollRows++;
126
+ }
127
+ const usedContentRows = pinnedRowCount +
128
+ (bodySections.length > 0 ? 1 : 0) +
129
+ renderedScrollRows +
130
+ (resolvedFooterStatus ? 1 : 0);
131
+ const totalContentSlots = fixedModalHeight - 2;
132
+ const emptyRows = Math.max(0, totalContentSlots - usedContentRows);
133
+ for (let i = 0; i < emptyRows; i++) {
134
+ lines.push((0, layout_1.renderModalRow)(padding, modalWidth, ''));
135
+ }
136
+ if (resolvedFooterStatus) {
137
+ lines.push((0, layout_1.renderModalRow)(padding, modalWidth, resolvedFooterStatus));
138
+ }
139
+ lines.push(' '.repeat(padding) + chalk_1.default.gray('╰' + '─'.repeat(modalWidth - 2) + '╯'));
140
+ return {
141
+ lines,
142
+ maxScrollOffset: maxScroll,
143
+ totalContentRows: totalScrollableRows,
144
+ usesInternalScroll: true,
145
+ };
146
+ }
147
+ //# sourceMappingURL=package-info.js.map
@@ -0,0 +1,46 @@
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.renderThemeSelectorModal = renderThemeSelectorModal;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const themes_1 = require("../themes");
9
+ const layout_1 = require("./layout");
10
+ function renderThemeSelectorModal(currentTheme, previewTheme, terminalWidth = 80, terminalHeight = 24) {
11
+ const themeRows = themes_1.themeNames.map((themeName) => {
12
+ const isSelected = themeName === previewTheme;
13
+ const isCurrent = themeName === currentTheme;
14
+ const themeObj = themes_1.themes[themeName];
15
+ let themeLine = isSelected ? chalk_1.default.green('● ') : chalk_1.default.gray('○ ');
16
+ themeLine += themeObj.name;
17
+ if (isCurrent) {
18
+ themeLine += chalk_1.default.gray(' (current)');
19
+ }
20
+ return themeLine;
21
+ });
22
+ const sections = [
23
+ {
24
+ key: 'header',
25
+ rows: [chalk_1.default.cyan('🎨 Select Theme')],
26
+ required: true,
27
+ },
28
+ {
29
+ key: 'themes',
30
+ rows: themeRows,
31
+ required: true,
32
+ },
33
+ {
34
+ key: 'instructions',
35
+ rows: [chalk_1.default.gray('↑/↓ to navigate • Enter to confirm • Esc to cancel')],
36
+ required: true,
37
+ },
38
+ ];
39
+ return (0, layout_1.renderModalFrame)(sections, {
40
+ terminalWidth,
41
+ terminalHeight,
42
+ minWidth: 76,
43
+ maxWidth: 76,
44
+ });
45
+ }
46
+ //# sourceMappingURL=theme-selector.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectRepresentativeAdvisory = exports.mergeVulnerabilitySummary = exports.getVulnerabilitySeverityColor = exports.getVulnerabilityLinkLabel = exports.getVulnerabilityBadge = exports.createVulnerabilitySummary = void 0;
4
+ var vulnerability_1 = require("./vulnerability");
5
+ Object.defineProperty(exports, "createVulnerabilitySummary", { enumerable: true, get: function () { return vulnerability_1.createVulnerabilitySummary; } });
6
+ Object.defineProperty(exports, "getVulnerabilityBadge", { enumerable: true, get: function () { return vulnerability_1.getVulnerabilityBadge; } });
7
+ Object.defineProperty(exports, "getVulnerabilityLinkLabel", { enumerable: true, get: function () { return vulnerability_1.getVulnerabilityLinkLabel; } });
8
+ Object.defineProperty(exports, "getVulnerabilitySeverityColor", { enumerable: true, get: function () { return vulnerability_1.getVulnerabilitySeverityColor; } });
9
+ Object.defineProperty(exports, "mergeVulnerabilitySummary", { enumerable: true, get: function () { return vulnerability_1.mergeVulnerabilitySummary; } });
10
+ Object.defineProperty(exports, "selectRepresentativeAdvisory", { enumerable: true, get: function () { return vulnerability_1.selectRepresentativeAdvisory; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,76 @@
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.getVulnerabilitySeverityColor = getVulnerabilitySeverityColor;
7
+ exports.getVulnerabilityBadge = getVulnerabilityBadge;
8
+ exports.shouldDisplayVulnerabilityForDependency = shouldDisplayVulnerabilityForDependency;
9
+ exports.getVulnerabilityLinkLabel = getVulnerabilityLinkLabel;
10
+ exports.selectRepresentativeAdvisory = selectRepresentativeAdvisory;
11
+ exports.createVulnerabilitySummary = createVulnerabilitySummary;
12
+ exports.mergeVulnerabilitySummary = mergeVulnerabilitySummary;
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ function getVulnerabilitySeverityColor(severity) {
15
+ switch (severity) {
16
+ case 'critical':
17
+ return chalk_1.default.bgRed.white.bold;
18
+ case 'high':
19
+ return chalk_1.default.red;
20
+ case 'moderate':
21
+ return chalk_1.default.yellow;
22
+ case 'low':
23
+ case 'info':
24
+ default:
25
+ return chalk_1.default.gray;
26
+ }
27
+ }
28
+ function getVulnerabilityBadge(vulnerability) {
29
+ if (!vulnerability)
30
+ return '';
31
+ switch (vulnerability.highestSeverity) {
32
+ case 'critical':
33
+ return chalk_1.default.bgRed.white.bold('[CRIT]');
34
+ case 'high':
35
+ return chalk_1.default.red('[HIGH]');
36
+ case 'moderate':
37
+ return chalk_1.default.yellow('[MOD]');
38
+ case 'low':
39
+ return chalk_1.default.gray('[LOW]');
40
+ case 'info':
41
+ return chalk_1.default.gray('[INFO]');
42
+ default:
43
+ return '';
44
+ }
45
+ }
46
+ function shouldDisplayVulnerabilityForDependency(dependencyType, options = {}) {
47
+ switch (dependencyType) {
48
+ case 'peerDependencies':
49
+ return options.showPeerDependencyVulnerabilities === true;
50
+ case 'optionalDependencies':
51
+ return options.showOptionalDependencyVulnerabilities === true;
52
+ default:
53
+ return true;
54
+ }
55
+ }
56
+ function getVulnerabilityLinkLabel(detailsUrl) {
57
+ return detailsUrl.includes('/advisories') ? 'Security:' : 'Details:';
58
+ }
59
+ function selectRepresentativeAdvisory(vulnerability) {
60
+ return vulnerability.advisories[0];
61
+ }
62
+ function createVulnerabilitySummary(existing, advisories, highestSeverity) {
63
+ return {
64
+ count: advisories.length,
65
+ highestSeverity,
66
+ detailsUrl: existing?.detailsUrl || advisories[0]?.url,
67
+ advisories,
68
+ };
69
+ }
70
+ function mergeVulnerabilitySummary(existing, summary) {
71
+ return {
72
+ ...summary,
73
+ detailsUrl: existing?.detailsUrl || summary.detailsUrl,
74
+ };
75
+ }
76
+ //# sourceMappingURL=vulnerability.js.map
@@ -39,14 +39,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.UIRenderer = void 0;
40
40
  const PackageList = __importStar(require("./package-list"));
41
41
  const Confirmation = __importStar(require("./confirmation"));
42
- const Modal = __importStar(require("./modal"));
43
- const ThemeSelector = __importStar(require("./theme-selector"));
42
+ const Modal = __importStar(require("../modal"));
44
43
  /**
45
44
  * Main UI renderer class that composes all rendering parts
46
45
  */
47
46
  class UIRenderer {
48
- renderPackageLine(state, index, isCurrentRow) {
49
- return PackageList.renderPackageLine(state, index, isCurrentRow);
47
+ renderPackageLine(state, index, isCurrentRow, options) {
48
+ return PackageList.renderPackageLine(state, index, isCurrentRow, 80, options);
50
49
  }
51
50
  renderSectionHeader(title, sectionType) {
52
51
  return PackageList.renderSectionHeader(title, sectionType);
@@ -54,8 +53,8 @@ class UIRenderer {
54
53
  renderSpacer() {
55
54
  return PackageList.renderSpacer();
56
55
  }
57
- renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth = 80, loadingProgress) {
58
- return PackageList.renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth, loadingProgress);
56
+ renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth = 80, loadingProgress, auditProgress, options) {
57
+ return PackageList.renderInterface(states, currentRow, scrollOffset, maxVisibleItems, forceFullRender, renderableItems, activeFilterLabel, packageManager, filterMode, filterQuery, totalPackagesBeforeFilter, terminalWidth, loadingProgress, auditProgress, options);
59
58
  }
60
59
  renderPackagesTable(packages) {
61
60
  return PackageList.renderPackagesTable(packages);
@@ -66,17 +65,16 @@ class UIRenderer {
66
65
  renderPackageInfoLoading(state, terminalWidth = 80, terminalHeight = 24) {
67
66
  return Modal.renderPackageInfoLoading(state, terminalWidth, terminalHeight);
68
67
  }
69
- renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24) {
70
- return Modal.renderPackageInfoModal(state, terminalWidth, terminalHeight);
68
+ renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24, scrollOffset = 0) {
69
+ return Modal.renderPackageInfoModal(state, terminalWidth, terminalHeight, scrollOffset);
71
70
  }
72
71
  renderThemeSelectorModal(currentTheme, previewTheme, terminalWidth = 80, terminalHeight = 24) {
73
- return ThemeSelector.renderThemeSelectorModal(currentTheme, previewTheme, terminalWidth, terminalHeight);
72
+ return Modal.renderThemeSelectorModal(currentTheme, previewTheme, terminalWidth, terminalHeight);
74
73
  }
75
74
  }
76
75
  exports.UIRenderer = UIRenderer;
77
76
  // Re-export all functions for direct use if needed
78
77
  __exportStar(require("./package-list"), exports);
79
78
  __exportStar(require("./confirmation"), exports);
80
- __exportStar(require("./modal"), exports);
81
- __exportStar(require("./theme-selector"), exports);
79
+ __exportStar(require("../modal"), exports);
82
80
  //# sourceMappingURL=index.js.map