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 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>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -188,16 +188,18 @@ export const Component = () => {
|
|||||||
|
|
||||||
tool.session = currentSession;
|
tool.session = currentSession;
|
||||||
|
|
||||||
|
tool.onNewSession = () => {
|
||||||
|
if (!currentSession) return;
|
||||||
|
setCurrentSession(null);
|
||||||
|
chatContent?.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
tool.onTogglePin = async () => {
|
||||||
|
await togglePin();
|
||||||
|
};
|
||||||
|
|
||||||
// initial props
|
// initial props
|
||||||
if (!chatTool) {
|
if (!chatTool) {
|
||||||
tool.onNewSession = () => {
|
|
||||||
if (!currentSession) return;
|
|
||||||
setCurrentSession(null);
|
|
||||||
chatContent?.reset();
|
|
||||||
};
|
|
||||||
tool.onTogglePin = async () => {
|
|
||||||
await togglePin();
|
|
||||||
};
|
|
||||||
// mount
|
// mount
|
||||||
chatToolContainerRef.current.append(tool);
|
chatToolContainerRef.current.append(tool);
|
||||||
setChatTool(tool);
|
setChatTool(tool);
|
||||||
|
|||||||
Reference in New Issue
Block a user