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.
This commit is contained in:
Jaayden Halko
2026-04-22 08:04:55 +01:00
committed by GitHub
parent 353e522614
commit b62881eb85
3 changed files with 39 additions and 5 deletions
@@ -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();
},
};
@@ -499,10 +499,9 @@ const ChatMessageItem = memo<{
)}
</div>
)}
{/* 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 && <div className="min-h-6" />}
{displayState.needsAssistantBottomSpacer && (
<div className="min-h-6" data-testid="assistant-bottom-spacer" />
)}
{previewImage && (
<ImageLightbox
src={previewImage}
@@ -58,11 +58,17 @@ export const deriveMessageDisplayState = ({
userInlineContent.length > 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 &&