fix: align chat messages and composer without horizontal overflow

Move message avatars into outer gutters and align message content with the composer while preserving existing colors and styling. Keep scrollbar space in the outer gutter to prevent horizontal scrolling.

Release note: Chat avatars now sit outside message content, with the composer aligned to message edges and no extra horizontal scrollbar.
This commit is contained in:
mesalogo
2026-09-09 01:51:27 +08:00
parent cbb6398851
commit 177ab2140e
5 changed files with 44 additions and 17 deletions
+6 -4
View File
@@ -116,11 +116,11 @@ describe('ChatTimeline', () => {
expect(rule).not.toContain('contain-intrinsic')
})
it('places message identity above the message body', () => {
it.each(['assistant', 'user'] as const)('places the %s avatar outside the header and body', (role) => {
const messages: Message[] = [
{
id: 'assistant-message',
role: 'assistant',
role,
content: 'Answer',
createdAt: 1_775_000_000_000,
state: 'complete'
@@ -145,9 +145,11 @@ describe('ChatTimeline', () => {
const header = message?.querySelector('.message__header')
const body = message?.querySelector('.message__body')
expect(header).toContainElement(message?.querySelector('.message__avatar') ?? null)
const avatar = message?.querySelector('.message__avatar')
expect(avatar).toHaveAttribute('aria-hidden', 'true')
expect(header).toContainElement(message?.querySelector('.message__meta') ?? null)
expect(Array.from(message?.children ?? [])).toEqual([header, body])
expect(Array.from(message?.children ?? [])).toEqual([avatar, header, body])
})
it('enables HTML rendering only for completed Agent output', () => {
+7 -7
View File
@@ -553,14 +553,14 @@ function ChatMessageRowView({
ref={(element) => onArticleRef(message.id, element)}
tabIndex={-1}
>
<div aria-hidden="true" className="message__avatar">
{message.role === 'assistant' ? (
<Bot size={18} />
) : (
<UserRound size={18} />
)}
</div>
<div className="message__header">
<div className="message__avatar">
{message.role === 'assistant' ? (
<Bot size={18} />
) : (
<UserRound size={18} />
)}
</div>
<div className="message__meta">
<strong>
{message.role === 'assistant' ? 'GoodBuddy' : t('chat.user')}
@@ -248,7 +248,7 @@ describe('WorkspacePrimitives', () => {
/@media \(max-width: 719px\)\s*\{(?:[^{}]|\{[^{}]*\})*?\.assistant-sidebar/u
)
expect(stylesheet).toMatch(
/\.composer-wrap\s*\{[^}]*width:\s*100%;[^}]*max-width:\s*100%;[^}]*min-width:\s*0;[^}]*padding:\s*var\(--space-3\)\s*max\(var\(--page-gutter\),\s*calc\(\(100% - var\(--content-reading\)\) \/ 2\)\)\s*var\(--space-2\);/u
/\.composer-wrap\s*\{[^}]*width:\s*100%;[^}]*max-width:\s*100%;[^}]*min-width:\s*0;[^}]*padding:\s*var\(--space-3\)\s*var\(--chat-inline-padding\)\s*var\(--space-2\);/u
)
expect(stylesheet).toMatch(
/\.assistant-sidebar__tabs\s*\{[^}]*grid-template-columns:\s*repeat\(5,\s*minmax\(max-content,\s*1fr\)\);/u
+28 -5
View File
@@ -5190,12 +5190,21 @@ button > svg {
display: none;
}
.chat,
.composer-wrap {
--message-avatar-size: 29px;
--chat-inline-padding: max(
calc(var(--space-4) + var(--message-avatar-size) + var(--space-2)),
calc((100cqw - var(--content-reading)) / 2)
);
}
.chat {
height: 100%;
min-height: 0;
padding:
var(--page-gutter)
max(var(--page-gutter), calc((100% - var(--content-reading)) / 2))
0
10px;
overflow-y: auto;
background: var(--surface-raised);
@@ -5333,6 +5342,13 @@ button > svg {
line-height: 1.5;
}
.chat > .welcome,
.message-list {
/* Keep the scrollbar inside the outer gutter without adding trailing scroll padding. */
width: calc(100cqw - 2 * var(--chat-inline-padding));
margin-left: var(--chat-inline-padding);
}
.message-list {
display: flex;
min-width: 0;
@@ -5363,9 +5379,10 @@ button > svg {
}
.message {
position: relative;
display: flex;
min-width: 0;
padding: 17px 8px;
padding: 17px 0;
flex-direction: column;
gap: 8px;
}
@@ -5373,6 +5390,7 @@ button > svg {
.message__header {
display: flex;
min-width: 0;
min-height: var(--message-avatar-size);
align-items: center;
gap: 12px;
}
@@ -5434,10 +5452,13 @@ button > svg {
}
.message__avatar {
position: absolute;
top: 17px;
left: calc(-1 * (var(--message-avatar-size) + var(--space-2)));
display: grid;
flex: 0 0 auto;
width: 29px;
height: 29px;
width: var(--message-avatar-size);
height: var(--message-avatar-size);
place-items: center;
border: 1px solid var(--border-default);
border-radius: 9px;
@@ -5446,6 +5467,8 @@ button > svg {
}
.message--user .message__avatar {
right: calc(-1 * (var(--message-avatar-size) + var(--space-2)));
left: auto;
border-color: var(--accent-selected);
background: var(--accent-subtle);
color: var(--accent);
@@ -6511,7 +6534,7 @@ button > svg {
min-width: 0;
padding:
var(--space-3)
max(var(--page-gutter), calc((100% - var(--content-reading)) / 2))
var(--chat-inline-padding)
var(--space-2);
background: var(--surface-raised);
}