gsd-pi 2.64.0-dev.05b8a94 → 2.64.0-dev.4ac9673

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 (56) hide show
  1. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +1 -0
  2. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +1 -0
  3. package/dist/resources/extensions/gsd/notification-overlay.js +13 -9
  4. package/dist/resources/extensions/gsd/notification-store.js +10 -5
  5. package/dist/web/standalone/.next/BUILD_ID +1 -1
  6. package/dist/web/standalone/.next/app-path-routes-manifest.json +22 -22
  7. package/dist/web/standalone/.next/build-manifest.json +2 -2
  8. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  9. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  10. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  18. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/index.html +1 -1
  26. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app-paths-manifest.json +22 -22
  33. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  34. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  35. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  36. package/package.json +1 -1
  37. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
  38. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
  39. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
  40. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
  41. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  42. package/packages/pi-tui/dist/overlay-layout.js +12 -1
  43. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  44. package/packages/pi-tui/dist/tui.d.ts +2 -0
  45. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  46. package/packages/pi-tui/dist/tui.js.map +1 -1
  47. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
  48. package/packages/pi-tui/src/overlay-layout.ts +13 -1
  49. package/packages/pi-tui/src/tui.ts +2 -0
  50. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +1 -0
  51. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +1 -0
  52. package/src/resources/extensions/gsd/notification-overlay.ts +14 -9
  53. package/src/resources/extensions/gsd/notification-store.ts +8 -3
  54. package/src/resources/extensions/gsd/tests/notification-store.test.ts +34 -1
  55. /package/dist/web/standalone/.next/static/{Vbx2-SrSBOgta6576xj9m → 1btalZ1AEGX9RBvxBqJlC}/_buildManifest.js +0 -0
  56. /package/dist/web/standalone/.next/static/{Vbx2-SrSBOgta6576xj9m → 1btalZ1AEGX9RBvxBqJlC}/_ssgManifest.js +0 -0
@@ -157,13 +157,18 @@ export class GSDNotificationOverlay {
157
157
  }
158
158
 
159
159
  const content = this.buildContentLines(width);
160
- const viewportHeight = Math.max(5, process.stdout.rows ? process.stdout.rows - 8 : 24);
161
- const chromeHeight = 2; // top + bottom border
162
- const visibleContentRows = Math.max(1, viewportHeight - chromeHeight);
160
+ const maxVisibleRows = Math.max(5, process.stdout.rows ? process.stdout.rows - 8 : 24) - 2;
161
+ const visibleContentRows = Math.min(content.length, maxVisibleRows);
163
162
  const maxScroll = Math.max(0, content.length - visibleContentRows);
164
163
  this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
165
164
  const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
166
165
 
166
+ // Pad to consistent height so filter changes don't leave ghost artifacts
167
+ // (differential renderer can't clear old overlay positions)
168
+ while (visibleContent.length < maxVisibleRows) {
169
+ visibleContent.push("");
170
+ }
171
+
167
172
  const lines = this.wrapInBox(visibleContent, width);
168
173
 
169
174
  this.cachedWidth = width;
@@ -253,13 +258,13 @@ export class GSDNotificationOverlay {
253
258
  const time = th.fg("dim", formatTimestamp(entry.ts));
254
259
  const source = entry.source === "workflow-logger" ? th.fg("dim", " [engine]") : "";
255
260
 
256
- // First line: icon + timestamp + source
257
- const msgMaxWidth = contentWidth - 20;
258
- const msg = entry.message.length > msgMaxWidth
259
- ? entry.message.slice(0, msgMaxWidth - 1) + "…"
260
- : entry.message;
261
+ // Measure actual prefix width to truncate message accurately
262
+ const prefix = `${coloredIcon} ${time}${source} `;
263
+ const prefixWidth = visibleWidth(prefix);
264
+ const msgMaxWidth = Math.max(10, contentWidth - prefixWidth);
265
+ const msg = truncateToWidth(entry.message, msgMaxWidth, "…");
261
266
 
262
- lines.push(row(`${coloredIcon} ${time}${source} ${msg}`));
267
+ lines.push(row(`${prefix}${msg}`));
263
268
  }
264
269
 
265
270
  return lines;
@@ -275,14 +275,19 @@ function _withLock<T>(basePath: string, fn: () => T): T {
275
275
  }
276
276
  }
277
277
 
278
+ // Only run the mutation if we actually own the lock
279
+ const ownsLock = fd !== null;
278
280
  try {
279
- // Write our PID timestamp into the lock for stale detection
280
- if (fd !== null) {
281
+ if (ownsLock && fd !== null) {
282
+ // Write our PID timestamp into the lock for stale detection
281
283
  writeFileSync(lockPath, String(Date.now()), "utf-8");
282
284
  closeSync(fd);
283
285
  }
284
286
  return fn();
285
287
  } finally {
286
- try { unlinkSync(lockPath); } catch { /* best-effort cleanup */ }
288
+ // Only delete the lock if we created it never remove another process's lock
289
+ if (ownsLock) {
290
+ try { unlinkSync(lockPath); } catch { /* best-effort cleanup */ }
291
+ }
287
292
  }
288
293
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { describe, test, beforeEach, afterEach } from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync } from "node:fs";
5
+ import { mkdtempSync, mkdirSync, rmSync, readFileSync, existsSync, writeFileSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
 
@@ -246,4 +246,37 @@ describe("notification-store", () => {
246
246
  assert.equal(getUnreadCount(), 0);
247
247
  assert.equal(getLineCount(), 0);
248
248
  });
249
+
250
+ test("markAllRead does not delete a foreign lock file", () => {
251
+ initNotificationStore(tmp);
252
+ appendNotification("msg1", "info");
253
+
254
+ // Simulate another process holding the lock
255
+ const lockPath = join(tmp, ".gsd", "notifications.lock");
256
+ writeFileSync(lockPath, String(Date.now()), "utf-8");
257
+
258
+ // markAllRead should still work (best-effort) but not delete the foreign lock
259
+ markAllRead();
260
+
261
+ assert.ok(existsSync(lockPath), "foreign lock file should not be deleted");
262
+
263
+ // Clean up the lock so afterEach doesn't leave artifacts
264
+ rmSync(lockPath, { force: true });
265
+ });
266
+
267
+ test("clearNotifications does not delete a foreign lock file", () => {
268
+ initNotificationStore(tmp);
269
+ appendNotification("msg1", "info");
270
+
271
+ // Simulate another process holding the lock
272
+ const lockPath = join(tmp, ".gsd", "notifications.lock");
273
+ writeFileSync(lockPath, String(Date.now()), "utf-8");
274
+
275
+ // clearNotifications should still work but not delete the foreign lock
276
+ clearNotifications();
277
+
278
+ assert.ok(existsSync(lockPath), "foreign lock file should not be deleted");
279
+
280
+ rmSync(lockPath, { force: true });
281
+ });
249
282
  });