diff --git a/frontend/src/components/Chat/MessageList.styles.ts b/frontend/src/components/Chat/MessageList.styles.ts index 7273fa158..f650686e2 100644 --- a/frontend/src/components/Chat/MessageList.styles.ts +++ b/frontend/src/components/Chat/MessageList.styles.ts @@ -20,13 +20,15 @@ export const useMessageListStyles = makeStyles({ flexDirection: 'row-reverse', }, messageContent: { - backgroundColor: tokens.colorNeutralBackground3, + backgroundColor: tokens.colorNeutralBackground1, + border: `1px solid ${tokens.colorNeutralStroke2}`, padding: tokens.spacingVerticalM, borderRadius: tokens.borderRadiusMedium, flex: 1, }, userMessageContent: { - backgroundColor: tokens.colorBrandBackground, + backgroundColor: tokens.colorBrandBackground2, + border: `1px solid ${tokens.colorBrandStroke2}`, }, messageText: { whiteSpace: 'pre-wrap', diff --git a/frontend/src/components/Chat/MessageList.test.tsx b/frontend/src/components/Chat/MessageList.test.tsx index fd68e15ab..58bf2d761 100644 --- a/frontend/src/components/Chat/MessageList.test.tsx +++ b/frontend/src/components/Chat/MessageList.test.tsx @@ -87,6 +87,50 @@ describe("MessageList", () => { expect(screen.getByText("Assistant message test")).toBeInTheDocument(); }); + describe("bubble class composition", () => { + // Regression guard for an earlier bug where the user-bubble background + // override silently lost to the assistant-bubble base style. The cause was + // string-concatenated class names — Griffel's atomic CSS doesn't dedupe + // conflicting properties unless mergeClasses is used. We assert here that + // the two bubble containers receive distinguishable className strings, so + // the override always has a chance to win. + it("renders user and assistant bubbles with distinct class signatures", () => { + render( + + + + ); + const userBubble = screen.getByTestId("message-bubble-0"); + const assistantBubble = screen.getByTestId("message-bubble-1"); + // The two bubble class strings must differ — otherwise the user + // override silently lost (which was the original bug). + expect(userBubble.className).not.toBe(assistantBubble.className); + // Both bubbles must carry at least one style hook (catches a future + // refactor that drops the className entirely). + expect(userBubble.className.trim()).not.toBe(''); + expect(assistantBubble.className.trim()).not.toBe(''); + // The user bubble must carry at least one style hook the assistant + // bubble doesn't have — that's the override actually being applied. + const userClasses = userBubble.className.split(/\s+/).filter(Boolean); + const assistantClasses = new Set(assistantBubble.className.split(/\s+/).filter(Boolean)); + const userOnly = userClasses.filter(c => !assistantClasses.has(c)); + expect(userOnly.length).toBeGreaterThan(0); + }); + }); + it("should handle messages with image attachments", () => { const messagesWithAttachments: Message[] = [ { diff --git a/frontend/src/components/Chat/MessageList.tsx b/frontend/src/components/Chat/MessageList.tsx index 6e2a54086..77fdc740b 100644 --- a/frontend/src/components/Chat/MessageList.tsx +++ b/frontend/src/components/Chat/MessageList.tsx @@ -8,6 +8,7 @@ import { Button, Tooltip, Spinner, + mergeClasses, } from '@fluentui/react-components' import { ArrowDownloadRegular, ArrowReplyRegular, ArrowForwardRegular, ChatAddRegular, BranchForkRegular } from '@fluentui/react-icons' import { Message, MessageAttachment } from '../../types' @@ -138,13 +139,16 @@ export default function MessageList({ messages, onCopyToInput, onCopyToNewConver return (
-
+
{/* Error rendering */} {message.error && (