Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
52 lines
1.3 KiB
TypeScript
52 lines
1.3 KiB
TypeScript
import type { Component } from "../tui.ts";
|
|
import { truncateToWidth } from "../utils.ts";
|
|
|
|
const DEFAULT_DURATION_MS = 1000;
|
|
|
|
interface FlashEntry {
|
|
id: number;
|
|
message: string;
|
|
timer: NodeJS.Timeout;
|
|
}
|
|
|
|
/** Transient messages composited by the alternate-screen renderer. */
|
|
export class AltScreenFlashContainer implements Component {
|
|
private readonly entries: FlashEntry[] = [];
|
|
private nextId = 0;
|
|
private readonly requestRender: () => void;
|
|
|
|
constructor(requestRender: () => void) {
|
|
this.requestRender = requestRender;
|
|
}
|
|
|
|
flash(message: string, durationMs = DEFAULT_DURATION_MS): void {
|
|
const id = this.nextId++;
|
|
const timer = setTimeout(
|
|
() => {
|
|
const index = this.entries.findIndex((entry) => entry.id === id);
|
|
if (index === -1) return;
|
|
this.entries.splice(index, 1);
|
|
this.requestRender();
|
|
},
|
|
Math.max(0, durationMs),
|
|
);
|
|
timer.unref();
|
|
this.entries.push({ id, message, timer });
|
|
this.requestRender();
|
|
}
|
|
|
|
dispose(): void {
|
|
for (const entry of this.entries) clearTimeout(entry.timer);
|
|
this.entries.length = 0;
|
|
}
|
|
|
|
invalidate(): void {}
|
|
|
|
render(width: number): string[] {
|
|
return this.entries.map((entry) => {
|
|
const message = truncateToWidth(` ${entry.message} `, width, "");
|
|
return `\x1b[7m${message}\x1b[27m`;
|
|
});
|
|
}
|
|
}
|