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

View File

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

View File

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