mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
fix(core): re-layout ai-chat-content to display preview panel (#13030)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a split-view layout in the AI chat, allowing chat content and a preview panel to be displayed side-by-side. * Added responsive padding and layout adjustments for improved chat panel appearance. * **Refactor** * Simplified the chat panel by removing the previous preview panel feature and related state from the main chat component. * Updated internal logic to support the new split-view structure. * **Style** * Adjusted chat panel and workspace chat page styles for better layout consistency and responsiveness. * **Chores** * Improved code organization and import statements for maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -13,16 +13,9 @@ import { ShadowlessElement } from '@blocksuite/affine/std';
|
||||
import type { ExtensionType, Store } from '@blocksuite/affine/store';
|
||||
import { CenterPeekIcon } from '@blocksuite/icons/lit';
|
||||
import { type Signal, signal } from '@preact/signals-core';
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
type PropertyValues,
|
||||
type TemplateResult,
|
||||
} from 'lit';
|
||||
import { css, html, nothing, type PropertyValues } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type {
|
||||
DocDisplayConfig,
|
||||
@@ -111,12 +104,6 @@ export class ChatPanel extends SignalWatcher(
|
||||
@state()
|
||||
accessor embeddingProgress: [number, number] = [0, 0];
|
||||
|
||||
@state()
|
||||
accessor showPreviewPanel = false;
|
||||
|
||||
@state()
|
||||
accessor previewPanelContent: TemplateResult<1> | null = null;
|
||||
|
||||
private isSidebarOpen: Signal<boolean | undefined> = signal(false);
|
||||
|
||||
private sidebarWidth: Signal<number | undefined> = signal(undefined);
|
||||
@@ -269,7 +256,6 @@ export class ChatPanel extends SignalWatcher(
|
||||
private readonly resetPanel = () => {
|
||||
this.session = undefined;
|
||||
this.embeddingProgress = [0, 0];
|
||||
this.showPreviewPanel = false;
|
||||
this.hasPinned = false;
|
||||
};
|
||||
|
||||
@@ -350,12 +336,7 @@ export class ChatPanel extends SignalWatcher(
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const width = this.sidebarWidth.value || 0;
|
||||
const style = styleMap({
|
||||
padding: width > 540 ? '8px 24px 0 24px' : '8px 12px 0 12px',
|
||||
});
|
||||
|
||||
const left = html`<div class="chat-panel-container" style=${style}>
|
||||
return html`<div class="chat-panel-container">
|
||||
${keyed(
|
||||
this.hasPinned ? this.session?.id : this.doc.id,
|
||||
html`<ai-chat-content
|
||||
@@ -378,15 +359,6 @@ export class ChatPanel extends SignalWatcher(
|
||||
></ai-chat-content>`
|
||||
)}
|
||||
</div>`;
|
||||
|
||||
const right = this.previewPanelContent;
|
||||
|
||||
return html`<chat-panel-split-view
|
||||
.left=${left}
|
||||
.right=${right}
|
||||
.open=${this.showPreviewPanel}
|
||||
>
|
||||
</chat-panel-split-view>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ export class ChatPanelSplitView extends SignalWatcher(
|
||||
width: var(--gap);
|
||||
position: relative;
|
||||
}
|
||||
.ai-chat-panel-split-view-divider[data-open='false'] {
|
||||
.ai-chat-panel-split-view[data-open='false']
|
||||
.ai-chat-panel-split-view-divider {
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -88,6 +88,27 @@ export class AIChatContent extends SignalWatcher(
|
||||
overflow-y: visible;
|
||||
}
|
||||
}
|
||||
chat-panel-split-view {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
container-type: inline-size;
|
||||
container-name: chat-panel-split-view;
|
||||
}
|
||||
.chat-panel-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 8px 24px 0 24px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@container chat-panel-split-view (width < 540px) {
|
||||
.chat-panel-main {
|
||||
padding: 8px 12px 0 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@property({ attribute: false })
|
||||
@@ -152,6 +173,12 @@ export class AIChatContent extends SignalWatcher(
|
||||
@state()
|
||||
accessor isHistoryLoading = false;
|
||||
|
||||
@state()
|
||||
accessor showPreviewPanel = false;
|
||||
|
||||
@state()
|
||||
accessor previewPanelContent: TemplateResult<1> | null = null;
|
||||
|
||||
private readonly chatMessagesRef: Ref<AIChatMessages> =
|
||||
createRef<AIChatMessages>();
|
||||
|
||||
@@ -311,6 +338,8 @@ export class AIChatContent extends SignalWatcher(
|
||||
|
||||
public reset() {
|
||||
this.updateContext(DEFAULT_CHAT_CONTEXT_VALUE);
|
||||
this.showPreviewPanel = false;
|
||||
this.previewPanelContent = null;
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
@@ -349,7 +378,7 @@ export class AIChatContent extends SignalWatcher(
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`${this.chatTitle
|
||||
const left = html`${this.chatTitle
|
||||
? html`<div class="ai-chat-title">${this.chatTitle}</div>`
|
||||
: nothing}
|
||||
<ai-chat-messages
|
||||
@@ -399,5 +428,14 @@ export class AIChatContent extends SignalWatcher(
|
||||
control: 'chat-send',
|
||||
}}
|
||||
></ai-chat-composer>`;
|
||||
|
||||
const right = this.previewPanelContent;
|
||||
|
||||
return html`<chat-panel-split-view
|
||||
.left=${html`<div class="chat-panel-main">${left}</div>`}
|
||||
.right=${right}
|
||||
.open=${this.showPreviewPanel}
|
||||
>
|
||||
</chat-panel-split-view>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import { EmptyIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, nothing, type TemplateResult } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { AIChatContent } from '../ai-chat-content';
|
||||
|
||||
function getChatPanel(target: HTMLElement) {
|
||||
return target.closest('chat-panel');
|
||||
return target.closest('ai-chat-content') as AIChatContent;
|
||||
}
|
||||
|
||||
export const isPreviewPanelOpen = (target: HTMLElement) => {
|
||||
|
||||
@@ -3,9 +3,6 @@ import { style } from '@vanilla-extract/css';
|
||||
export const chatRoot = style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxWidth: 800,
|
||||
padding: '0px 16px',
|
||||
margin: '0 auto',
|
||||
});
|
||||
|
||||
export const chatHeader = style({
|
||||
|
||||
@@ -188,16 +188,18 @@ export const Component = () => {
|
||||
|
||||
tool.session = currentSession;
|
||||
|
||||
tool.onNewSession = () => {
|
||||
if (!currentSession) return;
|
||||
setCurrentSession(null);
|
||||
chatContent?.reset();
|
||||
};
|
||||
|
||||
tool.onTogglePin = async () => {
|
||||
await togglePin();
|
||||
};
|
||||
|
||||
// initial props
|
||||
if (!chatTool) {
|
||||
tool.onNewSession = () => {
|
||||
if (!currentSession) return;
|
||||
setCurrentSession(null);
|
||||
chatContent?.reset();
|
||||
};
|
||||
tool.onTogglePin = async () => {
|
||||
await togglePin();
|
||||
};
|
||||
// mount
|
||||
chatToolContainerRef.current.append(tool);
|
||||
setChatTool(tool);
|
||||
|
||||
Reference in New Issue
Block a user