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:
Cats Juice
2025-07-04 16:00:24 +08:00
committed by GitHub
parent b9c4d7230e
commit 882d06b359
6 changed files with 56 additions and 44 deletions

View File

@@ -13,16 +13,9 @@ import { ShadowlessElement } from '@blocksuite/affine/std';
import type { ExtensionType, Store } from '@blocksuite/affine/store'; import type { ExtensionType, Store } from '@blocksuite/affine/store';
import { CenterPeekIcon } from '@blocksuite/icons/lit'; import { CenterPeekIcon } from '@blocksuite/icons/lit';
import { type Signal, signal } from '@preact/signals-core'; import { type Signal, signal } from '@preact/signals-core';
import { import { css, html, nothing, type PropertyValues } from 'lit';
css,
html,
nothing,
type PropertyValues,
type TemplateResult,
} from 'lit';
import { property, state } from 'lit/decorators.js'; import { property, state } from 'lit/decorators.js';
import { keyed } from 'lit/directives/keyed.js'; import { keyed } from 'lit/directives/keyed.js';
import { styleMap } from 'lit/directives/style-map.js';
import type { import type {
DocDisplayConfig, DocDisplayConfig,
@@ -111,12 +104,6 @@ export class ChatPanel extends SignalWatcher(
@state() @state()
accessor embeddingProgress: [number, number] = [0, 0]; 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 isSidebarOpen: Signal<boolean | undefined> = signal(false);
private sidebarWidth: Signal<number | undefined> = signal(undefined); private sidebarWidth: Signal<number | undefined> = signal(undefined);
@@ -269,7 +256,6 @@ export class ChatPanel extends SignalWatcher(
private readonly resetPanel = () => { private readonly resetPanel = () => {
this.session = undefined; this.session = undefined;
this.embeddingProgress = [0, 0]; this.embeddingProgress = [0, 0];
this.showPreviewPanel = false;
this.hasPinned = false; this.hasPinned = false;
}; };
@@ -350,12 +336,7 @@ export class ChatPanel extends SignalWatcher(
return nothing; return nothing;
} }
const width = this.sidebarWidth.value || 0; return html`<div class="chat-panel-container">
const style = styleMap({
padding: width > 540 ? '8px 24px 0 24px' : '8px 12px 0 12px',
});
const left = html`<div class="chat-panel-container" style=${style}>
${keyed( ${keyed(
this.hasPinned ? this.session?.id : this.doc.id, this.hasPinned ? this.session?.id : this.doc.id,
html`<ai-chat-content html`<ai-chat-content
@@ -378,15 +359,6 @@ export class ChatPanel extends SignalWatcher(
></ai-chat-content>` ></ai-chat-content>`
)} )}
</div>`; </div>`;
const right = this.previewPanelContent;
return html`<chat-panel-split-view
.left=${left}
.right=${right}
.open=${this.showPreviewPanel}
>
</chat-panel-split-view>`;
} }
} }

View File

@@ -46,7 +46,8 @@ export class ChatPanelSplitView extends SignalWatcher(
width: var(--gap); width: var(--gap);
position: relative; 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; width: 0;
visibility: hidden; visibility: hidden;
pointer-events: none; pointer-events: none;

View File

@@ -88,6 +88,27 @@ export class AIChatContent extends SignalWatcher(
overflow-y: visible; 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 }) @property({ attribute: false })
@@ -152,6 +173,12 @@ export class AIChatContent extends SignalWatcher(
@state() @state()
accessor isHistoryLoading = false; accessor isHistoryLoading = false;
@state()
accessor showPreviewPanel = false;
@state()
accessor previewPanelContent: TemplateResult<1> | null = null;
private readonly chatMessagesRef: Ref<AIChatMessages> = private readonly chatMessagesRef: Ref<AIChatMessages> =
createRef<AIChatMessages>(); createRef<AIChatMessages>();
@@ -311,6 +338,8 @@ export class AIChatContent extends SignalWatcher(
public reset() { public reset() {
this.updateContext(DEFAULT_CHAT_CONTEXT_VALUE); this.updateContext(DEFAULT_CHAT_CONTEXT_VALUE);
this.showPreviewPanel = false;
this.previewPanelContent = null;
} }
override connectedCallback() { override connectedCallback() {
@@ -349,7 +378,7 @@ export class AIChatContent extends SignalWatcher(
} }
override render() { override render() {
return html`${this.chatTitle const left = html`${this.chatTitle
? html`<div class="ai-chat-title">${this.chatTitle}</div>` ? html`<div class="ai-chat-title">${this.chatTitle}</div>`
: nothing} : nothing}
<ai-chat-messages <ai-chat-messages
@@ -399,5 +428,14 @@ export class AIChatContent extends SignalWatcher(
control: 'chat-send', control: 'chat-send',
}} }}
></ai-chat-composer>`; ></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>`;
} }
} }

View File

@@ -5,8 +5,10 @@ import { EmptyIcon } from '@blocksuite/icons/lit';
import { css, html, nothing, type TemplateResult } from 'lit'; import { css, html, nothing, type TemplateResult } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import type { AIChatContent } from '../ai-chat-content';
function getChatPanel(target: HTMLElement) { function getChatPanel(target: HTMLElement) {
return target.closest('chat-panel'); return target.closest('ai-chat-content') as AIChatContent;
} }
export const isPreviewPanelOpen = (target: HTMLElement) => { export const isPreviewPanelOpen = (target: HTMLElement) => {

View File

@@ -3,9 +3,6 @@ import { style } from '@vanilla-extract/css';
export const chatRoot = style({ export const chatRoot = style({
width: '100%', width: '100%',
height: '100%', height: '100%',
maxWidth: 800,
padding: '0px 16px',
margin: '0 auto',
}); });
export const chatHeader = style({ export const chatHeader = style({

View File

@@ -188,16 +188,18 @@ export const Component = () => {
tool.session = currentSession; tool.session = currentSession;
// initial props
if (!chatTool) {
tool.onNewSession = () => { tool.onNewSession = () => {
if (!currentSession) return; if (!currentSession) return;
setCurrentSession(null); setCurrentSession(null);
chatContent?.reset(); chatContent?.reset();
}; };
tool.onTogglePin = async () => { tool.onTogglePin = async () => {
await togglePin(); await togglePin();
}; };
// initial props
if (!chatTool) {
// mount // mount
chatToolContainerRef.current.append(tool); chatToolContainerRef.current.append(tool);
setChatTool(tool); setChatTool(tool);