diff --git a/packages/pi-tui/src/__tests__/overlay-layout.test.ts b/packages/pi-tui/src/__tests__/overlay-layout.test.ts index 20907025a..49d0539da 100644 --- a/packages/pi-tui/src/__tests__/overlay-layout.test.ts +++ b/packages/pi-tui/src/__tests__/overlay-layout.test.ts @@ -34,7 +34,7 @@ describe("compositeOverlays — backdrop", () => { assert.ok(dimmedLine.includes("\x1b[2m"), "base line should be dimmed"); }); - it("backdrop uses 256-color dark gray background", () => { + it("backdrop uses gray foreground for dimming", () => { const base = ["hello world", "second line"]; const overlay = makeEntry(["OV"], { width: 2, @@ -44,11 +44,11 @@ describe("compositeOverlays — backdrop", () => { const result = compositeOverlays(base, [overlay], 20, 20, 2); - // Check a non-overlay line for full backdrop codes + // Check a non-overlay line for backdrop codes (dim + gray fg, no bg) const line = result.find((l) => l.includes("second line")); assert.ok(line, "should have a line containing 'second line'"); - assert.ok(line.includes("\x1b[38;5;242m"), "backdrop should set gray foreground"); - assert.ok(line.includes("\x1b[48;5;233m"), "backdrop should set dark gray background"); + assert.ok(line.includes("\x1b[38;5;240m"), "backdrop should set gray foreground"); + assert.ok(!line.includes("\x1b[48;"), "backdrop should not set background color"); }); it("does not dim when backdrop is false/absent", () => { diff --git a/packages/pi-tui/src/overlay-layout.ts b/packages/pi-tui/src/overlay-layout.ts index ee614f094..5e306ec91 100644 --- a/packages/pi-tui/src/overlay-layout.ts +++ b/packages/pi-tui/src/overlay-layout.ts @@ -325,11 +325,10 @@ export function compositeOverlays( const viewportStart = Math.max(0, workingHeight - termHeight); // Apply backdrop dimming if any visible overlay requests it. - // Uses dim + dark gray background (256-color 233) so the overlay pops visually. + // Uses dim + gray foreground so text fades without painting empty lines. const hasBackdrop = visibleEntries.some((e) => e.options?.backdrop); if (hasBackdrop) { - const dimFn = (text: string) => - `\x1b[2m\x1b[38;5;242m\x1b[48;5;233m${text}\x1b[49m\x1b[39m\x1b[22m`; + const dimFn = (text: string) => `\x1b[2m\x1b[38;5;240m${text}\x1b[39m\x1b[22m`; for (let i = viewportStart; i < result.length; i++) { if (!isImageLine(result[i]) && result[i].length > 0) { result[i] = applyBackgroundToLine(result[i], termWidth, dimFn); diff --git a/src/resources/extensions/gsd/notification-overlay.ts b/src/resources/extensions/gsd/notification-overlay.ts index 902e21b73..2c001d313 100644 --- a/src/resources/extensions/gsd/notification-overlay.ts +++ b/src/resources/extensions/gsd/notification-overlay.ts @@ -258,13 +258,13 @@ export class GSDNotificationOverlay { const time = th.fg("dim", formatTimestamp(entry.ts)); const source = entry.source === "workflow-logger" ? th.fg("dim", " [engine]") : ""; - // First line: icon + timestamp + source - const msgMaxWidth = contentWidth - 20; - const msg = entry.message.length > msgMaxWidth - ? entry.message.slice(0, msgMaxWidth - 1) + "…" - : entry.message; + // Measure actual prefix width to truncate message accurately + const prefix = `${coloredIcon} ${time}${source} `; + const prefixWidth = visibleWidth(prefix); + const msgMaxWidth = Math.max(10, contentWidth - prefixWidth); + const msg = truncateToWidth(entry.message, msgMaxWidth, "…"); - lines.push(row(`${coloredIcon} ${time}${source} ${msg}`)); + lines.push(row(`${prefix}${msg}`)); } return lines;