@varlock/bumpy 1.13.0 → 1.13.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.
@@ -24,6 +24,10 @@ const LEVELS = [
24
24
  * - Changed packages default to "patch", unchanged to "none"
25
25
  * - Enter to confirm
26
26
  * - Ctrl+C / Escape to cancel
27
+ *
28
+ * Renders a viewport that fits within the terminal so the list scrolls instead of
29
+ * overflowing — otherwise large package counts cause the redraw cursor-up to lose
30
+ * its anchor once content scrolls off-screen.
27
31
  */
28
32
  async function bumpSelectPrompt(items) {
29
33
  const changedEntries = items.map((item, idx) => ({
@@ -35,7 +39,44 @@ async function bumpSelectPrompt(items) {
35
39
  idx
36
40
  })).filter(({ item }) => !item.changed);
37
41
  const displayOrder = [...changedEntries, ...unchangedEntries];
42
+ const rows = [];
43
+ const itemRowIndex = [];
44
+ {
45
+ let displayIdx = 0;
46
+ if (changedEntries.length > 0) {
47
+ rows.push({
48
+ kind: "header",
49
+ text: "Changed"
50
+ });
51
+ for (const { idx } of changedEntries) {
52
+ itemRowIndex.push(rows.length);
53
+ rows.push({
54
+ kind: "item",
55
+ itemIdx: idx,
56
+ displayIdx
57
+ });
58
+ displayIdx++;
59
+ }
60
+ if (unchangedEntries.length > 0) rows.push({ kind: "separator" });
61
+ }
62
+ if (unchangedEntries.length > 0) {
63
+ rows.push({
64
+ kind: "header",
65
+ text: "Unchanged"
66
+ });
67
+ for (const { idx } of unchangedEntries) {
68
+ itemRowIndex.push(rows.length);
69
+ rows.push({
70
+ kind: "item",
71
+ itemIdx: idx,
72
+ displayIdx
73
+ });
74
+ displayIdx++;
75
+ }
76
+ }
77
+ }
38
78
  let cursor = 0;
79
+ let scroll = 0;
39
80
  const levels = items.map((item) => item.initialLevel !== void 0 ? item.initialLevel : item.changed ? "patch" : "skip");
40
81
  return new Promise((resolve) => {
41
82
  const { stdin, stdout } = process;
@@ -57,38 +98,77 @@ async function bumpSelectPrompt(items) {
57
98
  if (selected.length === 0) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("(none selected)")}`);
58
99
  else for (const { item, idx } of selected) lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.cyan(item.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(levels[idx])}`);
59
100
  lines.push(import_picocolors.default.dim("│"));
101
+ const output = lines.join("\n") + "\n";
102
+ stdout.write(output);
103
+ renderedLines = lines.length;
104
+ return;
105
+ }
106
+ const headerChrome = [
107
+ `${import_picocolors.default.cyan("◆")} Select bump levels`,
108
+ `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("↑/↓ navigate · ←/→ change level · enter to confirm")}`,
109
+ `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("0 skip current · x skip all · r reset all to defaults")}`,
110
+ import_picocolors.default.dim("│")
111
+ ];
112
+ const selectedCount = levels.filter((l) => l !== "skip").length;
113
+ const footerChrome = [
114
+ import_picocolors.default.dim("│"),
115
+ `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`${selectedCount} package${selectedCount !== 1 ? "s" : ""} selected`)}`,
116
+ import_picocolors.default.dim("└")
117
+ ];
118
+ const termRows = stdout.rows || 24;
119
+ const chromeLines = headerChrome.length + footerChrome.length;
120
+ const MIN_BODY = 3;
121
+ const availableBody = Math.max(MIN_BODY, termRows - chromeLines - 1);
122
+ let visibleRows;
123
+ let topIndicator = null;
124
+ let bottomIndicator = null;
125
+ let stickyHeader = null;
126
+ if (rows.length <= availableBody) {
127
+ visibleRows = rows;
128
+ scroll = 0;
60
129
  } else {
61
- lines.push(`${import_picocolors.default.cyan("◆")} Select bump levels`);
62
- lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("↑/↓ navigate · ←/→ change level · enter to confirm")}`);
63
- lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim("0 skip current · x skip all · r reset all to defaults")}`);
64
- lines.push(import_picocolors.default.dim("│"));
65
- let displayIdx = 0;
66
- if (changedEntries.length > 0) {
67
- lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.underline("Changed")}`);
68
- for (const { item, idx } of changedEntries) {
69
- lines.push(formatRow(item, levels[idx], cursor === displayIdx));
70
- displayIdx++;
71
- }
72
- if (unchangedEntries.length > 0) lines.push(import_picocolors.default.dim("│"));
130
+ let windowSize = Math.max(MIN_BODY, availableBody - 2);
131
+ const focusedRowIdx = itemRowIndex[cursor];
132
+ const adjustScroll = () => {
133
+ if (focusedRowIdx < scroll) scroll = focusedRowIdx;
134
+ else if (focusedRowIdx >= scroll + windowSize) scroll = focusedRowIdx - windowSize + 1;
135
+ scroll = Math.max(0, Math.min(scroll, rows.length - windowSize));
136
+ };
137
+ adjustScroll();
138
+ const section = getCurrentSection(cursor, changedEntries.length, unchangedEntries.length);
139
+ if (section !== null && section.headerRowIdx < scroll) {
140
+ windowSize = Math.max(MIN_BODY, windowSize - 1);
141
+ adjustScroll();
142
+ stickyHeader = `${import_picocolors.default.dim("│")} ${import_picocolors.default.underline(section.name)}`;
73
143
  }
74
- if (unchangedEntries.length > 0) {
75
- lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.underline("Unchanged")}`);
76
- for (const { item, idx } of unchangedEntries) {
77
- lines.push(formatRow(item, levels[idx], cursor === displayIdx));
78
- displayIdx++;
79
- }
80
- }
81
- lines.push(import_picocolors.default.dim("│"));
82
- const selectedCount = levels.filter((l) => l !== "skip").length;
83
- lines.push(`${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`${selectedCount} package${selectedCount !== 1 ? "s" : ""} selected`)}`);
84
- lines.push(`${import_picocolors.default.dim("")}`);
144
+ visibleRows = rows.slice(scroll, scroll + windowSize);
145
+ const above = scroll;
146
+ const below = rows.length - (scroll + windowSize);
147
+ if (above > 0) topIndicator = `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`▲ ${above} more`)}`;
148
+ if (below > 0) bottomIndicator = `${import_picocolors.default.dim("│")} ${import_picocolors.default.dim(`▼ ${below} more`)}`;
149
+ }
150
+ lines.push(...headerChrome);
151
+ if (topIndicator !== null) lines.push(topIndicator);
152
+ if (stickyHeader !== null) lines.push(stickyHeader);
153
+ for (const row of visibleRows) if (row.kind === "separator") lines.push(import_picocolors.default.dim("│"));
154
+ else if (row.kind === "header") lines.push(`${import_picocolors.default.dim("")} ${import_picocolors.default.underline(row.text)}`);
155
+ else {
156
+ const item = items[row.itemIdx];
157
+ const isFocused = row.displayIdx === cursor;
158
+ lines.push(formatRow(item, levels[row.itemIdx], isFocused));
85
159
  }
160
+ if (bottomIndicator !== null) lines.push(bottomIndicator);
161
+ lines.push(...footerChrome);
86
162
  const output = lines.join("\n") + "\n";
87
163
  stdout.write(output);
88
164
  renderedLines = lines.length;
89
165
  }
166
+ function onResize() {
167
+ render();
168
+ }
90
169
  function cleanup() {
91
170
  stdin.removeListener("keypress", onKeypress);
171
+ stdout.removeListener("resize", onResize);
92
172
  rl.close();
93
173
  stdout.write("\x1B[?25h");
94
174
  if (stdin.isTTY) stdin.setRawMode(false);
@@ -141,8 +221,24 @@ async function bumpSelectPrompt(items) {
141
221
  render();
142
222
  }
143
223
  stdin.on("keypress", onKeypress);
224
+ stdout.on("resize", onResize);
144
225
  });
145
226
  }
227
+ /** Returns the section the focused item is in, plus the row index of its header. */
228
+ function getCurrentSection(cursor, changedCount, unchangedCount) {
229
+ if (cursor < changedCount) {
230
+ if (changedCount === 0) return null;
231
+ return {
232
+ headerRowIdx: 0,
233
+ name: "Changed"
234
+ };
235
+ }
236
+ if (unchangedCount === 0) return null;
237
+ return {
238
+ headerRowIdx: changedCount > 0 ? changedCount + 2 : 0,
239
+ name: "Unchanged"
240
+ };
241
+ }
146
242
  function formatRow(item, level, focused) {
147
243
  return `${import_picocolors.default.dim("│")} ${focused ? import_picocolors.default.cyan("›") : " "} ${focused ? import_picocolors.default.cyan(item.name) : item.name} ${import_picocolors.default.dim(`(${item.version})`)} ${formatLevel(level, focused)}`;
148
244
  }
package/dist/cli.mjs CHANGED
@@ -31,7 +31,7 @@ async function main() {
31
31
  }
32
32
  case "add": {
33
33
  const rootDir = await findRoot();
34
- const { addCommand } = await import("./add-DQA1TVHA.mjs");
34
+ const { addCommand } = await import("./add-5how2kia.mjs");
35
35
  await addCommand(rootDir, {
36
36
  packages: flags.packages,
37
37
  message: flags.message,
@@ -151,7 +151,7 @@ async function main() {
151
151
  }
152
152
  case "--version":
153
153
  case "-v":
154
- console.log(`bumpy 1.13.0`);
154
+ console.log(`bumpy 1.13.1`);
155
155
  break;
156
156
  case "help":
157
157
  case "--help":
@@ -171,7 +171,7 @@ async function main() {
171
171
  }
172
172
  function printHelp() {
173
173
  console.log(`
174
- ${colorize(`🐸 bumpy v1.13.0`, "bold")} - Modern monorepo versioning
174
+ ${colorize(`🐸 bumpy v1.13.1`, "bold")} - Modern monorepo versioning
175
175
 
176
176
  Usage: bumpy <command> [options]
177
177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlock/bumpy",
3
- "version": "1.13.0",
3
+ "version": "1.13.1",
4
4
  "description": "Modern monorepo versioning and changelog tool",
5
5
  "keywords": [
6
6
  "bump",