refactor(core): simplify chat panel user message rendering (#10910)

### TL;DR
Refactor style of  user chat message

> CLOSE AF-2323 AF-2324 AF-2325

### What Changed
* Refactor style of user message.
* Refactor style of image message.
This commit is contained in:
yoyoyohamapi
2025-03-20 04:59:10 +00:00
parent e83f7eba00
commit 5aa36efab0
3 changed files with 117 additions and 86 deletions
@@ -114,10 +114,6 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
white-space: nowrap;
}
.message {
display: contents;
}
.down-indicator {
position: absolute;
left: 50%;
@@ -249,25 +245,20 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
(_, index) => index,
(item, index) => {
const isLast = index === filteredItems.length - 1;
return html`<div class="message">
${isChatMessage(item) && item.role === 'user'
? html`<chat-panel-user-message
.host=${this.host}
.item=${item}
.avatarUrl=${this.avatarUrl}
.previewSpecBuilder=${this.previewSpecBuilder}
></chat-panel-user-message>`
: html`<chat-panel-assistant-message
.host=${this.host}
.item=${item}
.isLast=${isLast}
.status=${status}
.error=${error}
.previewSpecBuilder=${this.previewSpecBuilder}
.getSessionId=${this.getSessionId}
.retry=${() => this.retry()}
></chat-panel-assistant-message>`}
</div>`;
return isChatMessage(item) && item.role === 'user'
? html`<chat-panel-user-message
.item=${item}
></chat-panel-user-message>`
: html`<chat-panel-assistant-message
.host=${this.host}
.item=${item}
.isLast=${isLast}
.status=${status}
.error=${error}
.previewSpecBuilder=${this.previewSpecBuilder}
.getSessionId=${this.getSessionId}
.retry=${() => this.retry()}
></chat-panel-assistant-message>`;
}
)}
</div>
@@ -1,77 +1,100 @@
import type { EditorHost } from '@blocksuite/affine/block-std';
import { ShadowlessElement } from '@blocksuite/affine/block-std';
import { WithDisposable } from '@blocksuite/affine/global/lit';
import { css, html, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { type ChatItem, isChatMessage } from './chat-context';
import { type ChatMessage } from './chat-context';
export class ChatPanelUserMessage extends WithDisposable(ShadowlessElement) {
static override styles = css`
.avatar-container {
width: 24px;
height: 24px;
}
.chat-user-message {
display: flex;
flex-direction: column;
align-items: flex-end;
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: var(--affine-primary-color);
}
.images {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 8px;
margin-bottom: 8px;
max-width: 100%;
overflow-x: auto;
padding: 4px;
scrollbar-width: auto;
}
.avatar-container img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
.images::-webkit-scrollbar {
height: 4px;
}
.images::-webkit-scrollbar-thumb {
background-color: var(--affine-border-color);
border-radius: 4px;
}
.images::-webkit-scrollbar-track {
background: transparent;
}
img {
max-width: 180px;
max-height: 264px;
object-fit: cover;
border-radius: 8px;
flex-shrink: 0;
}
.text-content {
display: inline-block;
text-align: left;
max-width: 800px;
max-height: 500px;
overflow-y: auto;
background: var(--affine-v2-aI-userTextBackground);
border-radius: 8px;
padding: 12px;
white-space: pre-wrap;
word-wrap: break-word;
}
}
`;
@property({ attribute: false })
accessor host!: EditorHost;
accessor item!: ChatMessage;
@property({ attribute: false })
accessor item!: ChatItem;
renderImages(images: string[]) {
return images.length > 0
? html`<div class="images">
${repeat(
images,
image => image,
image => {
return html`<img src="${image}" />`;
}
)}
</div>`
: nothing;
}
@property({ attribute: false })
accessor avatarUrl: string = '';
@property({ attribute: false })
accessor previewSpecBuilder: any;
renderAvatar() {
return html`<div class="user-info">
<div class="avatar-container">
${this.avatarUrl
? html`<img .src=${this.avatarUrl} />`
: html`<div class="avatar"></div>`}
</div>
You
</div>`;
renderText(text: string) {
return text.length > 0
? html`<div class="text-content">${text}</div>`
: nothing;
}
renderContent() {
const { host, item } = this;
const { item } = this;
if (isChatMessage(item)) {
return html`<chat-text
.host=${host}
.attachments=${item.attachments}
.text=${item.content}
.state=${'finished'}
.previewSpecBuilder=${this.previewSpecBuilder}
></chat-text>`;
}
return nothing;
const imagesRendered = item.attachments
? this.renderImages(item.attachments)
: nothing;
return html` ${imagesRendered} ${this.renderText(item.content)} `;
}
protected override render() {
return html`
${this.renderAvatar()}
<div class="item-wrapper">${this.renderContent()}</div>
`;
return html` <div class="chat-user-message">${this.renderContent()}</div> `;
}
}
+29 -12
View File
@@ -106,13 +106,25 @@ const clearChat = async (page: Page) => {
const collectHistory = async (page: Page) => {
const chatPanel = await page.waitForSelector('.chat-panel-messages');
return Promise.all(
Array.from(await chatPanel.$$('.message')).map(async m => ({
name: await m.$('.user-info').then(i => i?.innerText()),
content: await m
.$('chat-text')
.then(t => t?.$('editor-host'))
.then(e => e?.innerText()),
}))
Array.from(
await chatPanel.$$('chat-panel-user-message,chat-panel-assistant-message')
).map(async m => {
const isAssistant = await m.evaluate(
el => el.tagName === 'CHAT-PANEL-ASSISTANT-MESSAGE'
);
return isAssistant
? {
name: await m.$('.user-info').then(i => i?.innerText()),
content: await m
.$('chat-text')
.then(t => t?.$('editor-host'))
.then(e => e?.innerText()),
}
: {
name: 'You',
content: await m.$('.text-content').then(i => i?.innerText()),
};
})
);
};
@@ -123,12 +135,17 @@ const collectChat = async (page: Page) => {
return [];
}
// wait ai response
await page.waitForSelector('.chat-panel-messages .message chat-copy-more', {
timeout: ONE_MINUTE,
});
await page.waitForSelector(
'.chat-panel-messages chat-panel-assistant-message chat-copy-more',
{
timeout: ONE_MINUTE,
}
);
await page.waitForTimeout(200);
const lastMessage = await chatPanel.$$('.message').then(m => m[m.length - 1]);
await lastMessage.waitForSelector('chat-copy-more');
await page
.locator('.chat-panel-messages > chat-panel-assistant-message')
.last()
.locator('chat-copy-more');
await page.waitForTimeout(200);
return collectHistory(page);
};