From 5aa36efab000ce2978e484c04468a8850c77c81c Mon Sep 17 00:00:00 2001 From: yoyoyohamapi <8338436+yoyoyohamapi@users.noreply.github.com> Date: Thu, 20 Mar 2025 04:59:10 +0000 Subject: [PATCH] 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. --- .../ai/chat-panel/chat-panel-messages.ts | 37 ++---- .../ai/chat-panel/chat-panel-user-message.ts | 125 +++++++++++------- .../affine-cloud-copilot/e2e/copilot.spec.ts | 41 ++++-- 3 files changed, 117 insertions(+), 86 deletions(-) diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts index 6ccd8f1d26..1e642c0bd0 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-messages.ts @@ -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`
- ${isChatMessage(item) && item.role === 'user' - ? html`` - : html` this.retry()} - >`} -
`; + return isChatMessage(item) && item.role === 'user' + ? html`` + : html` this.retry()} + >`; } )} diff --git a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-user-message.ts b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-user-message.ts index 784a93d6c4..8715bd9041 100644 --- a/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-user-message.ts +++ b/packages/frontend/core/src/blocksuite/ai/chat-panel/chat-panel-user-message.ts @@ -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`
+ ${repeat( + images, + image => image, + image => { + return html``; + } + )} +
` + : nothing; + } - @property({ attribute: false }) - accessor avatarUrl: string = ''; - - @property({ attribute: false }) - accessor previewSpecBuilder: any; - - renderAvatar() { - return html`
-
- ${this.avatarUrl - ? html`` - : html`
`} -
- You -
`; + renderText(text: string) { + return text.length > 0 + ? html`
${text}
` + : nothing; } renderContent() { - const { host, item } = this; + const { item } = this; - if (isChatMessage(item)) { - return html``; - } - - 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()} -
${this.renderContent()}
- `; + return html`
${this.renderContent()}
`; } } diff --git a/tests/affine-cloud-copilot/e2e/copilot.spec.ts b/tests/affine-cloud-copilot/e2e/copilot.spec.ts index 086771cd5f..f7b24b6fb2 100644 --- a/tests/affine-cloud-copilot/e2e/copilot.spec.ts +++ b/tests/affine-cloud-copilot/e2e/copilot.spec.ts @@ -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); };