promptgraph-mcp 2.2.4 → 2.2.6

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 (2) hide show
  1. package/package.json +1 -1
  2. package/tui.js +28 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.2.4",
3
+ "version": "2.2.6",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/tui.js CHANGED
@@ -85,7 +85,7 @@ function render(state, installedSet = new Set()) {
85
85
  const skills = items.filter(i => i.type === 'skill').length;
86
86
  const bundles = items.filter(i => i.type === 'bundle').length;
87
87
 
88
- write('\x1b[H\x1b[J'); // go home + clear to end (no flicker vs full CLEAR)
88
+ write('\x1b[H'); // go home screen already cleared on init, just reposition
89
89
 
90
90
  // ── header ─────────────────────────────────────────────────────────────────
91
91
  // Row 1: title bar
@@ -153,9 +153,9 @@ function render(state, installedSet = new Set()) {
153
153
  ? item.skillCount
154
154
  ? blue((item.skillCount + ' sk').padEnd(8))
155
155
  : item.repo_url
156
- ? blue('GitHub ')
156
+ ? chalk.hex('#3B82F6')('↗ GitHub')
157
157
  : dim(((item.skills?.length || 0) + ' sk').padEnd(8))
158
- : dim((item.code || '').padEnd(8));
158
+ : chalk.hex('#A78BFA')((item.code || '').padEnd(10));
159
159
  const desc = dim(truncate(item.description, Math.max(10, DESC_W)));
160
160
 
161
161
  write(bg + ` ${arrow} ${type} ${badge} ${nameCol} ${extra} ${desc}` + reset + CLEAR_EOL + '\n');
@@ -188,16 +188,37 @@ function render(state, installedSet = new Set()) {
188
188
 
189
189
  // ── clamp scroll ─────────────────────────────────────────────────────────────
190
190
 
191
+ // Count how many screen rows items[start..end] occupy (including category headers)
192
+ function countVisibleRows(items, start, end) {
193
+ let rows = 0;
194
+ let lastCat = start > 0 ? items[start - 1]?.category : null;
195
+ for (let i = start; i < end && i < items.length; i++) {
196
+ if (items[i].category !== lastCat) { rows++; lastCat = items[i].category; }
197
+ rows++;
198
+ }
199
+ return rows;
200
+ }
201
+
191
202
  function clampScroll(state) {
192
203
  const { rows } = termSize();
193
204
  const HEADER_ROWS = 4, FOOTER_ROWS = 3;
194
205
  const LIST_ROWS = rows - HEADER_ROWS - FOOTER_ROWS;
195
206
  const { cursor, items } = state;
196
207
 
197
- // ensure cursor visible — approximate (category headers add extra rows)
198
- if (cursor < state.scroll) state.scroll = cursor;
199
- if (cursor >= state.scroll + LIST_ROWS - 2) state.scroll = cursor - LIST_ROWS + 3;
200
208
  if (state.scroll < 0) state.scroll = 0;
209
+ if (state.scroll > cursor) state.scroll = cursor;
210
+
211
+ // Check if cursor is visible from current scroll
212
+ const visibleRows = countVisibleRows(items, state.scroll, cursor + 1);
213
+ if (visibleRows > LIST_ROWS - 1) {
214
+ // Cursor is below visible area — scroll forward until it fits
215
+ while (state.scroll < cursor) {
216
+ state.scroll++;
217
+ const v = countVisibleRows(items, state.scroll, cursor + 1);
218
+ if (v <= LIST_ROWS - 1) break;
219
+ }
220
+ }
221
+
201
222
  if (state.scroll >= items.length) state.scroll = Math.max(0, items.length - 1);
202
223
  }
203
224
 
@@ -225,7 +246,7 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
225
246
  }
226
247
 
227
248
  // Setup terminal
228
- write(HIDE + CLEAR);
249
+ write(HIDE + CLEAR + '\x1b[H\x1b[J');
229
250
  readline.emitKeypressEvents(process.stdin);
230
251
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
231
252