fix(core): text style is not centered when chat-panel is wide (#10607)

Fix issue [BS-2751](https://linear.app/affine-design/issue/BS-2751).

Before:

![截屏2025-03-04 16.56.46.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/d0aae342-ff53-42d6-964b-ecd2e8720af6.png)

After:

![截屏2025-03-04 16.56.57.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/986bde63-6fe8-485d-97d1-0a062658cf7c.png)
This commit is contained in:
akumatus
2025-03-04 15:25:35 +00:00
parent 66d9d576e0
commit a6692f70aa
4 changed files with 77 additions and 54 deletions

View File

@@ -11,7 +11,6 @@ import type { BaseSelection } from '@blocksuite/affine/store';
import { css, html, nothing } from 'lit'; import { css, html, nothing } from 'lit';
import { property, query, state } from 'lit/decorators.js'; import { property, query, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
import { styleMap } from 'lit/directives/style-map.js';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { import {
@@ -50,7 +49,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
overflow-y: auto; overflow-y: auto;
} }
.chat-panel-messages-placeholder { .messages-placeholder {
width: 100%; width: 100%;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
@@ -63,6 +62,47 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
gap: 12px; gap: 12px;
} }
.messages-placeholder-title {
font-size: 18px;
font-weight: 600;
color: var(--affine-text-primary-color);
}
.messages-placeholder-title[data-loading='true'] {
font-size: var(--affine-font-sm);
color: var(--affine-text-secondary-color);
}
.onboarding-wrapper {
display: flex;
gap: 8px;
flex-direction: column;
margin-top: 16px;
}
.onboarding-item {
display: flex;
height: 28px;
gap: 8px;
align-items: center;
justify-content: start;
cursor: pointer;
}
.onboarding-item-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.onboarding-item-text {
font-size: var(--affine-font-xs);
font-weight: 400;
color: var(--affine-text-primary-color);
white-space: nowrap;
}
.item-wrapper { .item-wrapper {
margin-left: 32px; margin-left: 32px;
} }
@@ -73,14 +113,14 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
gap: 10px; gap: 10px;
margin-bottom: 4px; margin-bottom: 4px;
color: var(--affine-text-primary-color); color: var(--affine-text-primary-color);
font-size: 14px; font-size: var(--affine-font-sm);
font-weight: 500; font-weight: 500;
user-select: none; user-select: none;
} }
.message-info { .message-info {
color: var(--affine-placeholder-color); color: var(--affine-placeholder-color);
font-size: 12px; font-size: var(--affine-font-xs);
font-weight: 400; font-weight: 400;
} }
@@ -126,7 +166,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
accessor _selectionValue: BaseSelection[] = []; accessor _selectionValue: BaseSelection[] = [];
@state() @state()
accessor showDownIndicator = false; accessor canScrollDown = false;
@state() @state()
accessor avatarUrl = ''; accessor avatarUrl = '';
@@ -156,46 +196,32 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
return this.messagesContainer; return this.messagesContainer;
} }
get showDownIndicator() {
if (!this.messagesContainer) return false;
const { clientHeight, scrollTop, scrollHeight } = this.messagesContainer;
const canScrollDown = scrollHeight - scrollTop - clientHeight > 200;
const showDownIndicator =
canScrollDown &&
this.chatContextValue.items.length > 0 &&
this.chatContextValue.status !== 'transmitting';
return showDownIndicator;
}
private _renderAIOnboarding() { private _renderAIOnboarding() {
return this.isLoading || return this.isLoading ||
!this.host?.doc.get(FeatureFlagService).getFlag('enable_ai_onboarding') !this.host?.doc.get(FeatureFlagService).getFlag('enable_ai_onboarding')
? nothing ? nothing
: html`<div : html`<div class="onboarding-wrapper">
style=${styleMap({
display: 'flex',
gap: '8px',
flexDirection: 'column',
alignItems: 'center',
marginTop: '16px',
width: '100%',
})}
>
${repeat( ${repeat(
AIPreloadConfig, AIPreloadConfig,
config => config.text, config => config.text,
config => { config => {
return html`<div return html`<div
@click=${() => config.handler()} @click=${() => config.handler()}
style=${styleMap({ class="onboarding-item"
display: 'flex',
height: '28px',
gap: '8px',
width: '88%',
alignItems: 'center',
justifyContent: 'start',
cursor: 'pointer',
})}
> >
${config.icon} <div class="onboarding-item-icon">${config.icon}</div>
<div <div class="onboarding-item-text">${config.text}</div>
style=${styleMap({
fontSize: '12px',
fontWeight: '400',
color: 'var(--affine-text-primary-color)',
})}
>
${config.text}
</div>
</div>`; </div>`;
} }
)} )}
@@ -205,7 +231,7 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
private readonly _onScroll = () => { private readonly _onScroll = () => {
if (!this.messagesContainer) return; if (!this.messagesContainer) return;
const { clientHeight, scrollTop, scrollHeight } = this.messagesContainer; const { clientHeight, scrollTop, scrollHeight } = this.messagesContainer;
this.showDownIndicator = scrollHeight - scrollTop - clientHeight > 200; this.canScrollDown = scrollHeight - scrollTop - clientHeight > 200;
}; };
private readonly _debouncedOnScroll = debounce( private readonly _debouncedOnScroll = debounce(
@@ -225,28 +251,19 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
); );
}); });
return html`<style> return html`
.chat-panel-messages-placeholder div {
color: ${isLoading
? 'var(--affine-text-secondary-color)'
: 'var(--affine-text-primary-color)'};
font-size: ${isLoading ? 'var(--affine-font-sm)' : '18px'};
font-weight: 600;
}
</style>
<div <div
class="chat-panel-messages" class="chat-panel-messages"
@scroll=${() => this._debouncedOnScroll()} @scroll=${() => this._debouncedOnScroll()}
> >
${items.length === 0 ${filteredItems.length === 0
? html`<div class="chat-panel-messages-placeholder"> ? html`<div class="messages-placeholder">
${AffineIcon( ${AffineIcon(
isLoading isLoading
? 'var(--affine-icon-secondary)' ? 'var(--affine-icon-secondary)'
: 'var(--affine-primary-color)' : 'var(--affine-primary-color)'
)} )}
<div> <div class="messages-placeholder-title" data-loading=${isLoading}>
${this.isLoading ${this.isLoading
? 'AFFiNE AI is loading history...' ? 'AFFiNE AI is loading history...'
: 'What can I help you with?'} : 'What can I help you with?'}
@@ -267,11 +284,12 @@ export class ChatPanelMessages extends WithDisposable(ShadowlessElement) {
} }
)} )}
</div> </div>
${this.showDownIndicator && filteredItems.length > 1 ${this.showDownIndicator && filteredItems.length > 0
? html`<div class="down-indicator" @click=${this.scrollToEnd}> ? html`<div class="down-indicator" @click=${this.scrollToEnd}>
${DownArrowIcon} ${DownArrowIcon}
</div>` </div>`
: nothing} `; : nothing}
`;
} }
override connectedCallback() { override connectedCallback() {

View File

@@ -133,7 +133,6 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
private readonly _updateHistory = async () => { private readonly _updateHistory = async () => {
const { doc } = this; const { doc } = this;
this.isLoading = true;
const currentRequest = ++this._updateHistoryCounter; const currentRequest = ++this._updateHistoryCounter;
@@ -164,7 +163,6 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
), ),
}; };
this.isLoading = false;
this._scrollToEnd(); this._scrollToEnd();
}; };
@@ -253,7 +251,7 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
accessor previewSpecBuilder!: SpecBuilder; accessor previewSpecBuilder!: SpecBuilder;
@state() @state()
accessor isLoading = false; accessor isLoading = true;
@state() @state()
accessor chatContextValue: ChatContextValue = DEFAULT_CHAT_CONTEXT_VALUE; accessor chatContextValue: ChatContextValue = DEFAULT_CHAT_CONTEXT_VALUE;
@@ -301,6 +299,7 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
const userId = (await AIProvider.userInfo)?.id; const userId = (await AIProvider.userInfo)?.id;
if (!userId) return; if (!userId) return;
this.isLoading = true;
const sessions = await AIProvider.session?.getSessions( const sessions = await AIProvider.session?.getSessions(
this.doc.workspace.id, this.doc.workspace.id,
this.doc.id this.doc.id
@@ -309,6 +308,7 @@ export class ChatPanel extends WithDisposable(ShadowlessElement) {
this._chatSessionId = sessions?.[0].id; this._chatSessionId = sessions?.[0].id;
await this._updateHistory(); await this._updateHistory();
} }
this.isLoading = false;
if (this._chatSessionId) { if (this._chatSessionId) {
this._chatContextId = await AIProvider.context?.getContextId( this._chatContextId = await AIProvider.context?.getContextId(
this.doc.workspace.id, this.doc.workspace.id,

View File

@@ -313,6 +313,11 @@ export class TextRenderer extends WithDisposable(ShadowlessElement) {
); );
// Apply min-height to prevent shrinking // Apply min-height to prevent shrinking
this._container.style.minHeight = `${this._maxContainerHeight}px`; this._container.style.minHeight = `${this._maxContainerHeight}px`;
} else {
setTimeout(() => {
this._maxContainerHeight = 0;
this._container.style.minHeight = '';
}, 500);
} }
}); });
} }

View File

@@ -106,7 +106,7 @@ const clearChat = async (page: Page) => {
const collectChat = async (page: Page) => { const collectChat = async (page: Page) => {
await page.waitForTimeout(ONE_SECOND); await page.waitForTimeout(ONE_SECOND);
const chatPanel = await page.waitForSelector('.chat-panel-messages'); const chatPanel = await page.waitForSelector('.chat-panel-messages');
if (await chatPanel.$('.chat-panel-messages-placeholder')) { if (await chatPanel.$('.messages-placeholder')) {
return []; return [];
} }
// wait ai response // wait ai response