gsd-pi 2.64.0-dev.b3ee078 → 2.64.0-dev.cc2cef3
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/notification-overlay.js +39 -6
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
- 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 +18 -18
- 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.js +4 -4
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +2 -2
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +4 -4
- package/packages/pi-tui/src/overlay-layout.ts +2 -3
- package/src/resources/extensions/gsd/notification-overlay.ts +36 -7
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +73 -0
- /package/dist/web/standalone/.next/static/{l7tiSF0KtXOwxxYn0ZAyF → kOmg_dJ8TT33Q12mP5qZc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{l7tiSF0KtXOwxxYn0ZAyF → kOmg_dJ8TT33Q12mP5qZc}/_ssgManifest.js +0 -0
|
@@ -28,6 +28,27 @@ function severityIcon(severity: NotifySeverity): string {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** Word-wrap plain text to fit within maxWidth columns. */
|
|
32
|
+
function wrapText(text: string, maxWidth: number): string[] {
|
|
33
|
+
if (text.length <= maxWidth) return [text];
|
|
34
|
+
const words = text.split(/\s+/);
|
|
35
|
+
const lines: string[] = [];
|
|
36
|
+
let current = "";
|
|
37
|
+
for (const word of words) {
|
|
38
|
+
if (current.length === 0) {
|
|
39
|
+
current = word;
|
|
40
|
+
} else if (current.length + 1 + word.length <= maxWidth) {
|
|
41
|
+
current += " " + word;
|
|
42
|
+
} else {
|
|
43
|
+
lines.push(current);
|
|
44
|
+
current = word;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (current.length > 0) lines.push(current);
|
|
48
|
+
// If a single word exceeds maxWidth, truncate it
|
|
49
|
+
return lines.map((l) => l.length > maxWidth ? l.slice(0, maxWidth - 1) + "…" : l);
|
|
50
|
+
}
|
|
51
|
+
|
|
31
52
|
function formatTimestamp(ts: string): string {
|
|
32
53
|
try {
|
|
33
54
|
const d = new Date(ts);
|
|
@@ -252,13 +273,21 @@ export class GSDNotificationOverlay {
|
|
|
252
273
|
const time = th.fg("dim", formatTimestamp(entry.ts));
|
|
253
274
|
const source = entry.source === "workflow-logger" ? th.fg("dim", " [engine]") : "";
|
|
254
275
|
|
|
255
|
-
//
|
|
256
|
-
const
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
276
|
+
// Measure actual prefix width for wrapping
|
|
277
|
+
const prefix = `${coloredIcon} ${time}${source} `;
|
|
278
|
+
const prefixWidth = visibleWidth(prefix);
|
|
279
|
+
const msgMaxWidth = Math.max(10, contentWidth - prefixWidth);
|
|
280
|
+
|
|
281
|
+
// Wrap long messages onto continuation lines indented to align with message start
|
|
282
|
+
const msgLines = wrapText(entry.message, msgMaxWidth);
|
|
283
|
+
const indent = " ".repeat(prefixWidth);
|
|
284
|
+
for (let i = 0; i < msgLines.length; i++) {
|
|
285
|
+
if (i === 0) {
|
|
286
|
+
lines.push(row(`${prefix}${msgLines[i]}`));
|
|
287
|
+
} else {
|
|
288
|
+
lines.push(row(`${indent}${msgLines[i]}`));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
262
291
|
}
|
|
263
292
|
|
|
264
293
|
return lines;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// GSD Extension — Notification Overlay Tests
|
|
2
|
+
// Tests for message wrapping and content-fit sizing in the notification panel.
|
|
3
|
+
|
|
4
|
+
import { describe, test } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
|
|
7
|
+
// The wrapText function is private to the module, so we test the overlay's
|
|
8
|
+
// render output indirectly. We also extract and test wrapText logic directly.
|
|
9
|
+
|
|
10
|
+
// ── wrapText logic (mirrors the private function) ───────────────────────────
|
|
11
|
+
|
|
12
|
+
function wrapText(text: string, maxWidth: number): string[] {
|
|
13
|
+
if (text.length <= maxWidth) return [text];
|
|
14
|
+
const words = text.split(/\s+/);
|
|
15
|
+
const lines: string[] = [];
|
|
16
|
+
let current = "";
|
|
17
|
+
for (const word of words) {
|
|
18
|
+
if (current.length === 0) {
|
|
19
|
+
current = word;
|
|
20
|
+
} else if (current.length + 1 + word.length <= maxWidth) {
|
|
21
|
+
current += " " + word;
|
|
22
|
+
} else {
|
|
23
|
+
lines.push(current);
|
|
24
|
+
current = word;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (current.length > 0) lines.push(current);
|
|
28
|
+
return lines.map((l) => l.length > maxWidth ? l.slice(0, maxWidth - 1) + "…" : l);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("notification overlay — wrapText", () => {
|
|
32
|
+
test("short text returns single line", () => {
|
|
33
|
+
const result = wrapText("hello world", 80);
|
|
34
|
+
assert.deepStrictEqual(result, ["hello world"]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("long text wraps at word boundaries", () => {
|
|
38
|
+
const text = "This is a long notification message that should wrap across multiple lines";
|
|
39
|
+
const result = wrapText(text, 40);
|
|
40
|
+
assert.ok(result.length > 1, `expected multiple lines, got ${result.length}`);
|
|
41
|
+
for (const line of result) {
|
|
42
|
+
assert.ok(line.length <= 40, `line exceeds maxWidth: "${line}" (${line.length})`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("single word exceeding maxWidth is truncated", () => {
|
|
47
|
+
const result = wrapText("superlongwordthatexceedsmaxwidth", 10);
|
|
48
|
+
assert.equal(result.length, 1);
|
|
49
|
+
assert.equal(result[0]!.length, 10);
|
|
50
|
+
assert.ok(result[0]!.endsWith("…"));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("empty string returns single empty line", () => {
|
|
54
|
+
const result = wrapText("", 80);
|
|
55
|
+
assert.deepStrictEqual(result, [""]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("exact-fit text returns single line", () => {
|
|
59
|
+
const text = "exactly twenty chars";
|
|
60
|
+
const result = wrapText(text, 20);
|
|
61
|
+
assert.deepStrictEqual(result, [text]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("preserves all words across wrapped lines", () => {
|
|
65
|
+
const words = ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot"];
|
|
66
|
+
const text = words.join(" ");
|
|
67
|
+
const result = wrapText(text, 15);
|
|
68
|
+
const rejoined = result.join(" ");
|
|
69
|
+
for (const w of words) {
|
|
70
|
+
assert.ok(rejoined.includes(w), `missing word: ${w}`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
File without changes
|
|
File without changes
|