authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-05 10:06:47-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-05 10:20:34-08:00
log7c7fcf46c2307a0fa0348d8af9012791eb9ebb2d
tree577db5d30b407168bfa601e7189155a068bbfeda
parent0c39733cbf505ce1bf7f15d9b4576ba6ee4fd6b7

Handle system timeline messages with assistant height rules

- Route `system` messages through assistant height estimation - Keep user attachment height logic scoped to user messages - Add regression test for system-message sizing behavior

2 files changed, 23 insertions(+), 6 deletions(-)

apps/web/src/components/timelineHeight.test.ts+9
......@@ -12,6 +12,15 @@ describe("estimateTimelineMessageHeight", () => {
1212 ).toBe(122);
1313 });
1414
15 it("uses assistant sizing rules for system messages", () => {
16 expect(
17 estimateTimelineMessageHeight({
18 role: "system",
19 text: "a".repeat(144),
20 }),
21 ).toBe(122);
22 });
23
1524 it("adds one attachment row for one or two user attachments", () => {
1625 expect(
1726 estimateTimelineMessageHeight({
apps/web/src/components/timelineHeight.ts+14-6
......@@ -67,16 +67,24 @@ export function estimateTimelineMessageHeight(
6767 message: TimelineMessageHeightInput,
6868 layout: TimelineHeightEstimateLayout = { timelineWidthPx: null },
6969): number {
70 if (message.role !== "user") {
70 if (message.role === "assistant") {
7171 const charsPerLine = estimateCharsPerLineForAssistant(layout.timelineWidthPx);
7272 const estimatedLines = estimateWrappedLineCount(message.text, charsPerLine);
7373 return ASSISTANT_BASE_HEIGHT_PX + estimatedLines * LINE_HEIGHT_PX;
7474 }
7575
76 const charsPerLine = estimateCharsPerLineForUser(layout.timelineWidthPx);
76 if (message.role === "user") {
77 const charsPerLine = estimateCharsPerLineForUser(layout.timelineWidthPx);
78 const estimatedLines = estimateWrappedLineCount(message.text, charsPerLine);
79 const attachmentCount = message.attachments?.length ?? 0;
80 const attachmentRows = Math.ceil(attachmentCount / ATTACHMENTS_PER_ROW);
81 const attachmentHeight = attachmentRows * USER_ATTACHMENT_ROW_HEIGHT_PX;
82 return USER_BASE_HEIGHT_PX + estimatedLines * LINE_HEIGHT_PX + attachmentHeight;
83 }
84
85 // `system` messages are not rendered in the chat timeline, but keep a stable
86 // explicit branch in case they are present in timeline data.
87 const charsPerLine = estimateCharsPerLineForAssistant(layout.timelineWidthPx);
7788 const estimatedLines = estimateWrappedLineCount(message.text, charsPerLine);
78 const attachmentCount = message.attachments?.length ?? 0;
79 const attachmentRows = Math.ceil(attachmentCount / ATTACHMENTS_PER_ROW);
80 const attachmentHeight = attachmentRows * USER_ATTACHMENT_ROW_HEIGHT_PX;
81 return USER_BASE_HEIGHT_PX + estimatedLines * LINE_HEIGHT_PX + attachmentHeight;
89 return ASSISTANT_BASE_HEIGHT_PX + estimatedLines * LINE_HEIGHT_PX;
8290}