diff --git a/apps/cli/src/config.ts b/apps/cli/src/config.ts index 44ce8205..8be7b754 100644 --- a/apps/cli/src/config.ts +++ b/apps/cli/src/config.ts @@ -103,7 +103,7 @@ export function queueMigrationNotice(notice: MigrationNotice): void { /** * Show any pending migration notice and clear it. - * Call this after the welcome screen renders so `console.clear()` doesn't wipe it. + * Call this after the welcome screen renders so the fresh viewport doesn't hide it. */ export function flushMigrationNotice(): void { if (pendingMigrationNotice) { diff --git a/apps/cli/src/lib/ui/welcome.test.ts b/apps/cli/src/lib/ui/welcome.test.ts new file mode 100644 index 00000000..b35a0a47 --- /dev/null +++ b/apps/cli/src/lib/ui/welcome.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from "bun:test"; +import { freshViewportSequence } from "./welcome.ts"; + +describe("freshViewportSequence", () => { + test("scrolls the visible screen into history and returns to the top", () => { + const sequence = freshViewportSequence(3); + + expect(sequence).toBe("\x1b[3;1H\n\n\n\x1b[H"); + expect(sequence).not.toContain("\x1b[2J"); + expect(sequence).not.toContain("\x1b[3J"); + }); + + test("returns no output for invalid terminal heights", () => { + expect(freshViewportSequence(0)).toBe(""); + expect(freshViewportSequence(-1)).toBe(""); + expect(freshViewportSequence(1.5)).toBe(""); + }); +}); diff --git a/apps/cli/src/lib/ui/welcome.ts b/apps/cli/src/lib/ui/welcome.ts index 23d814fb..54cbf4ba 100644 --- a/apps/cli/src/lib/ui/welcome.ts +++ b/apps/cli/src/lib/ui/welcome.ts @@ -13,6 +13,22 @@ export interface WelcomeResult { proceed: boolean; } +/** + * Build a blank viewport while retaining the current screen in scrollback. + */ +export function freshViewportSequence(rows: number): string { + if (!Number.isInteger(rows) || rows <= 0) return ""; + + return `\x1b[${rows};1H${"\n".repeat(rows)}\x1b[H`; +} + +function startOnFreshViewport(): void { + if (!process.stdout.isTTY) return; + + const sequence = freshViewportSequence(process.stdout.rows); + if (sequence) process.stdout.write(sequence); +} + /** * Strip ANSI escape codes from a string. */ @@ -90,7 +106,7 @@ export async function showWelcomeScreen( version: string, options?: WelcomeOptions, ): Promise { - console.clear(); + startOnFreshViewport(); // Layout dimensions const leftWidth = 30;