fix(gsd): add backdrop dimming and viewport padding to notification overlay

The notification overlay was rendering too small with few entries, allowing
underlying content to bleed through. Added viewport padding to fill the
overlay box and a new `backdrop` option to OverlayOptions that dims the
background behind modal overlays.
This commit is contained in:
Jeremy 2026-04-06 17:34:45 -05:00
parent 6dfc422990
commit 2c4ac844f1
5 changed files with 21 additions and 1 deletions

View file

@ -6,7 +6,7 @@
*/
import type { OverlayAnchor, OverlayOptions, SizeValue } from "./tui.js";
import { extractSegments, sliceByColumn, sliceWithWidth, truncateToWidth, visibleWidth } from "./utils.js";
import { applyBackgroundToLine, extractSegments, sliceByColumn, sliceWithWidth, truncateToWidth, visibleWidth } from "./utils.js";
import { isImageLine } from "./terminal-image.js";
import { CURSOR_MARKER } from "./tui.js";
@ -324,6 +324,17 @@ export function compositeOverlays(
const viewportStart = Math.max(0, workingHeight - termHeight);
// Apply backdrop dimming if any visible overlay requests it
const hasBackdrop = visibleEntries.some((e) => e.options?.backdrop);
if (hasBackdrop) {
const dimFn = (text: string) => `\x1b[2m${text}\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);
}
}
}
// Composite each overlay
for (const { overlayLines, row, col, w } of rendered) {
for (let i = 0; i < overlayLines.length; i++) {

View file

@ -141,6 +141,8 @@ export interface OverlayOptions {
visible?: (termWidth: number, termHeight: number) => boolean;
/** If true, don't capture keyboard focus when shown */
nonCapturing?: boolean;
/** If true, dim the background behind the overlay */
backdrop?: boolean;
}
/**

View file

@ -44,6 +44,7 @@ export function registerShortcuts(pi: ExtensionAPI): void {
minWidth: 60,
maxHeight: "88%",
anchor: "center",
backdrop: true,
},
},
);

View file

@ -105,6 +105,7 @@ export async function handleNotificationsCommand(
minWidth: 60,
maxHeight: "88%",
anchor: "center",
backdrop: true,
},
},
);

View file

@ -164,6 +164,11 @@ export class GSDNotificationOverlay {
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
// Pad to fill viewport so the overlay covers underlying content
while (visibleContent.length < visibleContentRows) {
visibleContent.push("");
}
const lines = this.wrapInBox(visibleContent, width);
this.cachedWidth = width;