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.
@@ -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
@@ -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