inup 1.4.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.
- package/README.md +142 -0
- package/dist/cli.js +109 -0
- package/dist/constants.js +28 -0
- package/dist/core/index.js +23 -0
- package/dist/core/package-detector.js +182 -0
- package/dist/core/upgrade-runner.js +135 -0
- package/dist/core/upgrader.js +139 -0
- package/dist/index.js +10 -0
- package/dist/interactive-ui.js +411 -0
- package/dist/services/changelog-fetcher.js +193 -0
- package/dist/services/index.js +24 -0
- package/dist/services/jsdelivr-registry.js +232 -0
- package/dist/services/npm-registry.js +145 -0
- package/dist/services/package-manager-detector.js +172 -0
- package/dist/services/version-checker.js +92 -0
- package/dist/types.js +3 -0
- package/dist/ui/index.js +13 -0
- package/dist/ui/input-handler.js +112 -0
- package/dist/ui/renderer/confirmation.js +34 -0
- package/dist/ui/renderer/index.js +77 -0
- package/dist/ui/renderer/modal.js +184 -0
- package/dist/ui/renderer/package-list.js +243 -0
- package/dist/ui/state.js +250 -0
- package/dist/ui/utils.js +66 -0
- package/dist/utils/exec.js +38 -0
- package/dist/utils/filesystem.js +225 -0
- package/dist/utils/index.js +28 -0
- package/dist/utils/version.js +130 -0
- package/package.json +73 -0
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
/**
|
|
10
|
+
* Remove ANSI color codes from a string for length calculation
|
|
11
|
+
*/
|
|
12
|
+
function stripAnsi(str) {
|
|
13
|
+
return str.replace(/\u001b\[[0-9;]*m/g, '');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format a number for display (e.g., 1000000 -> "1M", 1000 -> "1K")
|
|
17
|
+
*/
|
|
18
|
+
function formatNumber(num) {
|
|
19
|
+
if (!num)
|
|
20
|
+
return 'N/A';
|
|
21
|
+
if (num >= 1000000)
|
|
22
|
+
return (num / 1000000).toFixed(1) + 'M';
|
|
23
|
+
if (num >= 1000)
|
|
24
|
+
return (num / 1000).toFixed(1) + 'K';
|
|
25
|
+
return num.toString();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wrap text to fit within max width
|
|
29
|
+
*/
|
|
30
|
+
function wrapText(text, maxWidth) {
|
|
31
|
+
if (text.length <= maxWidth) {
|
|
32
|
+
return [text];
|
|
33
|
+
}
|
|
34
|
+
const lines = [];
|
|
35
|
+
let current = '';
|
|
36
|
+
const words = text.split(' ');
|
|
37
|
+
for (const word of words) {
|
|
38
|
+
if ((current + ' ' + word).length > maxWidth) {
|
|
39
|
+
if (current)
|
|
40
|
+
lines.push(current);
|
|
41
|
+
current = word;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
current = current ? current + ' ' + word : word;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (current)
|
|
48
|
+
lines.push(current);
|
|
49
|
+
return lines;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Render a loading state for the info modal
|
|
53
|
+
*/
|
|
54
|
+
function renderPackageInfoLoading(state, terminalWidth = 80, terminalHeight = 24) {
|
|
55
|
+
const modalWidth = Math.min(terminalWidth - 6, 120);
|
|
56
|
+
const padding = Math.floor((terminalWidth - modalWidth) / 2);
|
|
57
|
+
const lines = [];
|
|
58
|
+
// Top padding to center vertically
|
|
59
|
+
const topPadding = Math.max(1, Math.floor((terminalHeight - 10) / 2));
|
|
60
|
+
for (let i = 0; i < topPadding; i++) {
|
|
61
|
+
lines.push('');
|
|
62
|
+
}
|
|
63
|
+
// Modal border
|
|
64
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('╭' + '─'.repeat(modalWidth - 2) + '╮'));
|
|
65
|
+
// Loading message
|
|
66
|
+
const loadingMsg = '⏳ Loading package info...';
|
|
67
|
+
const msgPadding = modalWidth - 4 - stripAnsi(loadingMsg).length;
|
|
68
|
+
lines.push(' '.repeat(padding) +
|
|
69
|
+
chalk_1.default.gray('│') +
|
|
70
|
+
' ' +
|
|
71
|
+
chalk_1.default.cyan(loadingMsg) +
|
|
72
|
+
' '.repeat(Math.max(0, msgPadding)) +
|
|
73
|
+
chalk_1.default.gray('│'));
|
|
74
|
+
// Package name
|
|
75
|
+
const nameMsg = `${state.name}`;
|
|
76
|
+
const namePadding = modalWidth - 4 - nameMsg.length;
|
|
77
|
+
lines.push(' '.repeat(padding) +
|
|
78
|
+
chalk_1.default.gray('│') +
|
|
79
|
+
' ' +
|
|
80
|
+
chalk_1.default.white(nameMsg) +
|
|
81
|
+
' '.repeat(Math.max(0, namePadding)) +
|
|
82
|
+
chalk_1.default.gray('│'));
|
|
83
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('╰' + '─'.repeat(modalWidth - 2) + '╯'));
|
|
84
|
+
return lines;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Render a full-screen modal overlay showing package information
|
|
88
|
+
* Similar to Turbo's help menu - centered with disabled background
|
|
89
|
+
*/
|
|
90
|
+
function renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24) {
|
|
91
|
+
const modalWidth = Math.min(terminalWidth - 6, 120); // Leave margins
|
|
92
|
+
const padding = Math.floor((terminalWidth - modalWidth) / 2);
|
|
93
|
+
const lines = [];
|
|
94
|
+
// Top padding to center vertically
|
|
95
|
+
const topPadding = Math.max(1, Math.floor((terminalHeight - 20) / 2));
|
|
96
|
+
for (let i = 0; i < topPadding; i++) {
|
|
97
|
+
lines.push('');
|
|
98
|
+
}
|
|
99
|
+
// Modal border and header
|
|
100
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('╭' + '─'.repeat(modalWidth - 2) + '╮'));
|
|
101
|
+
// Title with package name
|
|
102
|
+
const title = ` ℹ️ ${state.name}`;
|
|
103
|
+
const titleLength = stripAnsi(title).length;
|
|
104
|
+
const titlePadding = Math.max(0, modalWidth - 2 - titleLength);
|
|
105
|
+
lines.push(' '.repeat(padding) +
|
|
106
|
+
chalk_1.default.gray('│') +
|
|
107
|
+
chalk_1.default.cyan.bold(title) +
|
|
108
|
+
' '.repeat(titlePadding) +
|
|
109
|
+
chalk_1.default.gray('│'));
|
|
110
|
+
// License and author line
|
|
111
|
+
const authorLicense = `${state.author || 'Unknown'} • ${state.license || 'MIT'}`;
|
|
112
|
+
const authorLength = authorLicense.length;
|
|
113
|
+
const authorPadding = Math.max(0, modalWidth - 3 - authorLength);
|
|
114
|
+
lines.push(' '.repeat(padding) +
|
|
115
|
+
chalk_1.default.gray('│') +
|
|
116
|
+
' ' +
|
|
117
|
+
chalk_1.default.gray(authorLicense) +
|
|
118
|
+
' '.repeat(authorPadding) +
|
|
119
|
+
chalk_1.default.gray('│'));
|
|
120
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
|
|
121
|
+
// Current and target versions
|
|
122
|
+
const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
|
|
123
|
+
const targetVersion = chalk_1.default.green(state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion);
|
|
124
|
+
const versionText = `Current: ${currentVersion} → Target: ${targetVersion}`;
|
|
125
|
+
const versionLength = stripAnsi(versionText).length;
|
|
126
|
+
const versionPadding = Math.max(0, modalWidth - 3 - versionLength);
|
|
127
|
+
lines.push(' '.repeat(padding) +
|
|
128
|
+
chalk_1.default.gray('│') +
|
|
129
|
+
' ' +
|
|
130
|
+
versionText +
|
|
131
|
+
' '.repeat(versionPadding) +
|
|
132
|
+
chalk_1.default.gray('│'));
|
|
133
|
+
// Weekly downloads
|
|
134
|
+
if (state.weeklyDownloads !== undefined) {
|
|
135
|
+
const downloadsText = `📊 ${formatNumber(state.weeklyDownloads)} downloads/week`;
|
|
136
|
+
const downloadsLength = stripAnsi(downloadsText).length;
|
|
137
|
+
const downloadsPadding = Math.max(0, modalWidth - 3 - downloadsLength);
|
|
138
|
+
lines.push(' '.repeat(padding) +
|
|
139
|
+
chalk_1.default.gray('│') +
|
|
140
|
+
' ' +
|
|
141
|
+
chalk_1.default.blue(downloadsText) +
|
|
142
|
+
' '.repeat(downloadsPadding) +
|
|
143
|
+
chalk_1.default.gray('│'));
|
|
144
|
+
}
|
|
145
|
+
// Description
|
|
146
|
+
if (state.description) {
|
|
147
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
|
|
148
|
+
const descriptionLines = wrapText(state.description, modalWidth - 4);
|
|
149
|
+
for (const descLine of descriptionLines) {
|
|
150
|
+
const descLength = descLine.length;
|
|
151
|
+
const descPadding = Math.max(0, modalWidth - 3 - descLength);
|
|
152
|
+
lines.push(' '.repeat(padding) +
|
|
153
|
+
chalk_1.default.gray('│') +
|
|
154
|
+
' ' +
|
|
155
|
+
chalk_1.default.white(descLine) +
|
|
156
|
+
' '.repeat(descPadding) +
|
|
157
|
+
chalk_1.default.gray('│'));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Changelog/Releases section (moved to middle)
|
|
161
|
+
if (state.repository) {
|
|
162
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
|
|
163
|
+
const repoLabel = 'Changelog:';
|
|
164
|
+
const repoUrl = state.repository.substring(0, modalWidth - 20);
|
|
165
|
+
const repoText = ` ${repoLabel} ${chalk_1.default.blue.underline(repoUrl)}`;
|
|
166
|
+
const repoLength = stripAnsi(repoText).length;
|
|
167
|
+
const repoPadding = Math.max(0, modalWidth - 2 - repoLength);
|
|
168
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('│') + repoText + ' '.repeat(repoPadding) + chalk_1.default.gray('│'));
|
|
169
|
+
}
|
|
170
|
+
// Links section
|
|
171
|
+
if (state.homepage) {
|
|
172
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('├' + '─'.repeat(modalWidth - 2) + '┤'));
|
|
173
|
+
const homeLabel = 'Homepage:';
|
|
174
|
+
const homeUrl = state.homepage.substring(0, modalWidth - 20);
|
|
175
|
+
const homeText = ` ${homeLabel} ${chalk_1.default.blue.underline(homeUrl)}`;
|
|
176
|
+
const homeLength = stripAnsi(homeText).length;
|
|
177
|
+
const homePadding = Math.max(0, modalWidth - 2 - homeLength);
|
|
178
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('│') + homeText + ' '.repeat(homePadding) + chalk_1.default.gray('│'));
|
|
179
|
+
}
|
|
180
|
+
// Footer
|
|
181
|
+
lines.push(' '.repeat(padding) + chalk_1.default.gray('╰' + '─'.repeat(modalWidth - 2) + '╯'));
|
|
182
|
+
return lines;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=modal.js.map
|
|
@@ -0,0 +1,243 @@
|
|
|
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.renderPackageLine = renderPackageLine;
|
|
7
|
+
exports.renderSectionHeader = renderSectionHeader;
|
|
8
|
+
exports.renderSpacer = renderSpacer;
|
|
9
|
+
exports.renderInterface = renderInterface;
|
|
10
|
+
exports.renderPackagesTable = renderPackagesTable;
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const utils_1 = require("../utils");
|
|
13
|
+
/**
|
|
14
|
+
* Render a single package line
|
|
15
|
+
*/
|
|
16
|
+
function renderPackageLine(state, index, isCurrentRow) {
|
|
17
|
+
const prefix = isCurrentRow ? chalk_1.default.green('❯ ') : ' ';
|
|
18
|
+
// Package name with special formatting for scoped packages (@author/package)
|
|
19
|
+
let packageName;
|
|
20
|
+
if (state.name.startsWith('@')) {
|
|
21
|
+
const parts = state.name.split('/');
|
|
22
|
+
if (parts.length >= 2) {
|
|
23
|
+
const author = parts[0]; // @author
|
|
24
|
+
const packagePart = parts.slice(1).join('/'); // package name
|
|
25
|
+
if (isCurrentRow) {
|
|
26
|
+
packageName = chalk_1.default.white.bold(author) + chalk_1.default.cyan('/' + packagePart);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
packageName = chalk_1.default.white.bold(author) + chalk_1.default.white('/' + packagePart);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
packageName = isCurrentRow ? chalk_1.default.cyan(state.name) : chalk_1.default.white(state.name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
packageName = isCurrentRow ? chalk_1.default.cyan(state.name) : chalk_1.default.white(state.name);
|
|
38
|
+
}
|
|
39
|
+
// Determine which dot should be filled (only one per package)
|
|
40
|
+
const isCurrentSelected = state.selectedOption === 'none';
|
|
41
|
+
const isRangeSelected = state.selectedOption === 'range';
|
|
42
|
+
const isLatestSelected = state.selectedOption === 'latest';
|
|
43
|
+
// Current version dot and version (show original specifier with prefix)
|
|
44
|
+
const currentDot = isCurrentSelected ? chalk_1.default.green('●') : chalk_1.default.gray('○');
|
|
45
|
+
const currentVersion = chalk_1.default.white(state.currentVersionSpecifier);
|
|
46
|
+
// Range version dot and version
|
|
47
|
+
let rangeDot = '';
|
|
48
|
+
let rangeVersionText = '';
|
|
49
|
+
let rangeDashes = '';
|
|
50
|
+
if (state.hasRangeUpdate) {
|
|
51
|
+
rangeDot = isRangeSelected ? chalk_1.default.green('●') : chalk_1.default.gray('○');
|
|
52
|
+
const rangeVersionWithPrefix = utils_1.VersionUtils.applyVersionPrefix(state.currentVersionSpecifier, state.rangeVersion);
|
|
53
|
+
rangeVersionText = chalk_1.default.yellow(rangeVersionWithPrefix);
|
|
54
|
+
rangeDashes = '';
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
rangeDot = chalk_1.default.gray('○');
|
|
58
|
+
rangeVersionText = '';
|
|
59
|
+
rangeDashes = chalk_1.default.gray('─');
|
|
60
|
+
}
|
|
61
|
+
// Latest version dot and version
|
|
62
|
+
let latestDot = '';
|
|
63
|
+
let latestVersionText = '';
|
|
64
|
+
let latestDashes = '';
|
|
65
|
+
if (state.hasMajorUpdate) {
|
|
66
|
+
latestDot = isLatestSelected ? chalk_1.default.green('●') : chalk_1.default.gray('○');
|
|
67
|
+
const latestVersionWithPrefix = utils_1.VersionUtils.applyVersionPrefix(state.currentVersionSpecifier, state.latestVersion);
|
|
68
|
+
latestVersionText = chalk_1.default.red(latestVersionWithPrefix);
|
|
69
|
+
latestDashes = '';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
latestDot = chalk_1.default.gray('○');
|
|
73
|
+
latestVersionText = '';
|
|
74
|
+
latestDashes = chalk_1.default.gray('─');
|
|
75
|
+
}
|
|
76
|
+
// Fixed column widths for perfect alignment
|
|
77
|
+
const packageNameWidth = 38; // Total package column width minus prefix (2 chars)
|
|
78
|
+
const currentColumnWidth = 16; // Increased to accommodate ^ and ~ prefixes
|
|
79
|
+
const rangeColumnWidth = 16; // Increased to accommodate ^ and ~ prefixes
|
|
80
|
+
const latestColumnWidth = 16; // Increased to accommodate ^ and ~ prefixes
|
|
81
|
+
// Package name with fixed width and dashes
|
|
82
|
+
const nameLength = state.name.length;
|
|
83
|
+
const namePadding = Math.max(0, packageNameWidth - nameLength - 1); // -1 for space after package name
|
|
84
|
+
const nameDashes = '-'.repeat(namePadding);
|
|
85
|
+
const dashColor = isCurrentRow ? chalk_1.default.white : chalk_1.default.gray;
|
|
86
|
+
const packageNameSection = `${packageName} ${dashColor(nameDashes)}`;
|
|
87
|
+
// Current version section with fixed width
|
|
88
|
+
const currentSection = `${currentDot} ${currentVersion}`;
|
|
89
|
+
const currentSectionLength = utils_1.VersionUtils.getVisualLength(currentSection) + 1; // +1 for space before dashes
|
|
90
|
+
const currentPadding = Math.max(0, currentColumnWidth - currentSectionLength);
|
|
91
|
+
const currentWithPadding = currentSection + ' ' + dashColor('-').repeat(currentPadding);
|
|
92
|
+
// Range version section with fixed width
|
|
93
|
+
let rangeSection = '';
|
|
94
|
+
if (state.hasRangeUpdate) {
|
|
95
|
+
rangeSection = `${rangeDot} ${rangeVersionText}`;
|
|
96
|
+
const rangeSectionLength = utils_1.VersionUtils.getVisualLength(rangeSection) + 1; // +1 for space before dashes
|
|
97
|
+
const rangePadding = Math.max(0, rangeColumnWidth - rangeSectionLength);
|
|
98
|
+
rangeSection += ' ' + dashColor('-').repeat(rangePadding);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Empty slot - just spaces to maintain column width
|
|
102
|
+
rangeSection = ' '.repeat(rangeColumnWidth);
|
|
103
|
+
}
|
|
104
|
+
// Latest version section with fixed width
|
|
105
|
+
let latestSection = '';
|
|
106
|
+
if (state.hasMajorUpdate) {
|
|
107
|
+
latestSection = `${latestDot} ${latestVersionText}`;
|
|
108
|
+
const latestSectionLength = utils_1.VersionUtils.getVisualLength(latestSection) + 1; // +1 for space before dashes
|
|
109
|
+
const latestPadding = Math.max(0, latestColumnWidth - latestSectionLength);
|
|
110
|
+
latestSection += ' ' + dashColor('-').repeat(latestPadding);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Empty slot - just spaces to maintain column width
|
|
114
|
+
latestSection = ' '.repeat(latestColumnWidth);
|
|
115
|
+
}
|
|
116
|
+
// Build line with fixed column widths
|
|
117
|
+
const line = `${prefix}${packageNameSection} ${currentWithPadding} ${rangeSection} ${latestSection}`;
|
|
118
|
+
return line;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Render section header
|
|
122
|
+
*/
|
|
123
|
+
function renderSectionHeader(title, sectionType) {
|
|
124
|
+
const colorFn = sectionType === 'main' ? chalk_1.default.cyan : sectionType === 'peer' ? chalk_1.default.magenta : chalk_1.default.yellow;
|
|
125
|
+
return ' ' + colorFn.bold(title);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Render spacer
|
|
129
|
+
*/
|
|
130
|
+
function renderSpacer() {
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Render the main interface
|
|
135
|
+
*/
|
|
136
|
+
function renderInterface(states, currentRow, scrollOffset, maxVisibleItems, isInitialRender, renderableItems, dependencyTypeLabel, packageManager) {
|
|
137
|
+
const output = [];
|
|
138
|
+
// Header section (same for initial and incremental render)
|
|
139
|
+
if (packageManager) {
|
|
140
|
+
// Color map for each package manager - use their primary color for main text
|
|
141
|
+
const colorMap = {
|
|
142
|
+
npm: chalk_1.default.red,
|
|
143
|
+
yarn: chalk_1.default.blue,
|
|
144
|
+
pnpm: chalk_1.default.yellow,
|
|
145
|
+
bun: chalk_1.default.magenta,
|
|
146
|
+
};
|
|
147
|
+
const pmColor = colorMap[packageManager.name] || packageManager.color;
|
|
148
|
+
// Each character in "inup" gets a different color
|
|
149
|
+
const inupColors = [chalk_1.default.red, chalk_1.default.yellow, chalk_1.default.blue, chalk_1.default.magenta];
|
|
150
|
+
const coloredInup = inupColors.map((color, i) => color.bold('inup'[i])).join('');
|
|
151
|
+
output.push(' ' + chalk_1.default.bold(pmColor('🚀')) + ' ' + coloredInup + chalk_1.default.gray(` (${packageManager.displayName})`));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
output.push(' ' + chalk_1.default.bold.blue('🚀 ') + chalk_1.default.bold.red('i') + chalk_1.default.bold.yellow('n') + chalk_1.default.bold.blue('u') + chalk_1.default.bold.magenta('p'));
|
|
155
|
+
}
|
|
156
|
+
output.push('');
|
|
157
|
+
// Show dependency type if provided
|
|
158
|
+
if (dependencyTypeLabel) {
|
|
159
|
+
output.push(' ' + chalk_1.default.bold.cyan(dependencyTypeLabel));
|
|
160
|
+
output.push('');
|
|
161
|
+
}
|
|
162
|
+
output.push(' ' +
|
|
163
|
+
chalk_1.default.bold.white('↑/↓ ') +
|
|
164
|
+
chalk_1.default.gray('Move') +
|
|
165
|
+
' ' +
|
|
166
|
+
chalk_1.default.bold.white('←/→ ') +
|
|
167
|
+
chalk_1.default.gray('Select versions') +
|
|
168
|
+
' ' +
|
|
169
|
+
chalk_1.default.bold.white('I ') +
|
|
170
|
+
chalk_1.default.gray('Info') +
|
|
171
|
+
' ' +
|
|
172
|
+
chalk_1.default.bold.white('M ') +
|
|
173
|
+
chalk_1.default.gray('Select all minor') +
|
|
174
|
+
' ' +
|
|
175
|
+
chalk_1.default.bold.white('L ') +
|
|
176
|
+
chalk_1.default.gray('Select all') +
|
|
177
|
+
' ' +
|
|
178
|
+
chalk_1.default.bold.white('U ') +
|
|
179
|
+
chalk_1.default.gray('Unselect all'));
|
|
180
|
+
// Show status line with item range
|
|
181
|
+
const totalPackages = states.length;
|
|
182
|
+
// Use renderableItems length only if we have renderable items (grouped mode), otherwise use totalPackages (flat mode)
|
|
183
|
+
const totalVisualItems = renderableItems && renderableItems.length > 0 ? renderableItems.length : totalPackages;
|
|
184
|
+
const startItem = scrollOffset + 1;
|
|
185
|
+
const endItem = Math.min(scrollOffset + maxVisibleItems, totalVisualItems);
|
|
186
|
+
const statusLine = totalVisualItems > maxVisibleItems
|
|
187
|
+
? chalk_1.default.gray(`Showing ${chalk_1.default.gray(startItem)}-${chalk_1.default.gray(endItem)} of ${chalk_1.default.gray(totalPackages)} packages`) +
|
|
188
|
+
' ' +
|
|
189
|
+
chalk_1.default.gray('Enter ') +
|
|
190
|
+
chalk_1.default.gray('Confirm') +
|
|
191
|
+
' ' +
|
|
192
|
+
chalk_1.default.gray('Esc ') +
|
|
193
|
+
chalk_1.default.gray('Cancel')
|
|
194
|
+
: chalk_1.default.gray(`Showing all ${chalk_1.default.gray(totalPackages)} packages`) +
|
|
195
|
+
' ' +
|
|
196
|
+
chalk_1.default.gray('Enter ') +
|
|
197
|
+
chalk_1.default.gray('Confirm') +
|
|
198
|
+
' ' +
|
|
199
|
+
chalk_1.default.gray('Esc ') +
|
|
200
|
+
chalk_1.default.gray('Cancel');
|
|
201
|
+
output.push(' ' + statusLine);
|
|
202
|
+
output.push('');
|
|
203
|
+
// Render visible items
|
|
204
|
+
if (renderableItems && renderableItems.length > 0) {
|
|
205
|
+
// Use renderable items for grouped display
|
|
206
|
+
for (let i = scrollOffset; i < Math.min(scrollOffset + maxVisibleItems, renderableItems.length); i++) {
|
|
207
|
+
const item = renderableItems[i];
|
|
208
|
+
if (item.type === 'header') {
|
|
209
|
+
output.push(renderSectionHeader(item.title, item.sectionType));
|
|
210
|
+
}
|
|
211
|
+
else if (item.type === 'spacer') {
|
|
212
|
+
output.push(renderSpacer());
|
|
213
|
+
}
|
|
214
|
+
else if (item.type === 'package') {
|
|
215
|
+
const line = renderPackageLine(item.state, item.originalIndex, item.originalIndex === currentRow);
|
|
216
|
+
output.push(line);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Fallback to flat rendering (legacy mode)
|
|
222
|
+
for (let i = scrollOffset; i < Math.min(scrollOffset + maxVisibleItems, states.length); i++) {
|
|
223
|
+
const line = renderPackageLine(states[i], i, i === currentRow);
|
|
224
|
+
output.push(line);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return output;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Render packages table
|
|
231
|
+
*/
|
|
232
|
+
function renderPackagesTable(packages) {
|
|
233
|
+
if (packages.length === 0) {
|
|
234
|
+
return chalk_1.default.green('✅ All packages are up to date!');
|
|
235
|
+
}
|
|
236
|
+
const outdatedPackages = packages.filter((p) => p.isOutdated);
|
|
237
|
+
if (outdatedPackages.length === 0) {
|
|
238
|
+
return chalk_1.default.green('✅ All packages are up to date!');
|
|
239
|
+
}
|
|
240
|
+
// Just show a simple message, the interactive interface will handle the display
|
|
241
|
+
return chalk_1.default.bold.blue('🚀 inup\n');
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=package-list.js.map
|
package/dist/ui/state.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StateManager = void 0;
|
|
4
|
+
class StateManager {
|
|
5
|
+
constructor(initialRow = 0, terminalHeight = 24) {
|
|
6
|
+
this.headerLines = 7; // title + empty + label + empty + 1 instruction line + status + empty
|
|
7
|
+
this.uiState = {
|
|
8
|
+
currentRow: initialRow,
|
|
9
|
+
previousRow: -1,
|
|
10
|
+
scrollOffset: 0,
|
|
11
|
+
previousScrollOffset: 0,
|
|
12
|
+
maxVisibleItems: Math.max(5, terminalHeight - this.headerLines - 2),
|
|
13
|
+
terminalHeight,
|
|
14
|
+
isInitialRender: true,
|
|
15
|
+
renderedLines: [],
|
|
16
|
+
renderableItems: [],
|
|
17
|
+
showInfoModal: false,
|
|
18
|
+
infoModalRow: -1,
|
|
19
|
+
isLoadingModalInfo: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
getUIState() {
|
|
23
|
+
return { ...this.uiState };
|
|
24
|
+
}
|
|
25
|
+
setRenderableItems(items) {
|
|
26
|
+
this.uiState.renderableItems = items;
|
|
27
|
+
}
|
|
28
|
+
// Convert package index to visual row index in renderable items
|
|
29
|
+
packageIndexToVisualIndex(packageIndex) {
|
|
30
|
+
// If no renderable items (flat mode), visual index equals package index
|
|
31
|
+
if (this.uiState.renderableItems.length === 0) {
|
|
32
|
+
return packageIndex;
|
|
33
|
+
}
|
|
34
|
+
// Otherwise search in renderable items (grouped mode)
|
|
35
|
+
for (let i = 0; i < this.uiState.renderableItems.length; i++) {
|
|
36
|
+
const item = this.uiState.renderableItems[i];
|
|
37
|
+
if (item.type === 'package' && item.originalIndex === packageIndex) {
|
|
38
|
+
return i;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
// Find the next navigable package index in the given direction
|
|
44
|
+
findNextPackageIndex(currentPackageIndex, direction, totalPackages) {
|
|
45
|
+
if (this.uiState.renderableItems.length === 0) {
|
|
46
|
+
// Fallback to simple navigation if no renderable items
|
|
47
|
+
if (direction === 'up') {
|
|
48
|
+
return currentPackageIndex <= 0 ? totalPackages - 1 : currentPackageIndex - 1;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return currentPackageIndex >= totalPackages - 1 ? 0 : currentPackageIndex + 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Find current visual index
|
|
55
|
+
const currentVisualIndex = this.packageIndexToVisualIndex(currentPackageIndex);
|
|
56
|
+
// Get all package items with their visual indices
|
|
57
|
+
const packageItems = [];
|
|
58
|
+
for (let i = 0; i < this.uiState.renderableItems.length; i++) {
|
|
59
|
+
const item = this.uiState.renderableItems[i];
|
|
60
|
+
if (item.type === 'package') {
|
|
61
|
+
packageItems.push({ visualIndex: i, packageIndex: item.originalIndex });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (packageItems.length === 0)
|
|
65
|
+
return currentPackageIndex;
|
|
66
|
+
// Find current position in packageItems
|
|
67
|
+
const currentPos = packageItems.findIndex((p) => p.packageIndex === currentPackageIndex);
|
|
68
|
+
if (currentPos === -1)
|
|
69
|
+
return packageItems[0].packageIndex;
|
|
70
|
+
// Navigate with wrap-around
|
|
71
|
+
if (direction === 'up') {
|
|
72
|
+
const newPos = currentPos <= 0 ? packageItems.length - 1 : currentPos - 1;
|
|
73
|
+
return packageItems[newPos].packageIndex;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const newPos = currentPos >= packageItems.length - 1 ? 0 : currentPos + 1;
|
|
77
|
+
return packageItems[newPos].packageIndex;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
updateTerminalHeight(newHeight) {
|
|
81
|
+
const newMaxVisibleItems = Math.max(5, newHeight - this.headerLines - 2);
|
|
82
|
+
if (newHeight !== this.uiState.terminalHeight ||
|
|
83
|
+
newMaxVisibleItems !== this.uiState.maxVisibleItems) {
|
|
84
|
+
this.uiState.terminalHeight = newHeight;
|
|
85
|
+
this.uiState.maxVisibleItems = newMaxVisibleItems;
|
|
86
|
+
return true; // Changed
|
|
87
|
+
}
|
|
88
|
+
return false; // No change
|
|
89
|
+
}
|
|
90
|
+
navigateUp(totalItems) {
|
|
91
|
+
this.uiState.previousRow = this.uiState.currentRow;
|
|
92
|
+
this.uiState.currentRow = this.findNextPackageIndex(this.uiState.currentRow, 'up', totalItems);
|
|
93
|
+
this.ensureVisible(this.uiState.currentRow, totalItems);
|
|
94
|
+
}
|
|
95
|
+
navigateDown(totalItems) {
|
|
96
|
+
this.uiState.previousRow = this.uiState.currentRow;
|
|
97
|
+
this.uiState.currentRow = this.findNextPackageIndex(this.uiState.currentRow, 'down', totalItems);
|
|
98
|
+
this.ensureVisible(this.uiState.currentRow, totalItems);
|
|
99
|
+
}
|
|
100
|
+
ensureVisible(packageIndex, totalPackages) {
|
|
101
|
+
// Convert package index to visual index for scrolling
|
|
102
|
+
const visualIndex = this.packageIndexToVisualIndex(packageIndex);
|
|
103
|
+
const totalVisualItems = this.uiState.renderableItems.length || totalPackages;
|
|
104
|
+
// Try to show section header if the current item is just below a header
|
|
105
|
+
let targetVisualIndex = visualIndex;
|
|
106
|
+
if (visualIndex > 0) {
|
|
107
|
+
const prevItem = this.uiState.renderableItems[visualIndex - 1];
|
|
108
|
+
if (prevItem?.type === 'header') {
|
|
109
|
+
targetVisualIndex = visualIndex - 1;
|
|
110
|
+
}
|
|
111
|
+
else if (visualIndex > 1) {
|
|
112
|
+
// Also check for spacer + header combo (for first package in non-first section)
|
|
113
|
+
const prevPrevItem = this.uiState.renderableItems[visualIndex - 2];
|
|
114
|
+
if (prevItem?.type === 'spacer' && prevPrevItem?.type === 'header') {
|
|
115
|
+
// Show spacer and header if possible
|
|
116
|
+
targetVisualIndex = Math.max(0, visualIndex - 2);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Scrolling up: show from targetVisualIndex (includes headers if applicable)
|
|
121
|
+
if (targetVisualIndex < this.uiState.scrollOffset) {
|
|
122
|
+
this.uiState.scrollOffset = targetVisualIndex;
|
|
123
|
+
}
|
|
124
|
+
// Scrolling down: ensure the package is visible, but prefer showing context if possible
|
|
125
|
+
else if (visualIndex >= this.uiState.scrollOffset + this.uiState.maxVisibleItems) {
|
|
126
|
+
// Calculate how many items we need to show from targetVisualIndex to visualIndex
|
|
127
|
+
const rangeSize = visualIndex - targetVisualIndex + 1;
|
|
128
|
+
if (rangeSize <= this.uiState.maxVisibleItems) {
|
|
129
|
+
// We can fit the context (header/spacer) and the package, so show from targetVisualIndex
|
|
130
|
+
this.uiState.scrollOffset = targetVisualIndex;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Not enough room for context, position package at bottom of viewport
|
|
134
|
+
this.uiState.scrollOffset = visualIndex - this.uiState.maxVisibleItems + 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Ensure scrollOffset doesn't go negative or beyond bounds
|
|
138
|
+
this.uiState.scrollOffset = Math.max(0, Math.min(this.uiState.scrollOffset, Math.max(0, totalVisualItems - this.uiState.maxVisibleItems)));
|
|
139
|
+
}
|
|
140
|
+
updateSelection(states, direction) {
|
|
141
|
+
const currentState = states[this.uiState.currentRow];
|
|
142
|
+
if (direction === 'left') {
|
|
143
|
+
// Move selection left with wraparound: latest -> range -> none -> latest
|
|
144
|
+
if (currentState.selectedOption === 'latest') {
|
|
145
|
+
if (currentState.hasRangeUpdate) {
|
|
146
|
+
currentState.selectedOption = 'range';
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
currentState.selectedOption = 'none';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (currentState.selectedOption === 'range') {
|
|
153
|
+
currentState.selectedOption = 'none';
|
|
154
|
+
}
|
|
155
|
+
else if (currentState.selectedOption === 'none') {
|
|
156
|
+
// Wrap around to the last available option
|
|
157
|
+
if (currentState.hasMajorUpdate) {
|
|
158
|
+
currentState.selectedOption = 'latest';
|
|
159
|
+
}
|
|
160
|
+
else if (currentState.hasRangeUpdate) {
|
|
161
|
+
currentState.selectedOption = 'range';
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// Move selection right with wraparound: none -> range -> latest -> none
|
|
167
|
+
if (currentState.selectedOption === 'none') {
|
|
168
|
+
if (currentState.hasRangeUpdate) {
|
|
169
|
+
currentState.selectedOption = 'range';
|
|
170
|
+
}
|
|
171
|
+
else if (currentState.hasMajorUpdate) {
|
|
172
|
+
currentState.selectedOption = 'latest';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else if (currentState.selectedOption === 'range') {
|
|
176
|
+
if (currentState.hasMajorUpdate) {
|
|
177
|
+
currentState.selectedOption = 'latest';
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Wrap around to none
|
|
181
|
+
currentState.selectedOption = 'none';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (currentState.selectedOption === 'latest') {
|
|
185
|
+
// Wrap around to none
|
|
186
|
+
currentState.selectedOption = 'none';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
bulkSelectMinor(states) {
|
|
191
|
+
states.forEach((state) => {
|
|
192
|
+
if (state.hasRangeUpdate) {
|
|
193
|
+
state.selectedOption = 'range';
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
bulkSelectLatest(states) {
|
|
198
|
+
states.forEach((state) => {
|
|
199
|
+
if (state.hasMajorUpdate) {
|
|
200
|
+
state.selectedOption = 'latest';
|
|
201
|
+
}
|
|
202
|
+
else if (state.hasRangeUpdate) {
|
|
203
|
+
state.selectedOption = 'range';
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
bulkUnselectAll(states) {
|
|
208
|
+
states.forEach((state) => {
|
|
209
|
+
state.selectedOption = 'none';
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
markRendered(renderedLines) {
|
|
213
|
+
this.uiState.renderedLines = renderedLines;
|
|
214
|
+
this.uiState.previousRow = this.uiState.currentRow;
|
|
215
|
+
this.uiState.previousScrollOffset = this.uiState.scrollOffset;
|
|
216
|
+
}
|
|
217
|
+
setInitialRender(isInitial) {
|
|
218
|
+
this.uiState.isInitialRender = isInitial;
|
|
219
|
+
}
|
|
220
|
+
resetForResize() {
|
|
221
|
+
const totalItems = this.uiState.renderableItems.length || this.uiState.maxVisibleItems;
|
|
222
|
+
this.ensureVisible(this.uiState.currentRow, totalItems);
|
|
223
|
+
this.uiState.isInitialRender = true;
|
|
224
|
+
}
|
|
225
|
+
toggleInfoModal() {
|
|
226
|
+
if (this.uiState.showInfoModal) {
|
|
227
|
+
// Close the modal
|
|
228
|
+
this.uiState.showInfoModal = false;
|
|
229
|
+
this.uiState.infoModalRow = -1;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Open the modal for the current package
|
|
233
|
+
this.uiState.showInfoModal = true;
|
|
234
|
+
this.uiState.infoModalRow = this.uiState.currentRow;
|
|
235
|
+
}
|
|
236
|
+
this.uiState.isInitialRender = true;
|
|
237
|
+
}
|
|
238
|
+
closeInfoModal() {
|
|
239
|
+
this.uiState.showInfoModal = false;
|
|
240
|
+
this.uiState.infoModalRow = -1;
|
|
241
|
+
this.uiState.isLoadingModalInfo = false;
|
|
242
|
+
this.uiState.isInitialRender = true;
|
|
243
|
+
}
|
|
244
|
+
setModalLoading(isLoading) {
|
|
245
|
+
this.uiState.isLoadingModalInfo = isLoading;
|
|
246
|
+
this.uiState.isInitialRender = true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
exports.StateManager = StateManager;
|
|
250
|
+
//# sourceMappingURL=state.js.map
|