fix(terminal): always emit full intro art (#121501)

* fix(terminal): always emit full intro art

Open-time request.cols is the pre-fit boot grid (the client resizes right
after open), so the 40-column gate suppressed the art on real terminals.
Live-verified against a dev gateway on post-merge main.

* test(terminal): align intro banner fixture
This commit is contained in:
Peter Steinberger
2026-08-10 01:30:37 -07:00
committed by GitHub
parent a471a94b53
commit 83dfd44eca
5 changed files with 9 additions and 12 deletions
+1 -7
View File
@@ -22,7 +22,7 @@ const EXPECTED_ART = [
describe("composeTerminalIntroBanner", () => {
it("composes the exact colored CRLF intro and resets ANSI state", () => {
const banner = composeTerminalIntroBanner(80);
const banner = composeTerminalIntroBanner();
expect(banner).toBe(
`\r\n\x1b[38;5;223mWelcome to the Claw.\x1b[0m\r\n\r\n\x1b[38;5;216m${EXPECTED_ART.join("\r\n")}\r\n\r\n\x1b[0m`,
@@ -31,10 +31,4 @@ describe("composeTerminalIntroBanner", () => {
expect(banner.endsWith("\r\n\r\n\x1b[0m")).toBe(true);
expect(banner.replaceAll("\r\n", "")).not.toContain("\n");
});
it("emits only the headline below 40 columns", () => {
expect(composeTerminalIntroBanner(39)).toBe(
"\r\n\x1b[38;5;223mWelcome to the Claw.\x1b[0m\r\n\r\n\x1b[0m",
);
});
});
+5 -2
View File
@@ -19,8 +19,11 @@ const TERMINAL_INTRO_ART = [
" .::•::•::",
] as const;
export function composeTerminalIntroBanner(cols: number): string {
// Always full art: open-time request.cols is the pre-fit boot grid (the client
// resizes immediately after open), so width gating keyed on it suppressed the
// art on real, wide terminals.
export function composeTerminalIntroBanner(): string {
const headline = `\x1b[38;5;223mWelcome to the Claw.${RESET}`;
const art = cols >= 40 ? `\x1b[38;5;216m${TERMINAL_INTRO_ART.join("\r\n")}\r\n\r\n` : "";
const art = `\x1b[38;5;216m${TERMINAL_INTRO_ART.join("\r\n")}\r\n\r\n`;
return `\r\n${headline}\r\n\r\n${art}${RESET}`;
}
@@ -13,7 +13,7 @@ describe("TerminalSessionManager intro banner", () => {
if (!operator.ok) {
throw new Error("expected operator open");
}
const intro = composeTerminalIntroBanner(80);
const intro = composeTerminalIntroBanner();
expect(manager.snapshot(operator.sessionId)).toBe(intro);
await vi.advanceTimersByTimeAsync(4);
+1 -1
View File
@@ -10,7 +10,7 @@ import {
} from "./session-manager.test-helpers.js";
const TERMINAL_EVENT_DATA = "terminal.data";
const TERMINAL_EVENT_EXIT = "terminal.exit";
const OPERATOR_INTRO = composeTerminalIntroBanner(80);
const OPERATOR_INTRO = composeTerminalIntroBanner();
function deferred<T>() {
let resolve!: (value: T) => void;
+1 -1
View File
@@ -269,7 +269,7 @@ export class TerminalSessionManager {
this.sessions.set(session.id, session);
if (request.owner.kind === "conn") {
this.indexByConn(request.owner.connId, session.id);
session.output.push(composeTerminalIntroBanner(request.cols));
session.output.push(composeTerminalIntroBanner());
}
backend.onData((chunk) => {