From 92b1fc48b4a78ad8fbd41c7644b5cd6c4042500e Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 6 May 2026 17:44:43 +0100 Subject: [PATCH] fix(site/src/pages/AgentsPage/components): right-align user chat bubbles (#25000) --- .../ConversationTimeline.stories.tsx | 39 +++++++++++++++++++ .../ChatConversation/ConversationTimeline.tsx | 5 ++- .../ChatConversation/UserMessageContent.tsx | 2 +- .../ChatElements/Conversation.stories.tsx | 2 +- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx index bb599a01e5..f518f525ce 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.stories.tsx @@ -235,6 +235,13 @@ const buildStoryArgs = (...messages: TypesGen.ChatMessage[]) => ({ parsedMessages: buildMessages(messages), }); +const LONG_USER_MESSAGE = [ + "This is a deliberately long user message that should stay pinned to the", + "right edge while the bubble stops short of filling the entire timeline", + "column. It gives the Storybook test enough content to exercise the", + "maximum width cap.", +].join(" "); + const findAttachmentTile = async ( canvas: ReturnType, label: string, @@ -291,6 +298,38 @@ const meta: Meta = { export default meta; type Story = StoryObj; +/** + * User bubbles should stay right-aligned, shrink to fit short content, + * and cap long content so the timeline keeps some breathing room. + */ +export const UserMessageBubbleAlignment: Story = { + args: buildStoryArgs( + buildUserMessage({ + text: LONG_USER_MESSAGE, + }), + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const messageText = canvas.getByText(/deliberately long user message/i); + const userRow = messageText.closest('[data-role="user"]'); + expect(userRow).not.toBeNull(); + + const bubble = userRow?.firstElementChild; + expect(bubble).not.toBeNull(); + + await userEvent.hover(userRow?.parentElement as HTMLElement); + const actions = await canvas.findByTestId("message-actions"); + + const rowRect = (userRow as HTMLElement).getBoundingClientRect(); + const bubbleRect = (bubble as HTMLElement).getBoundingClientRect(); + const actionsRect = actions.getBoundingClientRect(); + + expect(bubbleRect.width).toBeLessThanOrEqual(rowRect.width * 0.81); + expect(Math.abs(rowRect.right - bubbleRect.right)).toBeLessThanOrEqual(2); + expect(Math.abs(rowRect.right - actionsRect.right)).toBeLessThanOrEqual(2); + }, +}; + /** Regression guard: a single image attachment must not be duplicated. */ export const UserMessageWithSingleImage: Story = { args: { diff --git a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx index e157092f3e..a1df150a7a 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx @@ -592,7 +592,10 @@ const ChatMessageItem = memo<{ (displayState.hasCopyableContent || (isUser && onEditUserMessage)) && (
{displayState.hasCopyableContent && ( diff --git a/site/src/pages/AgentsPage/components/ChatConversation/UserMessageContent.tsx b/site/src/pages/AgentsPage/components/ChatConversation/UserMessageContent.tsx index eea1f2dc56..924bee9b0c 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/UserMessageContent.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/UserMessageContent.tsx @@ -43,7 +43,7 @@ export const UserMessageContent: FC<{ onTextFileClick, }) => { return ( - + - + Check why `git fetch` is failing in this workspace.