@pi-unipi/info-screen 0.1.7 → 0.1.9

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
@@ -322,8 +322,10 @@ export function setPiApi(api: any): void {
322
322
  * Add a module to the announced list.
323
323
  */
324
324
  export function trackModule(name: string, version: string): void {
325
+
325
326
  if (!announcedModules.find((m) => m.name === name)) {
326
327
  announcedModules.push({ name, version });
328
+
327
329
  }
328
330
  }
329
331
 
@@ -331,6 +333,7 @@ export function trackModule(name: string, version: string): void {
331
333
  * Get list of announced modules.
332
334
  */
333
335
  export function getAnnouncedModules(): Array<{ name: string; version: string }> {
336
+
334
337
  return [...announcedModules];
335
338
  }
336
339
 
@@ -375,16 +378,32 @@ export function registerCoreGroups(): void {
375
378
  const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
376
379
  const shortCwd = cwd.startsWith(homeDir) ? `~${cwd.slice(homeDir.length)}` : cwd;
377
380
 
378
- const modules = getAnnouncedModules();
379
- const moduleNames = modules.map((m) => m.name.replace(/^@[^/]+\//, ""));
381
+ // Get modules from announced events AND registered groups
382
+ const announced = getAnnouncedModules();
383
+ const registeredGroups = infoRegistry.getAllGroups();
384
+
385
+ // Combine: announced modules + groups that aren't from announced modules
386
+ const moduleNames = new Set<string>();
387
+ for (const m of announced) {
388
+ moduleNames.add(m.name.replace(/^@[^/]+\//, ""));
389
+ }
390
+ // Add non-core groups as modules (they come from extensions)
391
+ const coreGroupIds = new Set(["overview", "usage", "tools", "extensions", "skills"]);
392
+ for (const g of registeredGroups) {
393
+ if (!coreGroupIds.has(g.id)) {
394
+ moduleNames.add(g.id);
395
+ }
396
+ }
397
+
380
398
  const totalLoadTime = getTotalLoadTime();
399
+ const moduleList = Array.from(moduleNames);
381
400
 
382
401
  return {
383
402
  version: { value: getPiVersion(), detail: "pi" },
384
403
  cwd: { value: shortCwd },
385
404
  modules: {
386
- value: String(modules.length),
387
- detail: moduleNames.slice(0, 4).join(", ") + (moduleNames.length > 4 ? ` +${moduleNames.length - 4} more` : ""),
405
+ value: String(moduleList.length),
406
+ detail: moduleList.slice(0, 4).join(", ") + (moduleList.length > 4 ? ` +${moduleList.length - 4} more` : ""),
388
407
  },
389
408
  uptime: { value: formatUptime(process.uptime()) },
390
409
  loadTime: { value: `${totalLoadTime}ms` },
package/index.ts CHANGED
@@ -64,6 +64,9 @@ export default function (pi: ExtensionAPI) {
64
64
  trackModule(event.name, event.version || "unknown");
65
65
  recordLoadTime(event.name, "module", event.loadTimeMs);
66
66
 
67
+ // Invalidate overview cache so next render shows updated modules
68
+ infoRegistry.invalidateCache("overview");
69
+
67
70
  // Track tools from this module
68
71
  if (event.tools && Array.isArray(event.tools)) {
69
72
  for (const tool of event.tools) {
@@ -96,11 +99,11 @@ export default function (pi: ExtensionAPI) {
96
99
  });
97
100
 
98
101
  // Session lifecycle
99
- pi.on("session_start", async (_event, ctx) => {
102
+ pi.on("session_start", async (event, ctx) => {
100
103
  const settings = getInfoSettings();
101
104
 
102
- // Show dashboard on boot if enabled
103
- if (settings.showOnBoot) {
105
+ // Show dashboard only on initial startup, not on /new
106
+ if (settings.showOnBoot && event.reason === "startup") {
104
107
  // Wait for other modules to announce
105
108
  await waitForModules();
106
109
 
@@ -109,6 +112,7 @@ export default function (pi: ExtensionAPI) {
109
112
  (tui, _theme, _keybindings, done) => {
110
113
  const overlay = new InfoOverlay();
111
114
  overlay.onClose = () => done(undefined);
115
+ overlay.requestRender = () => tui.requestRender();
112
116
  // Return three-method object as per pi-tui docs
113
117
  return {
114
118
  render: (w: number) => overlay.render(w),
@@ -151,6 +155,7 @@ export default function (pi: ExtensionAPI) {
151
155
  (tui, _theme, _keybindings, done) => {
152
156
  const overlay = new InfoOverlay();
153
157
  overlay.onClose = () => done(undefined);
158
+ overlay.requestRender = () => tui.requestRender();
154
159
  return {
155
160
  render: (w: number) => overlay.render(w),
156
161
  invalidate: () => overlay.invalidate(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/info-screen",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Dashboard and module registry for Unipi — configurable info overlay with tabbed groups",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,6 +54,7 @@ export class InfoOverlay implements Component {
54
54
  onClose?: () => void;
55
55
 
56
56
  constructor() {
57
+ // Start loading data immediately
57
58
  this.loadData();
58
59
  }
59
60
 
@@ -69,6 +70,8 @@ export class InfoOverlay implements Component {
69
70
  */
70
71
  private async loadData(): Promise<void> {
71
72
  this.loading = true;
73
+ // Wait a bit for modules to announce before fetching groups
74
+ await new Promise(r => setTimeout(r, 500));
72
75
  // Always re-fetch ALL groups to catch late registrations
73
76
  this.groups = infoRegistry.getAllGroups();
74
77
 
@@ -84,13 +87,21 @@ export class InfoOverlay implements Component {
84
87
  }
85
88
 
86
89
  try {
87
- // Load data for all groups in parallel
88
- const promises = this.groups.map(async (group) => {
89
- const data = await infoRegistry.getGroupData(group.id);
90
- this.groupData.set(group.id, data);
90
+ // Load data for all groups in parallel with timeout
91
+ const loadPromises = this.groups.map(async (group) => {
92
+ try {
93
+ const data = await Promise.race([
94
+ infoRegistry.getGroupData(group.id),
95
+ new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), 3000)),
96
+ ]);
97
+ this.groupData.set(group.id, data);
98
+ } catch {
99
+ // Use empty data on timeout/error
100
+ this.groupData.set(group.id, {});
101
+ }
91
102
  });
92
103
 
93
- await Promise.all(promises);
104
+ await Promise.all(loadPromises);
94
105
  } catch (error) {
95
106
  this.error = error instanceof Error ? error.message : String(error);
96
107
  }
@@ -145,6 +156,7 @@ export class InfoOverlay implements Component {
145
156
  * Render the component.
146
157
  */
147
158
  render(width: number): string[] {
159
+ // While loading, show loading state
148
160
  if (this.loading) {
149
161
  return this.renderLoading(width);
150
162
  }
@@ -198,6 +210,11 @@ export class InfoOverlay implements Component {
198
210
  }
199
211
  }
200
212
 
213
+ /**
214
+ * Callback set by wrapper to trigger re-render.
215
+ */
216
+ requestRender?: () => void;
217
+
201
218
  /**
202
219
  * Refresh data for all groups (non-blocking).
203
220
  */
@@ -207,7 +224,14 @@ export class InfoOverlay implements Component {
207
224
  infoRegistry.invalidateCache(group.id);
208
225
  // Fetch fresh data (non-blocking)
209
226
  infoRegistry.getGroupData(group.id).then(data => {
227
+ const old = this.groupData.get(group.id);
228
+ const oldStr = JSON.stringify(old);
229
+ const newStr = JSON.stringify(data);
210
230
  this.groupData.set(group.id, data);
231
+ // Trigger re-render if data changed
232
+ if (oldStr !== newStr && this.requestRender) {
233
+ this.requestRender();
234
+ }
211
235
  }).catch(() => {
212
236
  // Ignore errors
213
237
  });