From b62881eb85d06f93a77007f1e40ea662d6faf0c3 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Wed, 22 Apr 2026 14:04:55 +0700 Subject: [PATCH] fix(site): add bottom spacing for no-renderable assistant fallback messages (#24551) closes CODAGT-125 Assistant messages that show only the fallback text ("Message has no renderable content.") were missing bottom spacing before the next user bubble, because `needsAssistantBottomSpacer` only covered reasoning-only and sources-only cases. Extend the spacer predicate to also trigger when `!hasRenderableContent`, and add a `data-testid` to the spacer element for testability. A new Storybook story (`NoRenderableContentFallbackSpacing`) covers this regression. --- .../ConversationTimeline.stories.tsx | 29 +++++++++++++++++++ .../ChatConversation/ConversationTimeline.tsx | 7 ++--- .../ChatConversation/messageHelpers.ts | 8 ++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx index ce1cfc0345..a7dfdf35a3 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx @@ -1391,3 +1391,32 @@ export const SourcesOnlyAssistantSpacing: Story = { ).toBeInTheDocument(); }, }; + +export const NoRenderableContentFallbackSpacing: Story = { + args: { + ...defaultArgs, + parsedMessages: buildMessages([ + { + ...baseMessage, + id: 101, + role: "assistant", + content: [], + }, + { + ...baseMessage, + id: 102, + role: "user", + content: [{ type: "text", text: "Thanks for trying!" }], + }, + ]), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + expect( + canvas.getByText("Message has no renderable content."), + ).toBeInTheDocument(); + expect( + document.querySelector('[data-testid="assistant-bottom-spacer"]'), + ).toBeInTheDocument(); + }, +}; diff --git a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx index 3185079aa2..54b0ace204 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx @@ -499,10 +499,9 @@ const ChatMessageItem = memo<{ )} )} - {/* Spacer for assistant messages without an action bar - (e.g. reasoning-only or sources-only) so they have - consistent bottom padding before the next user bubble. */} - {displayState.needsAssistantBottomSpacer &&
} + {displayState.needsAssistantBottomSpacer && ( +
+ )} {previewImage && ( 0 || Boolean(parsed.markdown.trim()); const hasFileBlocks = userFileBlocks.length > 0; const hasCopyableContent = Boolean(parsed.markdown.trim()); + const hasRenderableContent = + parsed.blocks.length > 0 || + parsed.tools.length > 0 || + parsed.sources.length > 0; const needsAssistantBottomSpacer = !hideActions && !isUser && !hasCopyableContent && - (Boolean(parsed.reasoning) || parsed.sources.length > 0); + (Boolean(parsed.reasoning) || + parsed.sources.length > 0 || + !hasRenderableContent); const hasToolResultsOnly = parsed.toolResults.length > 0 && parsed.toolCalls.length === 0 &&