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.
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +1 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +13 -9
- package/dist/resources/extensions/gsd/notification-store.js +10 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +22 -22
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +22 -22
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +12 -1
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +2 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
- package/packages/pi-tui/src/overlay-layout.ts +13 -1
- package/packages/pi-tui/src/tui.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +1 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +14 -9
- package/src/resources/extensions/gsd/notification-store.ts +8 -3
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +34 -1
- /package/dist/web/standalone/.next/static/{Vbx2-SrSBOgta6576xj9m → 1btalZ1AEGX9RBvxBqJlC}/_buildManifest.js +0 -0
- /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
|
|
161
|
-
const
|
|
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
|
-
//
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
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(`${
|
|
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
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
});
|
|
File without changes
|
|
File without changes
|