Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/tui/components/event-log-assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,14 +611,17 @@ function blockToLines(
];
}
case "user": {
// A subtle box with a blank padded row above and below so the text has
// breathing room. Text starts at column 1 to line up with the assistant's
// "●" marker; a 1-col right margin keeps the fill off the edge.
// Subtle box: paint background only on content rows. Blank spacer rows
// used to be full-width bg fills and could flash as solid grey blocks
// during scroll/repaint when content was empty or mid-frame.
// plainLines/wrapRanges always yield ≥1 row (even for ""), so gate on
// real emptiness rather than userLines.length.
if (!block.content.trim()) return [];
const bg = color("userMessageBg");
const LEFT = 1;
const RIGHT = 1;
const innerWidth = Math.max(1, width - LEFT - RIGHT);
const blankRow = [{ text: " ".repeat(width), backgroundColor: bg }];
const blankRow: StyledLine = [{ text: "" }];
const userLines = plainLines(
compactUserCodeBlocks(block.content),
{ color: color("text"), backgroundColor: bg },
Expand Down
20 changes: 20 additions & 0 deletions src/tui/components/event-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "./event-log.js";
import { formatElapsed } from "./in-flight-indicator.js";
import type { ContentBlock, ContentBlockData } from "../use-stream.js";
import { color } from "../theme.js";

function asBlock(data: ContentBlockData & { id: string }): ContentBlock {
return data as ContentBlock;
Expand Down Expand Up @@ -706,6 +707,25 @@ describe("flat line buffer", () => {
}
});

test("user banner spacer rows have no background while body keeps userMessageBg", () => {
const block: ContentBlock = { type: "user", id: "user-bg", content: "hello" };
const lines = buildLines([block], COLUMNS, false, isExpanded);
// blank spacer, body, blank spacer
expect(lines.length).toBe(3);
const bg = color("userMessageBg");
expect(lines[0]!.every((seg) => seg.backgroundColor === undefined)).toBe(true);
expect(lines[2]!.every((seg) => seg.backgroundColor === undefined)).toBe(true);
expect(lines[1]!.some((seg) => seg.backgroundColor === bg)).toBe(true);
expect(lines[1]!.every((seg) => seg.backgroundColor === bg)).toBe(true);
});

test("empty or whitespace user content yields no grey box", () => {
for (const content of ["", " ", "\n\t"]) {
const block: ContentBlock = { type: "user", id: "empty-user", content };
expect(buildLines([block], COLUMNS, false, isExpanded)).toEqual([]);
}
});

test("buildResourceBanner shows brand, workspace path, and optional skills/plugins", () => {
const workspace = "/home/user/project";
const banner = buildResourceBanner([{ name: "scribe" }, { name: "tdd" }], ["exa"], 80, workspace);
Expand Down
Loading