@pi-unipi/mcp 0.1.0 → 0.1.7

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.
@@ -199,8 +199,8 @@ export function renderMcpSettingsOverlay(params?: {
199
199
  return;
200
200
  }
201
201
 
202
- // Navigation
203
- if (matchesKey(data, Key.up)) {
202
+ // Navigation (arrows + vim j/k)
203
+ if (matchesKey(data, Key.up) || data === "k") {
204
204
  if (state.selectedIndex > 0) {
205
205
  state.selectedIndex--;
206
206
  refresh();
@@ -208,7 +208,7 @@ export function renderMcpSettingsOverlay(params?: {
208
208
  return;
209
209
  }
210
210
 
211
- if (matchesKey(data, Key.down)) {
211
+ if (matchesKey(data, Key.down) || data === "j") {
212
212
  if (state.selectedIndex < state.servers.length - 1) {
213
213
  state.selectedIndex++;
214
214
  refresh();
@@ -267,35 +267,38 @@ export function renderMcpSettingsOverlay(params?: {
267
267
  }
268
268
  }
269
269
 
270
+ /** Pad content to exact visible width, accounting for ANSI codes and emoji */
271
+ function padVisible(content: string, targetWidth: number): string {
272
+ const vw = visibleWidth(content);
273
+ const pad = Math.max(0, targetWidth - vw);
274
+ return content + " ".repeat(pad);
275
+ }
276
+
270
277
  function render(width: number): string[] {
271
278
  if (cachedLines) return cachedLines;
272
279
 
273
280
  const lines: string[] = [];
281
+ const innerWidth = Math.max(22, width - 2);
274
282
 
275
- // Header
283
+ // ── Header ──────────────────────────────────────────────────────
276
284
  const header = " MCP Settings ";
277
285
  const scopeLabel = state.viewScope === "global" ? "● Global" : "● Project";
286
+ const headerPad = Math.max(0, innerWidth - visibleWidth(header) - visibleWidth(scopeLabel));
287
+ lines.push(theme.fg("accent", `╭${"─".repeat(innerWidth)}╮`));
278
288
  lines.push(
279
- theme.accent(`╭${"".repeat(Math.max(0, width - 2))}╮`),
280
- );
281
- lines.push(
282
- theme.accent("│") +
283
- theme.bold(header) +
284
- theme.accent(
285
- scopeLabel.padStart(width - visibleWidth(header) - visibleWidth(scopeLabel) - 1),
286
- ) +
287
- theme.accent("│"),
288
- );
289
- lines.push(
290
- theme.accent(`├${"─".repeat(Math.max(0, width - 2))}┤`),
289
+ theme.fg("accent", "│") +
290
+ theme.bold(header) +
291
+ theme.fg("accent", " ".repeat(headerPad) + scopeLabel) +
292
+ theme.fg("accent", "│"),
291
293
  );
294
+ lines.push(theme.fg("accent", `├${"─".repeat(innerWidth)}┤`));
292
295
 
293
- // Server list
296
+ // ── Server list ─────────────────────────────────────────────────
294
297
  if (state.servers.length === 0) {
295
298
  lines.push(
296
- theme.accent("│") +
297
- theme.muted(" No servers configured".padEnd(width - 2)) +
298
- theme.accent("│"),
299
+ theme.fg("accent", "│") +
300
+ padVisible(theme.fg("muted", " No servers configured"), innerWidth) +
301
+ theme.fg("accent", "│"),
299
302
  );
300
303
  } else {
301
304
  for (let i = 0; i < state.servers.length; i++) {
@@ -304,61 +307,53 @@ export function renderMcpSettingsOverlay(params?: {
304
307
 
305
308
  const statusIcon =
306
309
  server.status === "running"
307
- ? theme.success("●")
310
+ ? theme.fg("success", "●")
308
311
  : server.status === "error"
309
- ? theme.error("✗")
312
+ ? theme.fg("error", "✗")
310
313
  : server.enabled
311
- ? theme.muted("○")
312
- : theme.dim("○");
314
+ ? theme.fg("muted", "○")
315
+ : theme.fg("dim", "○");
313
316
 
314
- const name = selected ? theme.bold(server.name) : theme.fg("default", server.name);
315
- const cmd = theme.muted(truncateToWidth(server.command, 24));
317
+ const name = selected ? theme.bold(server.name) : theme.fg("text", server.name);
318
+ const cmd = theme.fg("muted", truncateToWidth(server.command, 24));
316
319
  const tools =
317
320
  server.status === "running" && server.toolCount > 0
318
- ? theme.accent(`${server.toolCount} tools`)
321
+ ? theme.fg("accent", `${server.toolCount} tools`)
319
322
  : server.status === "error" && server.error
320
- ? theme.error(truncateToWidth(server.error, 20))
321
- : theme.dim("stopped");
323
+ ? theme.fg("error", truncateToWidth(server.error, 20))
324
+ : theme.fg("dim", "stopped");
322
325
 
323
- const source = theme.muted(`[${server.source}]`);
324
- const prefix = selected ? theme.accent("▸ ") : " ";
326
+ const source = theme.fg("muted", `[${server.source}]`);
327
+ const prefix = selected ? theme.fg("accent", "▸ ") : " ";
325
328
 
326
329
  const line = ` ${prefix}${statusIcon} ${name} ${cmd} ${tools} ${source}`;
327
330
  lines.push(
328
- theme.accent("│") +
329
- truncateToWidth(line, width - 2).padEnd(width - 2) +
330
- theme.accent("│"),
331
+ theme.fg("accent", "│") +
332
+ padVisible(truncateToWidth(line, innerWidth), innerWidth) +
333
+ theme.fg("accent", "│"),
331
334
  );
332
335
  }
333
336
  }
334
337
 
335
- // Confirm delete overlay
338
+ // ── Confirm delete ─────────────────────────────────────────────
336
339
  if (state.confirmDelete) {
340
+ lines.push(theme.fg("accent", `├${"─".repeat(innerWidth)}┤`));
337
341
  lines.push(
338
- theme.accent(`├${"".repeat(Math.max(0, width - 2))}┤`),
339
- );
340
- lines.push(
341
- theme.accent("│") +
342
- theme.warning(
343
- ` Delete '${state.confirmDelete}'? (y/n)`.padEnd(width - 2),
344
- ) +
345
- theme.accent("│"),
342
+ theme.fg("accent", "│") +
343
+ padVisible(theme.fg("warning", ` Delete '${state.confirmDelete}'? (y/n)`), innerWidth) +
344
+ theme.fg("accent", "│"),
346
345
  );
347
346
  }
348
347
 
349
- // Keybinds
350
- lines.push(
351
- theme.accent(`├${"─".repeat(Math.max(0, width - 2))}┤`),
352
- );
348
+ // ── Keybinds ───────────────────────────────────────────────────
349
+ lines.push(theme.fg("accent", `├${"─".repeat(innerWidth)}┤`));
353
350
  const binds = " ↑↓ select Space toggle a add s sync g global p project d delete q/Esc close";
354
351
  lines.push(
355
- theme.accent("│") +
356
- theme.muted(truncateToWidth(binds, width - 2).padEnd(width - 2)) +
357
- theme.accent("│"),
358
- );
359
- lines.push(
360
- theme.accent(`╰${"─".repeat(Math.max(0, width - 2))}╯`),
352
+ theme.fg("accent", "│") +
353
+ padVisible(theme.fg("muted", truncateToWidth(binds, innerWidth)), innerWidth) +
354
+ theme.fg("accent", "│"),
361
355
  );
356
+ lines.push(theme.fg("accent", `╰${"─".repeat(innerWidth)}╯`));
362
357
 
363
358
  cachedLines = lines;
364
359
  return lines;