@pi-unipi/info-screen 0.1.4 → 0.1.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.
package/core-groups.ts CHANGED
@@ -367,6 +367,7 @@ export function registerCoreGroups(): void {
367
367
  { id: "cwd", label: "Working Directory", show: true },
368
368
  { id: "modules", label: "Active Modules", show: true },
369
369
  { id: "uptime", label: "Session Uptime", show: true },
370
+ { id: "loadTime", label: "Total Load Time", show: true },
370
371
  ],
371
372
  },
372
373
  dataProvider: async () => {
@@ -376,6 +377,7 @@ export function registerCoreGroups(): void {
376
377
 
377
378
  const modules = getAnnouncedModules();
378
379
  const moduleNames = modules.map((m) => m.name.replace(/^@[^/]+\//, ""));
380
+ const totalLoadTime = getTotalLoadTime();
379
381
 
380
382
  return {
381
383
  version: { value: getPiVersion(), detail: "pi" },
@@ -385,43 +387,7 @@ export function registerCoreGroups(): void {
385
387
  detail: moduleNames.slice(0, 4).join(", ") + (moduleNames.length > 4 ? ` +${moduleNames.length - 4} more` : ""),
386
388
  },
387
389
  uptime: { value: formatUptime(process.uptime()) },
388
- };
389
- },
390
- });
391
-
392
- // 1b. Load time group
393
- infoRegistry.registerGroup({
394
- id: "loadtime",
395
- name: "Load Time",
396
- icon: "⏱️",
397
- priority: 15,
398
- config: {
399
- showByDefault: true,
400
- stats: [
401
- { id: "total", label: "Total Load Time", show: true },
402
- { id: "count", label: "Items Loaded", show: true },
403
- { id: "list", label: "Load Times", show: true },
404
- ],
405
- },
406
- dataProvider: async () => {
407
- const times = getLoadTimes();
408
- const total = getTotalLoadTime();
409
-
410
- // Sort by load time descending
411
- const sorted = [...times].sort((a, b) => b.ms - a.ms);
412
-
413
- // Build list as comma-separated values
414
- const listStr = sorted.length > 0
415
- ? sorted.map(t => `${t.name} (${t.ms}ms)`).join(", ")
416
- : "none";
417
-
418
- return {
419
- total: { value: `${total}ms` },
420
- count: { value: String(times.length) },
421
- list: {
422
- value: sorted.length > 0 ? `${sorted[0].name} (${sorted[0].ms}ms)` : "none",
423
- detail: sorted.length > 1 ? sorted.slice(1).map(t => `${t.name} (${t.ms}ms)`).join(", ") : undefined,
424
- },
390
+ loadTime: { value: `${totalLoadTime}ms` },
425
391
  };
426
392
  },
427
393
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/info-screen",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Dashboard and module registry for Unipi — configurable info overlay with tabbed groups",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,7 @@ export class SettingsOverlay implements Component {
35
35
  private settings: InfoScreenSettings;
36
36
  private groups: Array<{ id: string; name: string; icon: string }>;
37
37
  private selectedIndex = 0;
38
+ private savedGroupIndex = 0; // Saved position before entering stats mode
38
39
  private mode: "groups" | "stats" = "groups";
39
40
  private selectedGroupId: string | null = null;
40
41
  /** Callback when overlay should close */
@@ -90,9 +91,9 @@ export class SettingsOverlay implements Component {
90
91
  this.selectedIndex = (this.selectedIndex + 1) % this.groups.length;
91
92
  break;
92
93
  case " ": // Space - toggle visibility
93
- case "\r": // Enter - toggle visibility
94
94
  this.toggleGroupVisibility(this.groups[this.selectedIndex].id);
95
95
  break;
96
+ case "\r": // Enter - enter stats mode
96
97
  case "\x1b[C": // Right - enter stats mode
97
98
  case "l":
98
99
  this.enterStatsMode(this.groups[this.selectedIndex].id);
@@ -129,14 +130,12 @@ export class SettingsOverlay implements Component {
129
130
  this.selectedIndex = (this.selectedIndex + 1) % group.config.stats.length;
130
131
  break;
131
132
  case " ": // Space - toggle stat
132
- case "\r": // Enter - toggle stat
133
133
  this.toggleStatVisibility(this.selectedGroupId, group.config.stats[this.selectedIndex].id);
134
134
  break;
135
135
  case "\x1b[D": // Left - back to groups
136
136
  case "h":
137
- this.mode = "groups";
138
- this.selectedGroupId = null;
139
- this.selectedIndex = this.groups.findIndex((g) => g.id === this.selectedGroupId) ?? 0;
137
+ case "\r": // Enter - also go back
138
+ this.backToGroups();
140
139
  break;
141
140
  case "q": // Quit from stats mode
142
141
  case "\x1b":
@@ -145,6 +144,15 @@ export class SettingsOverlay implements Component {
145
144
  }
146
145
  }
147
146
 
147
+ /**
148
+ * Return to groups mode, restoring cursor position.
149
+ */
150
+ private backToGroups(): void {
151
+ this.mode = "groups";
152
+ this.selectedIndex = this.savedGroupIndex; // Restore saved position
153
+ this.selectedGroupId = null;
154
+ }
155
+
148
156
  /**
149
157
  * Toggle group visibility.
150
158
  */
@@ -176,6 +184,7 @@ export class SettingsOverlay implements Component {
176
184
  * Enter stats editing mode for a group.
177
185
  */
178
186
  private enterStatsMode(groupId: string): void {
187
+ this.savedGroupIndex = this.selectedIndex; // Save position for later
179
188
  this.mode = "stats";
180
189
  this.selectedGroupId = groupId;
181
190
  this.selectedIndex = 0;
@@ -228,20 +237,28 @@ export class SettingsOverlay implements Component {
228
237
  }
229
238
  }
230
239
 
240
+ /**
241
+ * Pad a line to fill a target visual width.
242
+ */
243
+ private padToWidth(line: string, targetWidth: number): string {
244
+ const visLen = visibleWidth(line);
245
+ const pad = Math.max(0, targetWidth - visLen);
246
+ return line + " ".repeat(pad);
247
+ }
248
+
231
249
  /**
232
250
  * Render groups mode.
233
251
  */
234
252
  private renderGroupsMode(width: number): string[] {
235
253
  const lines: string[] = [];
254
+ const innerWidth = width - 2; // Subtract border chars
236
255
 
237
- // Header
238
- lines.push("");
239
- lines.push(this.renderCentered(`${ansi.bold}⚙️ Info Screen Settings${ansi.reset}`, width));
240
- lines.push("");
256
+ // Top border
257
+ lines.push(`${ansi.dim}╭${"".repeat(innerWidth)}╮${ansi.reset}`);
241
258
 
242
- // Separator
243
- lines.push(ansi.dim + "─".repeat(width) + ansi.reset);
244
- lines.push("");
259
+ // Header
260
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(this.renderCentered(`${ansi.bold}⚙️ Info Screen Settings${ansi.reset}`, innerWidth), innerWidth)}${ansi.dim}│${ansi.reset}`);
261
+ lines.push(`${ansi.dim}├${"".repeat(innerWidth)}┤${ansi.reset}`);
245
262
 
246
263
  // Group list
247
264
  for (let i = 0; i < this.groups.length; i++) {
@@ -259,18 +276,17 @@ export class SettingsOverlay implements Component {
259
276
  line += ` ${ansi.dim}→ stats${ansi.reset}`;
260
277
  }
261
278
 
262
- if (visibleWidth(line) > width - 2) {
263
- line = truncateToWidth(line, width - 2);
279
+ if (visibleWidth(line) > innerWidth - 2) {
280
+ line = truncateToWidth(line, innerWidth - 2);
264
281
  }
265
282
 
266
- lines.push(line);
283
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(line, innerWidth)}${ansi.dim}│${ansi.reset}`);
267
284
  }
268
285
 
269
286
  // Footer
270
- lines.push("");
271
- lines.push(ansi.dim + "─".repeat(width) + ansi.reset);
272
- lines.push(this.renderCentered(`${ansi.dim}↑↓ select Space toggle → stats J/K reorder q close${ansi.reset}`, width));
273
- lines.push("");
287
+ lines.push(`${ansi.dim}├${"".repeat(innerWidth)}┤${ansi.reset}`);
288
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(this.renderCentered(`${ansi.dim}↑↓ select Space toggle Enter/→ stats J/K reorder q close${ansi.reset}`, innerWidth), innerWidth)}${ansi.dim}│${ansi.reset}`);
289
+ lines.push(`${ansi.dim}╰${"─".repeat(innerWidth)}╯${ansi.reset}`);
274
290
 
275
291
  return lines;
276
292
  }
@@ -288,15 +304,14 @@ export class SettingsOverlay implements Component {
288
304
  }
289
305
 
290
306
  const groupSettings = getGroupSettings(group.id);
307
+ const innerWidth = width - 2;
291
308
 
292
- // Header
293
- lines.push("");
294
- lines.push(this.renderCentered(`${group.icon} ${group.name} Stats`, width));
295
- lines.push("");
309
+ // Top border
310
+ lines.push(`${ansi.dim}╭${"".repeat(innerWidth)}╮${ansi.reset}`);
296
311
 
297
- // Separator
298
- lines.push(ansi.dim + "─".repeat(width) + ansi.reset);
299
- lines.push("");
312
+ // Header
313
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(this.renderCentered(`${group.icon} ${group.name} Stats`, innerWidth), innerWidth)}${ansi.dim}│${ansi.reset}`);
314
+ lines.push(`${ansi.dim}├${"".repeat(innerWidth)}┤${ansi.reset}`);
300
315
 
301
316
  // Stats list
302
317
  for (let i = 0; i < group.config.stats.length; i++) {
@@ -309,18 +324,17 @@ export class SettingsOverlay implements Component {
309
324
 
310
325
  let line = ` ${indicator} ${toggle} ${stat.label}`;
311
326
 
312
- if (visibleWidth(line) > width - 2) {
313
- line = truncateToWidth(line, width - 2);
327
+ if (visibleWidth(line) > innerWidth - 2) {
328
+ line = truncateToWidth(line, innerWidth - 2);
314
329
  }
315
330
 
316
- lines.push(line);
331
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(line, innerWidth)}${ansi.dim}│${ansi.reset}`);
317
332
  }
318
333
 
319
334
  // Footer
320
- lines.push("");
321
- lines.push(ansi.dim + "─".repeat(width) + ansi.reset);
322
- lines.push(this.renderCentered(`${ansi.dim}↑↓ select Space toggle ← back q close${ansi.reset}`, width));
323
- lines.push("");
335
+ lines.push(`${ansi.dim}├${"".repeat(innerWidth)}┤${ansi.reset}`);
336
+ lines.push(`${ansi.dim}│${ansi.reset}${this.padToWidth(this.renderCentered(`${ansi.dim}↑↓ select Space toggle ←/Enter back q close${ansi.reset}`, innerWidth), innerWidth)}${ansi.dim}│${ansi.reset}`);
337
+ lines.push(`${ansi.dim}╰${"─".repeat(innerWidth)}╯${ansi.reset}`);
324
338
 
325
339
  return lines;
326
340
  }