mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-31 17:19:56 +08:00
feat: cleanup chat panel (#14259)
#### PR Dependency Tree * **PR #14258** * **PR #14259** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Split AI initialization into separate editor, app, and shared registries; removed legacy chat-panel and replaced it with a component-based editor chat, updating wiring and public exports. * Propagated server/subscription/model services into chat/playground components and improved session lifecycle and UI composition. * **Tests** * Added tests for AI effect registration and chat session resolution; extended DOM/test utilities and assertions. * **Chores** * Added happy-dom for runtime and test environments. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -26,7 +26,7 @@ import { property, state } from 'lit/decorators.js';
|
||||
|
||||
import { type ChatAction } from '../../components/ai-chat-messages';
|
||||
import { createTextRenderer } from '../../components/text-renderer';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../const';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../utils/history-image-actions';
|
||||
|
||||
const icons: Record<string, TemplateResult<1>> = {
|
||||
'Fix spelling for it': DoneIcon(),
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||
import type { ServerService } from '@affine/core/modules/cloud';
|
||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||
import { type NotificationService } from '@blocksuite/affine/shared/services';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
||||
import type { EditorHost } from '@blocksuite/affine/std';
|
||||
import { ShadowlessElement } from '@blocksuite/affine/std';
|
||||
import type { ExtensionType, Store } from '@blocksuite/affine/store';
|
||||
import { CenterPeekIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { SearchMenuConfig } from '../components/ai-chat-add-context';
|
||||
import type { DocDisplayConfig } from '../components/ai-chat-chips';
|
||||
import type {
|
||||
AIPlaygroundConfig,
|
||||
AIReasoningConfig,
|
||||
} from '../components/ai-chat-input';
|
||||
import type { ChatStatus } from '../components/ai-chat-messages';
|
||||
import { createPlaygroundModal } from '../components/playground/modal';
|
||||
import type { AppSidebarConfig } from './chat-config';
|
||||
|
||||
export class AIChatPanelTitle extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
static override styles = css`
|
||||
.ai-chat-panel-title {
|
||||
background: var(--affine-background-primary-color);
|
||||
position: relative;
|
||||
padding: 8px var(--h-padding, 16px);
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--affine-text-secondary-color);
|
||||
}
|
||||
|
||||
.chat-panel-title-text {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--affine-text-secondary-color);
|
||||
}
|
||||
|
||||
.chat-panel-playground {
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
margin-left: 8px;
|
||||
margin-right: auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-panel-playground:hover svg {
|
||||
color: ${unsafeCSSVarV2('icon/activated')};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor host!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor doc!: Store;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor playgroundConfig!: AIPlaygroundConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor appSidebarConfig!: AppSidebarConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor reasoningConfig!: AIReasoningConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor searchMenuConfig!: SearchMenuConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor docDisplayConfig!: DocDisplayConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor extensions!: ExtensionType[];
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor serverService!: ServerService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineFeatureFlagService!: FeatureFlagService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineWorkspaceDialogService!: WorkspaceDialogService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineThemeService!: AppThemeService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor notificationService!: NotificationService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor session!: CopilotChatHistoryFragment | null | undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor status!: ChatStatus;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor embeddingProgress: [number, number] = [0, 0];
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor newSession!: () => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor togglePin!: () => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor openSession!: (sessionId: string) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor openDoc!: (docId: string, sessionId: string) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor deleteSession!: (session: BlockSuitePresets.AIRecentSession) => void;
|
||||
|
||||
private readonly openPlayground = () => {
|
||||
const playgroundContent = html`
|
||||
<playground-content
|
||||
.host=${this.host}
|
||||
.doc=${this.doc}
|
||||
.reasoningConfig=${this.reasoningConfig}
|
||||
.playgroundConfig=${this.playgroundConfig}
|
||||
.appSidebarConfig=${this.appSidebarConfig}
|
||||
.searchMenuConfig=${this.searchMenuConfig}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.extensions=${this.extensions}
|
||||
.serverService=${this.serverService}
|
||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||
.affineThemeService=${this.affineThemeService}
|
||||
.notificationService=${this.notificationService}
|
||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||
></playground-content>
|
||||
`;
|
||||
|
||||
createPlaygroundModal(playgroundContent, 'AI Playground');
|
||||
};
|
||||
|
||||
override render() {
|
||||
const [done, total] = this.embeddingProgress;
|
||||
const isEmbedding = total > 0 && done < total;
|
||||
|
||||
return html`
|
||||
<div class="ai-chat-panel-title">
|
||||
<div class="chat-panel-title-text">
|
||||
${isEmbedding
|
||||
? html`<span data-testid="chat-panel-embedding-progress"
|
||||
>Embedding ${done}/${total}</span
|
||||
>`
|
||||
: 'AFFiNE AI'}
|
||||
</div>
|
||||
${this.playgroundConfig.visible.value
|
||||
? html`
|
||||
<div class="chat-panel-playground" @click=${this.openPlayground}>
|
||||
${CenterPeekIcon()}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
<ai-chat-toolbar
|
||||
.session=${this.session}
|
||||
.workspaceId=${this.doc.workspace.id}
|
||||
.docId=${this.doc.id}
|
||||
.status=${this.status}
|
||||
.onNewSession=${this.newSession}
|
||||
.onTogglePin=${this.togglePin}
|
||||
.onOpenSession=${this.openSession}
|
||||
.onOpenDoc=${this.openDoc}
|
||||
.onSessionDelete=${this.deleteSession}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.notificationService=${this.notificationService}
|
||||
></ai-chat-toolbar>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -1,485 +0,0 @@
|
||||
import type {
|
||||
AIDraftService,
|
||||
AIToolsConfigService,
|
||||
} from '@affine/core/modules/ai-button';
|
||||
import type { AIModelService } from '@affine/core/modules/ai-button/services/models';
|
||||
import type {
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
} from '@affine/core/modules/cloud';
|
||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||
import type { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import type {
|
||||
ContextEmbedStatus,
|
||||
CopilotChatHistoryFragment,
|
||||
UpdateChatSessionInput,
|
||||
} from '@affine/graphql';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/affine/global/lit';
|
||||
import { type NotificationService } from '@blocksuite/affine/shared/services';
|
||||
import type { EditorHost } from '@blocksuite/affine/std';
|
||||
import { ShadowlessElement } from '@blocksuite/affine/std';
|
||||
import type { ExtensionType, Store } from '@blocksuite/affine/store';
|
||||
import { type Signal, signal } from '@preact/signals-core';
|
||||
import { css, html, type PropertyValues } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
|
||||
import { AffineIcon } from '../_common/icons';
|
||||
import type { SearchMenuConfig } from '../components/ai-chat-add-context';
|
||||
import type { DocDisplayConfig } from '../components/ai-chat-chips';
|
||||
import type { ChatContextValue } from '../components/ai-chat-content';
|
||||
import type {
|
||||
AIPlaygroundConfig,
|
||||
AIReasoningConfig,
|
||||
} from '../components/ai-chat-input';
|
||||
import type { ChatStatus } from '../components/ai-chat-messages';
|
||||
import { AIProvider } from '../provider';
|
||||
import type { AppSidebarConfig } from './chat-config';
|
||||
|
||||
export class ChatPanel extends SignalWatcher(
|
||||
WithDisposable(ShadowlessElement)
|
||||
) {
|
||||
static override styles = css`
|
||||
chat-panel {
|
||||
width: 100%;
|
||||
user-select: text;
|
||||
|
||||
.chat-panel-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ai-chat-content {
|
||||
height: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.chat-loading-container {
|
||||
position: relative;
|
||||
padding: 44px 0 166px 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chat-loading {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.chat-loading-title {
|
||||
font-weight: 600;
|
||||
font-size: var(--affine-font-sm);
|
||||
color: var(--affine-text-secondary-color);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor host!: EditorHost;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor doc!: Store;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor playgroundConfig!: AIPlaygroundConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor appSidebarConfig!: AppSidebarConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor reasoningConfig!: AIReasoningConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor searchMenuConfig!: SearchMenuConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor docDisplayConfig!: DocDisplayConfig;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor extensions!: ExtensionType[];
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor serverService!: ServerService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineFeatureFlagService!: FeatureFlagService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineWorkspaceDialogService!: WorkspaceDialogService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineWorkbenchService!: WorkbenchService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineThemeService!: AppThemeService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor notificationService!: NotificationService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiDraftService!: AIDraftService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor peekViewService!: PeekViewService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor subscriptionService!: SubscriptionService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiModelService!: AIModelService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onAISubscribe!: () => Promise<void>;
|
||||
|
||||
@state()
|
||||
accessor session: CopilotChatHistoryFragment | null | undefined;
|
||||
|
||||
@state()
|
||||
accessor embeddingProgress: [number, number] = [0, 0];
|
||||
|
||||
@state()
|
||||
accessor status: ChatStatus = 'idle';
|
||||
|
||||
private isSidebarOpen: Signal<boolean | undefined> = signal(false);
|
||||
|
||||
private sidebarWidth: Signal<number | undefined> = signal(undefined);
|
||||
|
||||
private hasPinned = false;
|
||||
|
||||
private get isInitialized() {
|
||||
return this.session !== undefined;
|
||||
}
|
||||
|
||||
private readonly getSessionIdFromUrl = () => {
|
||||
if (this.affineWorkbenchService) {
|
||||
const { workbench } = this.affineWorkbenchService;
|
||||
const location = workbench.location$.value;
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const sessionId = searchParams.get('sessionId');
|
||||
if (sessionId) {
|
||||
workbench.activeView$.value.updateQueryString(
|
||||
{ sessionId: undefined },
|
||||
{ replace: true }
|
||||
);
|
||||
}
|
||||
return sessionId;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
private readonly setSession = (
|
||||
session: CopilotChatHistoryFragment | null | undefined
|
||||
) => {
|
||||
this.session = session ?? null;
|
||||
};
|
||||
|
||||
private readonly initSession = async () => {
|
||||
if (!AIProvider.session) {
|
||||
return;
|
||||
}
|
||||
const sessionId = this.getSessionIdFromUrl();
|
||||
const pinSessions = await AIProvider.session.getSessions(
|
||||
this.doc.workspace.id,
|
||||
undefined,
|
||||
{ pinned: true, limit: 1 }
|
||||
);
|
||||
|
||||
if (Array.isArray(pinSessions) && pinSessions[0]) {
|
||||
// pinned session
|
||||
this.session = pinSessions[0];
|
||||
} else if (sessionId) {
|
||||
// sessionId from url
|
||||
const session = await AIProvider.session.getSession(
|
||||
this.doc.workspace.id,
|
||||
sessionId
|
||||
);
|
||||
this.setSession(session);
|
||||
} else {
|
||||
// latest doc session
|
||||
const docSessions = await AIProvider.session.getSessions(
|
||||
this.doc.workspace.id,
|
||||
this.doc.id,
|
||||
{ action: false, fork: false, limit: 1 }
|
||||
);
|
||||
// sessions is descending ordered by updatedAt
|
||||
// the first item is the latest session
|
||||
const session = docSessions?.[0];
|
||||
this.setSession(session);
|
||||
}
|
||||
};
|
||||
|
||||
private readonly createSession = async (
|
||||
options: Partial<BlockSuitePresets.AICreateSessionOptions> = {}
|
||||
) => {
|
||||
if (this.session) {
|
||||
return this.session;
|
||||
}
|
||||
const sessionId = await AIProvider.session?.createSession({
|
||||
docId: this.doc.id,
|
||||
workspaceId: this.doc.workspace.id,
|
||||
promptName: 'Chat With AFFiNE AI',
|
||||
reuseLatestChat: false,
|
||||
...options,
|
||||
});
|
||||
if (sessionId) {
|
||||
const session = await AIProvider.session?.getSession(
|
||||
this.doc.workspace.id,
|
||||
sessionId
|
||||
);
|
||||
this.setSession(session);
|
||||
}
|
||||
return this.session;
|
||||
};
|
||||
|
||||
private readonly deleteSession = async (
|
||||
session: BlockSuitePresets.AIRecentSession
|
||||
) => {
|
||||
if (!AIProvider.histories) {
|
||||
return;
|
||||
}
|
||||
const confirm = await this.notificationService.confirm({
|
||||
title: 'Delete this history?',
|
||||
message:
|
||||
'Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.',
|
||||
confirmText: 'Delete',
|
||||
cancelText: 'Cancel',
|
||||
});
|
||||
if (confirm) {
|
||||
await AIProvider.histories.cleanup(
|
||||
session.workspaceId,
|
||||
session.docId || undefined,
|
||||
[session.sessionId]
|
||||
);
|
||||
if (session.sessionId === this.session?.sessionId) {
|
||||
this.newSession();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private readonly updateSession = async (options: UpdateChatSessionInput) => {
|
||||
await AIProvider.session?.updateSession(options);
|
||||
const session = await AIProvider.session?.getSession(
|
||||
this.doc.workspace.id,
|
||||
options.sessionId
|
||||
);
|
||||
this.setSession(session);
|
||||
};
|
||||
|
||||
private readonly newSession = () => {
|
||||
this.resetPanel();
|
||||
requestAnimationFrame(() => {
|
||||
this.session = null;
|
||||
});
|
||||
};
|
||||
|
||||
private readonly openSession = async (sessionId: string) => {
|
||||
if (this.session?.sessionId === sessionId) {
|
||||
return;
|
||||
}
|
||||
this.resetPanel();
|
||||
const session = await AIProvider.session?.getSession(
|
||||
this.doc.workspace.id,
|
||||
sessionId
|
||||
);
|
||||
this.setSession(session);
|
||||
};
|
||||
|
||||
private readonly openDoc = async (docId: string, sessionId: string) => {
|
||||
if (this.doc.id === docId) {
|
||||
if (this.session?.sessionId === sessionId || this.session?.pinned) {
|
||||
return;
|
||||
}
|
||||
await this.openSession(sessionId);
|
||||
} else if (this.affineWorkbenchService) {
|
||||
const { workbench } = this.affineWorkbenchService;
|
||||
if (this.session?.pinned) {
|
||||
workbench.open(`/${docId}`, { at: 'active' });
|
||||
} else {
|
||||
workbench.open(`/${docId}?sessionId=${sessionId}`, { at: 'active' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private readonly togglePin = async () => {
|
||||
const pinned = !this.session?.pinned;
|
||||
this.hasPinned = true;
|
||||
if (!this.session) {
|
||||
await this.createSession({ pinned });
|
||||
} else {
|
||||
await this.updateSession({
|
||||
sessionId: this.session.sessionId,
|
||||
pinned,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private readonly rebindSession = async () => {
|
||||
if (!this.session) {
|
||||
return;
|
||||
}
|
||||
if (this.session.docId !== this.doc.id) {
|
||||
await this.updateSession({
|
||||
sessionId: this.session.sessionId,
|
||||
docId: this.doc.id,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private readonly initPanel = async () => {
|
||||
try {
|
||||
if (!this.isSidebarOpen.value) {
|
||||
return;
|
||||
}
|
||||
await this.initSession();
|
||||
this.hasPinned = !!this.session?.pinned;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
private readonly resetPanel = () => {
|
||||
this.session = undefined;
|
||||
this.embeddingProgress = [0, 0];
|
||||
this.hasPinned = false;
|
||||
};
|
||||
|
||||
private readonly onEmbeddingProgressChange = (
|
||||
count: Record<ContextEmbedStatus, number>
|
||||
) => {
|
||||
const total = count.finished + count.processing + count.failed;
|
||||
this.embeddingProgress = [count.finished, total];
|
||||
};
|
||||
|
||||
private readonly onContextChange = async (
|
||||
context: Partial<ChatContextValue>
|
||||
) => {
|
||||
this.status = context.status ?? 'idle';
|
||||
if (context.status === 'success') {
|
||||
await this.rebindSession();
|
||||
}
|
||||
};
|
||||
|
||||
protected override updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has('doc')) {
|
||||
if (this.session?.pinned) {
|
||||
return;
|
||||
}
|
||||
this.resetPanel();
|
||||
this.initPanel().catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (!this.doc) throw new Error('doc is required');
|
||||
|
||||
this._disposables.add(
|
||||
AIProvider.slots.userInfo.subscribe(() => {
|
||||
this.resetPanel();
|
||||
this.initPanel().catch(console.error);
|
||||
})
|
||||
);
|
||||
|
||||
const isOpen = this.appSidebarConfig.isOpen();
|
||||
this.isSidebarOpen = isOpen.signal;
|
||||
this._disposables.add(isOpen.cleanup);
|
||||
|
||||
const width = this.appSidebarConfig.getWidth();
|
||||
this.sidebarWidth = width.signal;
|
||||
this._disposables.add(width.cleanup);
|
||||
|
||||
this._disposables.add(
|
||||
this.isSidebarOpen.subscribe(isOpen => {
|
||||
if (isOpen && !this.isInitialized) {
|
||||
this.initPanel().catch(console.error);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.isInitialized) {
|
||||
return html`<div class="chat-loading-container">
|
||||
<div class="chat-loading">
|
||||
${AffineIcon('var(--affine-icon-secondary)')}
|
||||
<div class="chat-loading-title">
|
||||
<span> AFFiNE AI is loading history... </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html`<div class="chat-panel-container">
|
||||
<ai-chat-panel-title
|
||||
.host=${this.host}
|
||||
.doc=${this.doc}
|
||||
.playgroundConfig=${this.playgroundConfig}
|
||||
.appSidebarConfig=${this.appSidebarConfig}
|
||||
.reasoningConfig=${this.reasoningConfig}
|
||||
.searchMenuConfig=${this.searchMenuConfig}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.extensions=${this.extensions}
|
||||
.serverService=${this.serverService}
|
||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||
.affineThemeService=${this.affineThemeService}
|
||||
.notificationService=${this.notificationService}
|
||||
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||
.session=${this.session}
|
||||
.status=${this.status}
|
||||
.embeddingProgress=${this.embeddingProgress}
|
||||
.newSession=${this.newSession}
|
||||
.togglePin=${this.togglePin}
|
||||
.openSession=${this.openSession}
|
||||
.openDoc=${this.openDoc}
|
||||
.deleteSession=${this.deleteSession}
|
||||
></ai-chat-panel-title>
|
||||
${keyed(
|
||||
this.hasPinned ? this.session?.sessionId : this.doc.id,
|
||||
html`<ai-chat-content
|
||||
.host=${this.host}
|
||||
.session=${this.session}
|
||||
.createSession=${this.createSession}
|
||||
.workspaceId=${this.doc.workspace.id}
|
||||
.docId=${this.doc.id}
|
||||
.reasoningConfig=${this.reasoningConfig}
|
||||
.searchMenuConfig=${this.searchMenuConfig}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.extensions=${this.extensions}
|
||||
.serverService=${this.serverService}
|
||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||
.affineThemeService=${this.affineThemeService}
|
||||
.notificationService=${this.notificationService}
|
||||
.aiDraftService=${this.aiDraftService}
|
||||
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||
.peekViewService=${this.peekViewService}
|
||||
.subscriptionService=${this.subscriptionService}
|
||||
.aiModelService=${this.aiModelService}
|
||||
.onAISubscribe=${this.onAISubscribe}
|
||||
.onEmbeddingProgressChange=${this.onEmbeddingProgressChange}
|
||||
.onContextChange=${this.onContextChange}
|
||||
.width=${this.sidebarWidth}
|
||||
.onOpenDoc=${this.openDoc}
|
||||
></ai-chat-content>`
|
||||
)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'chat-panel': ChatPanel;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
|
||||
import { type ChatAction } from '../../components/ai-chat-messages';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../const';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../utils/history-image-actions';
|
||||
|
||||
export class ChatMessageAction extends WithDisposable(ShadowlessElement) {
|
||||
@property({ attribute: false })
|
||||
|
||||
+1
-1
@@ -28,9 +28,9 @@ import { createRef, type Ref, ref } from 'lit/directives/ref.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../chat-panel/const';
|
||||
import { type AIChatParams, AIProvider } from '../../provider/ai-provider';
|
||||
import { extractSelectedContent } from '../../utils/extract';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../utils/history-image-actions';
|
||||
import type { SearchMenuConfig } from '../ai-chat-add-context';
|
||||
import type { DocDisplayConfig } from '../ai-chat-chips';
|
||||
import type { AIReasoningConfig } from '../ai-chat-input';
|
||||
|
||||
+1
-1
@@ -19,12 +19,12 @@ import { repeat } from 'lit/directives/repeat.js';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
import { AffineIcon } from '../../_common/icons';
|
||||
import { AIPreloadConfig } from '../../chat-panel/preload-config';
|
||||
import { type AIError, AIProvider, UnauthorizedError } from '../../provider';
|
||||
import { mergeStreamObjects } from '../../utils/stream-objects';
|
||||
import type { DocDisplayConfig } from '../ai-chat-chips';
|
||||
import { type ChatContextValue } from '../ai-chat-content/type';
|
||||
import type { AIReasoningConfig } from '../ai-chat-input';
|
||||
import { AIPreloadConfig } from './preload-config';
|
||||
import {
|
||||
type HistoryMessage,
|
||||
isChatAction,
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import {
|
||||
SendIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
|
||||
import { AIProvider } from '../provider/ai-provider.js';
|
||||
import { AIProvider } from '../../provider/ai-provider.js';
|
||||
import completeWritingWithAI from './templates/completeWritingWithAI.zip';
|
||||
import freelyCommunicateWithAI from './templates/freelyCommunicateWithAI.zip';
|
||||
import readAforeign from './templates/readAforeign.zip';
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||
import type { ServerService } from '@affine/core/modules/cloud';
|
||||
import type { AIModelService } from '@affine/core/modules/ai-button/services/models';
|
||||
import type {
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
} from '@affine/core/modules/cloud';
|
||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||
@@ -20,8 +24,8 @@ import { createRef, type Ref, ref } from 'lit/directives/ref.js';
|
||||
import { throttle } from 'lodash-es';
|
||||
|
||||
import type { AppSidebarConfig } from '../../chat-panel/chat-config';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../chat-panel/const';
|
||||
import { AIProvider } from '../../provider';
|
||||
import { HISTORY_IMAGE_ACTIONS } from '../../utils/history-image-actions';
|
||||
import type { SearchMenuConfig } from '../ai-chat-add-context';
|
||||
import type { DocDisplayConfig } from '../ai-chat-chips';
|
||||
import type { ChatContextValue } from '../ai-chat-content';
|
||||
@@ -179,6 +183,12 @@ export class PlaygroundChat extends SignalWatcher(
|
||||
@property({ attribute: false })
|
||||
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor subscriptionService!: SubscriptionService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiModelService!: AIModelService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onAISubscribe: (() => Promise<void>) | undefined;
|
||||
|
||||
@@ -373,6 +383,8 @@ export class PlaygroundChat extends SignalWatcher(
|
||||
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||
.affineWorkspaceDialogService=${this.affineWorkspaceDialogService}
|
||||
.affineFeatureFlagService=${this.affineFeatureFlagService}
|
||||
.subscriptionService=${this.subscriptionService}
|
||||
.aiModelService=${this.aiModelService}
|
||||
.onAISubscribe=${this.onAISubscribe}
|
||||
></ai-chat-composer>
|
||||
</div>`;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import type { AIToolsConfigService } from '@affine/core/modules/ai-button';
|
||||
import type { ServerService } from '@affine/core/modules/cloud';
|
||||
import type { AIModelService } from '@affine/core/modules/ai-button/services/models';
|
||||
import type {
|
||||
ServerService,
|
||||
SubscriptionService,
|
||||
} from '@affine/core/modules/cloud';
|
||||
import type { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import type { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||
import type { AppThemeService } from '@affine/core/modules/theme';
|
||||
import type { CopilotChatHistoryFragment } from '@affine/graphql';
|
||||
@@ -93,6 +98,15 @@ export class PlaygroundContent extends SignalWatcher(
|
||||
@property({ attribute: false })
|
||||
accessor aiToolsConfigService!: AIToolsConfigService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor affineWorkspaceDialogService!: WorkspaceDialogService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor subscriptionService!: SubscriptionService;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor aiModelService!: AIModelService;
|
||||
|
||||
@state()
|
||||
accessor sessions: CopilotChatHistoryFragment[] = [];
|
||||
|
||||
@@ -351,6 +365,10 @@ export class PlaygroundContent extends SignalWatcher(
|
||||
.affineThemeService=${this.affineThemeService}
|
||||
.notificationService=${this.notificationService}
|
||||
.aiToolsConfigService=${this.aiToolsConfigService}
|
||||
.affineWorkspaceDialogService=${this
|
||||
.affineWorkspaceDialogService}
|
||||
.subscriptionService=${this.subscriptionService}
|
||||
.aiModelService=${this.aiModelService}
|
||||
.addChat=${this.addChat}
|
||||
></playground-chat>
|
||||
</div>
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
import { effects as tooltipEffects } from '@blocksuite/affine-components/tooltip';
|
||||
|
||||
import { AIChatBlockComponent } from './blocks/ai-chat-block/ai-chat-block';
|
||||
import { EdgelessAIChatBlockComponent } from './blocks/ai-chat-block/ai-chat-edgeless-block';
|
||||
import { LitTranscriptionBlock } from './blocks/ai-chat-block/ai-transcription-block';
|
||||
import {
|
||||
AIChatBlockMessage,
|
||||
AIChatBlockMessages,
|
||||
} from './blocks/ai-chat-block/components/ai-chat-messages';
|
||||
import {
|
||||
ChatImage,
|
||||
ChatImages,
|
||||
} from './blocks/ai-chat-block/components/chat-images';
|
||||
import { ImagePlaceholder } from './blocks/ai-chat-block/components/image-placeholder';
|
||||
import { UserInfo } from './blocks/ai-chat-block/components/user-info';
|
||||
import { ChatPanel } from './chat-panel';
|
||||
import { ActionWrapper } from './chat-panel/actions/action-wrapper';
|
||||
import { ActionImage } from './chat-panel/actions/image';
|
||||
import { ActionImageToText } from './chat-panel/actions/image-to-text';
|
||||
import { ActionMakeReal } from './chat-panel/actions/make-real';
|
||||
import { ActionMindmap } from './chat-panel/actions/mindmap';
|
||||
import { ActionSlides } from './chat-panel/actions/slides';
|
||||
import { ActionText } from './chat-panel/actions/text';
|
||||
import { AILoading } from './chat-panel/ai-loading';
|
||||
import { AIChatPanelTitle } from './chat-panel/ai-title';
|
||||
import { ChatMessageAction } from './chat-panel/message/action';
|
||||
import { ChatMessageAssistant } from './chat-panel/message/assistant';
|
||||
import { ChatMessageUser } from './chat-panel/message/user';
|
||||
import { ChatPanelSplitView } from './chat-panel/split-view';
|
||||
import { ArtifactSkeleton } from './components/ai-artifact-skeleton';
|
||||
import { AIChatAddContext } from './components/ai-chat-add-context';
|
||||
import { ChatPanelAddPopover } from './components/ai-chat-chips/add-popover';
|
||||
import { ChatPanelAttachmentChip } from './components/ai-chat-chips/attachment-chip';
|
||||
import { ChatPanelCandidatesPopover } from './components/ai-chat-chips/candidates-popover';
|
||||
import { ChatPanelChips } from './components/ai-chat-chips/chat-panel-chips';
|
||||
import { ChatPanelChip } from './components/ai-chat-chips/chip';
|
||||
import { ChatPanelCollectionChip } from './components/ai-chat-chips/collection-chip';
|
||||
import { ChatPanelDocChip } from './components/ai-chat-chips/doc-chip';
|
||||
import { ChatPanelFileChip } from './components/ai-chat-chips/file-chip';
|
||||
import { ChatPanelSelectedChip } from './components/ai-chat-chips/selected-chip';
|
||||
import { ChatPanelTagChip } from './components/ai-chat-chips/tag-chip';
|
||||
import { AIChatComposer } from './components/ai-chat-composer';
|
||||
import { AIChatContent } from './components/ai-chat-content';
|
||||
import { AIChatInput } from './components/ai-chat-input';
|
||||
import { AIChatEmbeddingStatusTooltip } from './components/ai-chat-input/embedding-status-tooltip';
|
||||
import { ChatInputPreference } from './components/ai-chat-input/preference-popup';
|
||||
import { AIChatMessages } from './components/ai-chat-messages/ai-chat-messages';
|
||||
import { AIChatToolbar, AISessionHistory } from './components/ai-chat-toolbar';
|
||||
import { AIHistoryClear } from './components/ai-history-clear';
|
||||
import { effects as componentAiItemEffects } from './components/ai-item';
|
||||
import { AssistantAvatar } from './components/ai-message-content/assistant-avatar';
|
||||
import { ChatContentImages } from './components/ai-message-content/images';
|
||||
import { ChatContentPureText } from './components/ai-message-content/pure-text';
|
||||
import { ChatContentRichText } from './components/ai-message-content/rich-text';
|
||||
import { ChatContentStreamObjects } from './components/ai-message-content/stream-objects';
|
||||
import { AIScrollableTextRenderer } from './components/ai-scrollable-text-renderer';
|
||||
import { ArtifactPreviewPanel } from './components/ai-tools/artifacts-preview-panel';
|
||||
import {
|
||||
CodeArtifactTool,
|
||||
CodeHighlighter,
|
||||
} from './components/ai-tools/code-artifact';
|
||||
import { DocComposeTool } from './components/ai-tools/doc-compose';
|
||||
import { DocEditTool } from './components/ai-tools/doc-edit';
|
||||
import { DocKeywordSearchResult } from './components/ai-tools/doc-keyword-search-result';
|
||||
import { DocReadResult } from './components/ai-tools/doc-read-result';
|
||||
import { DocSemanticSearchResult } from './components/ai-tools/doc-semantic-search-result';
|
||||
import { DocWriteTool } from './components/ai-tools/doc-write';
|
||||
import { SectionEditTool } from './components/ai-tools/section-edit';
|
||||
import { ToolCallCard } from './components/ai-tools/tool-call-card';
|
||||
import { ToolFailedCard } from './components/ai-tools/tool-failed-card';
|
||||
import { ToolResultCard } from './components/ai-tools/tool-result-card';
|
||||
import { WebCrawlTool } from './components/ai-tools/web-crawl';
|
||||
import { WebSearchTool } from './components/ai-tools/web-search';
|
||||
import { AskAIButton } from './components/ask-ai-button';
|
||||
import { AskAIIcon } from './components/ask-ai-icon';
|
||||
import { AskAIPanel } from './components/ask-ai-panel';
|
||||
import { AskAIToolbarButton } from './components/ask-ai-toolbar';
|
||||
import { ChatActionList } from './components/chat-action-list';
|
||||
import { ChatCopyMore } from './components/copy-more';
|
||||
import { ImagePreviewGrid } from './components/image-preview-grid';
|
||||
import { effects as componentPlaygroundEffects } from './components/playground';
|
||||
import { TextRenderer } from './components/text-renderer';
|
||||
import { AIErrorWrapper } from './messages/error';
|
||||
import { AISlidesRenderer } from './messages/slides-renderer';
|
||||
import { AIAnswerWrapper } from './messages/wrapper';
|
||||
import { registerMiniMindmapBlocks } from './mini-mindmap';
|
||||
import { AIChatBlockPeekView } from './peek-view/chat-block-peek-view';
|
||||
import { DateTime } from './peek-view/date-time';
|
||||
import {
|
||||
AFFINE_AI_PANEL_WIDGET,
|
||||
AffineAIPanelWidget,
|
||||
} from './widgets/ai-panel/ai-panel';
|
||||
import {
|
||||
AIPanelAnswer,
|
||||
AIPanelDivider,
|
||||
AIPanelError,
|
||||
AIPanelGenerating,
|
||||
AIPanelInput,
|
||||
} from './widgets/ai-panel/components';
|
||||
import { AIFinishTip } from './widgets/ai-panel/components/finish-tip';
|
||||
import { GeneratingPlaceholder } from './widgets/ai-panel/components/generating-placeholder';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_BLOCK,
|
||||
AffineBlockDiffWidgetForBlock,
|
||||
} from './widgets/block-diff/block';
|
||||
import { BlockDiffOptions } from './widgets/block-diff/options';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_PAGE,
|
||||
AffineBlockDiffWidgetForPage,
|
||||
} from './widgets/block-diff/page';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_PLAYGROUND,
|
||||
AFFINE_BLOCK_DIFF_PLAYGROUND_MODAL,
|
||||
BlockDiffPlayground,
|
||||
BlockDiffPlaygroundModal,
|
||||
} from './widgets/block-diff/playground';
|
||||
import {
|
||||
AFFINE_EDGELESS_COPILOT_WIDGET,
|
||||
EdgelessCopilotWidget,
|
||||
} from './widgets/edgeless-copilot';
|
||||
import { EdgelessCopilotPanel } from './widgets/edgeless-copilot-panel';
|
||||
import { EdgelessCopilotToolbarEntry } from './widgets/edgeless-copilot-panel/toolbar-entry';
|
||||
|
||||
export function registerAIEffects() {
|
||||
registerMiniMindmapBlocks();
|
||||
componentAiItemEffects();
|
||||
componentPlaygroundEffects();
|
||||
tooltipEffects();
|
||||
|
||||
customElements.define('ask-ai-icon', AskAIIcon);
|
||||
customElements.define('ask-ai-button', AskAIButton);
|
||||
customElements.define('ask-ai-toolbar-button', AskAIToolbarButton);
|
||||
customElements.define('ask-ai-panel', AskAIPanel);
|
||||
customElements.define('chat-action-list', ChatActionList);
|
||||
customElements.define('chat-copy-more', ChatCopyMore);
|
||||
customElements.define('image-preview-grid', ImagePreviewGrid);
|
||||
customElements.define('action-wrapper', ActionWrapper);
|
||||
customElements.define('action-image-to-text', ActionImageToText);
|
||||
customElements.define('action-image', ActionImage);
|
||||
customElements.define('action-make-real', ActionMakeReal);
|
||||
customElements.define('action-mindmap', ActionMindmap);
|
||||
customElements.define('action-slides', ActionSlides);
|
||||
customElements.define('action-text', ActionText);
|
||||
customElements.define('ai-loading', AILoading);
|
||||
customElements.define('ai-chat-content', AIChatContent);
|
||||
customElements.define('ai-chat-toolbar', AIChatToolbar);
|
||||
customElements.define('ai-session-history', AISessionHistory);
|
||||
customElements.define('ai-chat-messages', AIChatMessages);
|
||||
customElements.define('chat-panel', ChatPanel);
|
||||
customElements.define('ai-chat-panel-title', AIChatPanelTitle);
|
||||
customElements.define('ai-chat-input', AIChatInput);
|
||||
customElements.define('ai-chat-add-context', AIChatAddContext);
|
||||
customElements.define(
|
||||
'ai-chat-embedding-status-tooltip',
|
||||
AIChatEmbeddingStatusTooltip
|
||||
);
|
||||
customElements.define('ai-chat-composer', AIChatComposer);
|
||||
customElements.define('chat-panel-chips', ChatPanelChips);
|
||||
customElements.define('ai-history-clear', AIHistoryClear);
|
||||
customElements.define('chat-panel-add-popover', ChatPanelAddPopover);
|
||||
customElements.define('chat-input-preference', ChatInputPreference);
|
||||
customElements.define(
|
||||
'chat-panel-candidates-popover',
|
||||
ChatPanelCandidatesPopover
|
||||
);
|
||||
customElements.define('chat-panel-doc-chip', ChatPanelDocChip);
|
||||
customElements.define('chat-panel-file-chip', ChatPanelFileChip);
|
||||
customElements.define('chat-panel-tag-chip', ChatPanelTagChip);
|
||||
customElements.define('chat-panel-collection-chip', ChatPanelCollectionChip);
|
||||
customElements.define('chat-panel-selected-chip', ChatPanelSelectedChip);
|
||||
customElements.define('chat-panel-attachment-chip', ChatPanelAttachmentChip);
|
||||
customElements.define('chat-panel-chip', ChatPanelChip);
|
||||
customElements.define('ai-error-wrapper', AIErrorWrapper);
|
||||
customElements.define('ai-slides-renderer', AISlidesRenderer);
|
||||
customElements.define('ai-answer-wrapper', AIAnswerWrapper);
|
||||
customElements.define('ai-chat-block-peek-view', AIChatBlockPeekView);
|
||||
customElements.define('date-time', DateTime);
|
||||
customElements.define(
|
||||
'affine-edgeless-ai-chat',
|
||||
EdgelessAIChatBlockComponent
|
||||
);
|
||||
customElements.define('affine-ai-chat', AIChatBlockComponent);
|
||||
customElements.define('ai-chat-block-message', AIChatBlockMessage);
|
||||
customElements.define('ai-chat-block-messages', AIChatBlockMessages);
|
||||
customElements.define(
|
||||
'ai-scrollable-text-renderer',
|
||||
AIScrollableTextRenderer
|
||||
);
|
||||
customElements.define('image-placeholder', ImagePlaceholder);
|
||||
customElements.define('chat-image', ChatImage);
|
||||
customElements.define('chat-images', ChatImages);
|
||||
customElements.define('user-info', UserInfo);
|
||||
customElements.define('text-renderer', TextRenderer);
|
||||
|
||||
customElements.define('generating-placeholder', GeneratingPlaceholder);
|
||||
customElements.define('ai-finish-tip', AIFinishTip);
|
||||
customElements.define('ai-panel-divider', AIPanelDivider);
|
||||
customElements.define('ai-panel-answer', AIPanelAnswer);
|
||||
customElements.define('ai-panel-input', AIPanelInput);
|
||||
customElements.define('ai-panel-generating', AIPanelGenerating);
|
||||
customElements.define('ai-panel-error', AIPanelError);
|
||||
customElements.define('chat-assistant-avatar', AssistantAvatar);
|
||||
customElements.define('chat-content-images', ChatContentImages);
|
||||
customElements.define('chat-content-pure-text', ChatContentPureText);
|
||||
customElements.define('chat-content-rich-text', ChatContentRichText);
|
||||
customElements.define(
|
||||
'chat-content-stream-objects',
|
||||
ChatContentStreamObjects
|
||||
);
|
||||
customElements.define('chat-message-action', ChatMessageAction);
|
||||
customElements.define('chat-message-assistant', ChatMessageAssistant);
|
||||
customElements.define('chat-message-user', ChatMessageUser);
|
||||
customElements.define('ai-block-diff-options', BlockDiffOptions);
|
||||
customElements.define(AFFINE_BLOCK_DIFF_PLAYGROUND, BlockDiffPlayground);
|
||||
customElements.define(
|
||||
AFFINE_BLOCK_DIFF_PLAYGROUND_MODAL,
|
||||
BlockDiffPlaygroundModal
|
||||
);
|
||||
|
||||
customElements.define('tool-call-card', ToolCallCard);
|
||||
customElements.define('tool-result-card', ToolResultCard);
|
||||
customElements.define('tool-call-failed', ToolFailedCard);
|
||||
customElements.define('doc-semantic-search-result', DocSemanticSearchResult);
|
||||
customElements.define('doc-keyword-search-result', DocKeywordSearchResult);
|
||||
customElements.define('doc-read-result', DocReadResult);
|
||||
customElements.define('doc-write-tool', DocWriteTool);
|
||||
customElements.define('web-crawl-tool', WebCrawlTool);
|
||||
customElements.define('web-search-tool', WebSearchTool);
|
||||
customElements.define('section-edit-tool', SectionEditTool);
|
||||
customElements.define('doc-compose-tool', DocComposeTool);
|
||||
customElements.define('code-artifact-tool', CodeArtifactTool);
|
||||
customElements.define('code-highlighter', CodeHighlighter);
|
||||
customElements.define('artifact-preview-panel', ArtifactPreviewPanel);
|
||||
customElements.define('doc-edit-tool', DocEditTool);
|
||||
|
||||
customElements.define(AFFINE_AI_PANEL_WIDGET, AffineAIPanelWidget);
|
||||
customElements.define(AFFINE_EDGELESS_COPILOT_WIDGET, EdgelessCopilotWidget);
|
||||
customElements.define(
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_BLOCK,
|
||||
AffineBlockDiffWidgetForBlock
|
||||
);
|
||||
customElements.define(
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_PAGE,
|
||||
AffineBlockDiffWidgetForPage
|
||||
);
|
||||
|
||||
customElements.define('edgeless-copilot-panel', EdgelessCopilotPanel);
|
||||
customElements.define(
|
||||
'edgeless-copilot-toolbar-entry',
|
||||
EdgelessCopilotToolbarEntry
|
||||
);
|
||||
|
||||
customElements.define('transcription-block', LitTranscriptionBlock);
|
||||
customElements.define('chat-panel-split-view', ChatPanelSplitView);
|
||||
customElements.define('artifact-skeleton', ArtifactSkeleton);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { ActionWrapper } from '../chat-panel/actions/action-wrapper';
|
||||
import { ActionImage } from '../chat-panel/actions/image';
|
||||
import { ActionImageToText } from '../chat-panel/actions/image-to-text';
|
||||
import { ActionMakeReal } from '../chat-panel/actions/make-real';
|
||||
import { ActionMindmap } from '../chat-panel/actions/mindmap';
|
||||
import { ActionSlides } from '../chat-panel/actions/slides';
|
||||
import { ActionText } from '../chat-panel/actions/text';
|
||||
import { AILoading } from '../chat-panel/ai-loading';
|
||||
import { ChatMessageAction } from '../chat-panel/message/action';
|
||||
import { ChatMessageAssistant } from '../chat-panel/message/assistant';
|
||||
import { ChatMessageUser } from '../chat-panel/message/user';
|
||||
import { AIChatAddContext } from '../components/ai-chat-add-context';
|
||||
import { ChatPanelAddPopover } from '../components/ai-chat-chips/add-popover';
|
||||
import { ChatPanelAttachmentChip } from '../components/ai-chat-chips/attachment-chip';
|
||||
import { ChatPanelCandidatesPopover } from '../components/ai-chat-chips/candidates-popover';
|
||||
import { ChatPanelChips } from '../components/ai-chat-chips/chat-panel-chips';
|
||||
import { ChatPanelChip } from '../components/ai-chat-chips/chip';
|
||||
import { ChatPanelCollectionChip } from '../components/ai-chat-chips/collection-chip';
|
||||
import { ChatPanelDocChip } from '../components/ai-chat-chips/doc-chip';
|
||||
import { ChatPanelFileChip } from '../components/ai-chat-chips/file-chip';
|
||||
import { ChatPanelSelectedChip } from '../components/ai-chat-chips/selected-chip';
|
||||
import { ChatPanelTagChip } from '../components/ai-chat-chips/tag-chip';
|
||||
import { AIChatComposer } from '../components/ai-chat-composer';
|
||||
import { AIChatContent } from '../components/ai-chat-content';
|
||||
import { ChatPanelSplitView } from '../components/ai-chat-content/split-view';
|
||||
import { AIChatInput } from '../components/ai-chat-input';
|
||||
import { AIChatEmbeddingStatusTooltip } from '../components/ai-chat-input/embedding-status-tooltip';
|
||||
import { ChatInputPreference } from '../components/ai-chat-input/preference-popup';
|
||||
import { AIChatMessages } from '../components/ai-chat-messages/ai-chat-messages';
|
||||
import { AIChatToolbar, AISessionHistory } from '../components/ai-chat-toolbar';
|
||||
import { AIHistoryClear } from '../components/ai-history-clear';
|
||||
import { AssistantAvatar } from '../components/ai-message-content/assistant-avatar';
|
||||
import { ChatActionList } from '../components/chat-action-list';
|
||||
import { ChatCopyMore } from '../components/copy-more';
|
||||
import { ImagePreviewGrid } from '../components/image-preview-grid';
|
||||
import { effects as componentPlaygroundEffects } from '../components/playground';
|
||||
import { AIChatBlockPeekView } from '../peek-view/chat-block-peek-view';
|
||||
import { DateTime } from '../peek-view/date-time';
|
||||
import { type AppEffectElementTag, appEffectElementTags } from './registry';
|
||||
import { registerAISharedEffects } from './shared';
|
||||
|
||||
const appRegistries = new WeakSet<CustomElementRegistry>();
|
||||
const appElements = {
|
||||
'chat-action-list': ChatActionList,
|
||||
'chat-copy-more': ChatCopyMore,
|
||||
'image-preview-grid': ImagePreviewGrid,
|
||||
'action-wrapper': ActionWrapper,
|
||||
'action-image-to-text': ActionImageToText,
|
||||
'action-image': ActionImage,
|
||||
'action-make-real': ActionMakeReal,
|
||||
'action-mindmap': ActionMindmap,
|
||||
'action-slides': ActionSlides,
|
||||
'action-text': ActionText,
|
||||
'ai-loading': AILoading,
|
||||
'ai-chat-content': AIChatContent,
|
||||
'ai-chat-toolbar': AIChatToolbar,
|
||||
'ai-session-history': AISessionHistory,
|
||||
'ai-chat-messages': AIChatMessages,
|
||||
'ai-chat-input': AIChatInput,
|
||||
'ai-chat-add-context': AIChatAddContext,
|
||||
'ai-chat-embedding-status-tooltip': AIChatEmbeddingStatusTooltip,
|
||||
'ai-chat-composer': AIChatComposer,
|
||||
'chat-panel-chips': ChatPanelChips,
|
||||
'ai-history-clear': AIHistoryClear,
|
||||
'chat-panel-add-popover': ChatPanelAddPopover,
|
||||
'chat-input-preference': ChatInputPreference,
|
||||
'chat-panel-candidates-popover': ChatPanelCandidatesPopover,
|
||||
'chat-panel-doc-chip': ChatPanelDocChip,
|
||||
'chat-panel-file-chip': ChatPanelFileChip,
|
||||
'chat-panel-tag-chip': ChatPanelTagChip,
|
||||
'chat-panel-collection-chip': ChatPanelCollectionChip,
|
||||
'chat-panel-selected-chip': ChatPanelSelectedChip,
|
||||
'chat-panel-attachment-chip': ChatPanelAttachmentChip,
|
||||
'chat-panel-chip': ChatPanelChip,
|
||||
'chat-assistant-avatar': AssistantAvatar,
|
||||
'chat-message-action': ChatMessageAction,
|
||||
'chat-message-assistant': ChatMessageAssistant,
|
||||
'chat-message-user': ChatMessageUser,
|
||||
'ai-chat-block-peek-view': AIChatBlockPeekView,
|
||||
'date-time': DateTime,
|
||||
'chat-panel-split-view': ChatPanelSplitView,
|
||||
} satisfies Record<AppEffectElementTag, CustomElementConstructor>;
|
||||
|
||||
export function registerAIAppEffects() {
|
||||
const registry = customElements;
|
||||
if (appRegistries.has(registry)) return;
|
||||
appRegistries.add(registry);
|
||||
|
||||
registerAISharedEffects();
|
||||
componentPlaygroundEffects();
|
||||
|
||||
for (const tag of appEffectElementTags) {
|
||||
customElements.define(tag, appElements[tag]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { AIChatBlockComponent } from '../blocks/ai-chat-block/ai-chat-block';
|
||||
import { EdgelessAIChatBlockComponent } from '../blocks/ai-chat-block/ai-chat-edgeless-block';
|
||||
import { LitTranscriptionBlock } from '../blocks/ai-chat-block/ai-transcription-block';
|
||||
import {
|
||||
AIChatBlockMessage,
|
||||
AIChatBlockMessages,
|
||||
} from '../blocks/ai-chat-block/components/ai-chat-messages';
|
||||
import {
|
||||
ChatImage,
|
||||
ChatImages,
|
||||
} from '../blocks/ai-chat-block/components/chat-images';
|
||||
import { ImagePlaceholder } from '../blocks/ai-chat-block/components/image-placeholder';
|
||||
import { UserInfo } from '../blocks/ai-chat-block/components/user-info';
|
||||
import { effects as componentAiItemEffects } from '../components/ai-item';
|
||||
import { AIScrollableTextRenderer } from '../components/ai-scrollable-text-renderer';
|
||||
import { AskAIButton } from '../components/ask-ai-button';
|
||||
import { AskAIIcon } from '../components/ask-ai-icon';
|
||||
import { AskAIPanel } from '../components/ask-ai-panel';
|
||||
import { AskAIToolbarButton } from '../components/ask-ai-toolbar';
|
||||
import {
|
||||
AFFINE_AI_PANEL_WIDGET,
|
||||
AffineAIPanelWidget,
|
||||
} from '../widgets/ai-panel/ai-panel';
|
||||
import {
|
||||
AIPanelAnswer,
|
||||
AIPanelDivider,
|
||||
AIPanelError,
|
||||
AIPanelGenerating,
|
||||
AIPanelInput,
|
||||
} from '../widgets/ai-panel/components';
|
||||
import { AIFinishTip } from '../widgets/ai-panel/components/finish-tip';
|
||||
import { GeneratingPlaceholder } from '../widgets/ai-panel/components/generating-placeholder';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_BLOCK,
|
||||
AffineBlockDiffWidgetForBlock,
|
||||
} from '../widgets/block-diff/block';
|
||||
import { BlockDiffOptions } from '../widgets/block-diff/options';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_WIDGET_FOR_PAGE,
|
||||
AffineBlockDiffWidgetForPage,
|
||||
} from '../widgets/block-diff/page';
|
||||
import {
|
||||
AFFINE_BLOCK_DIFF_PLAYGROUND,
|
||||
AFFINE_BLOCK_DIFF_PLAYGROUND_MODAL,
|
||||
BlockDiffPlayground,
|
||||
BlockDiffPlaygroundModal,
|
||||
} from '../widgets/block-diff/playground';
|
||||
import {
|
||||
AFFINE_EDGELESS_COPILOT_WIDGET,
|
||||
EdgelessCopilotWidget,
|
||||
} from '../widgets/edgeless-copilot';
|
||||
import { EdgelessCopilotPanel } from '../widgets/edgeless-copilot-panel';
|
||||
import { EdgelessCopilotToolbarEntry } from '../widgets/edgeless-copilot-panel/toolbar-entry';
|
||||
import {
|
||||
type EditorEffectElementTag,
|
||||
editorEffectElementTags,
|
||||
} from './registry';
|
||||
import { registerAISharedEffects } from './shared';
|
||||
|
||||
const editorRegistries = new WeakSet<CustomElementRegistry>();
|
||||
const editorElements = {
|
||||
'ask-ai-icon': AskAIIcon,
|
||||
'ask-ai-button': AskAIButton,
|
||||
'ask-ai-toolbar-button': AskAIToolbarButton,
|
||||
'ask-ai-panel': AskAIPanel,
|
||||
'affine-edgeless-ai-chat': EdgelessAIChatBlockComponent,
|
||||
'affine-ai-chat': AIChatBlockComponent,
|
||||
'ai-chat-block-message': AIChatBlockMessage,
|
||||
'ai-chat-block-messages': AIChatBlockMessages,
|
||||
'ai-scrollable-text-renderer': AIScrollableTextRenderer,
|
||||
'image-placeholder': ImagePlaceholder,
|
||||
'chat-image': ChatImage,
|
||||
'chat-images': ChatImages,
|
||||
'user-info': UserInfo,
|
||||
'generating-placeholder': GeneratingPlaceholder,
|
||||
'ai-finish-tip': AIFinishTip,
|
||||
'ai-panel-divider': AIPanelDivider,
|
||||
'ai-panel-answer': AIPanelAnswer,
|
||||
'ai-panel-input': AIPanelInput,
|
||||
'ai-panel-generating': AIPanelGenerating,
|
||||
'ai-panel-error': AIPanelError,
|
||||
'ai-block-diff-options': BlockDiffOptions,
|
||||
[AFFINE_BLOCK_DIFF_PLAYGROUND]: BlockDiffPlayground,
|
||||
[AFFINE_BLOCK_DIFF_PLAYGROUND_MODAL]: BlockDiffPlaygroundModal,
|
||||
[AFFINE_AI_PANEL_WIDGET]: AffineAIPanelWidget,
|
||||
[AFFINE_EDGELESS_COPILOT_WIDGET]: EdgelessCopilotWidget,
|
||||
[AFFINE_BLOCK_DIFF_WIDGET_FOR_BLOCK]: AffineBlockDiffWidgetForBlock,
|
||||
[AFFINE_BLOCK_DIFF_WIDGET_FOR_PAGE]: AffineBlockDiffWidgetForPage,
|
||||
'edgeless-copilot-panel': EdgelessCopilotPanel,
|
||||
'edgeless-copilot-toolbar-entry': EdgelessCopilotToolbarEntry,
|
||||
'transcription-block': LitTranscriptionBlock,
|
||||
} satisfies Record<EditorEffectElementTag, CustomElementConstructor>;
|
||||
|
||||
export function registerAIEditorEffects() {
|
||||
const registry = customElements;
|
||||
if (editorRegistries.has(registry)) return;
|
||||
editorRegistries.add(registry);
|
||||
|
||||
registerAISharedEffects();
|
||||
componentAiItemEffects();
|
||||
|
||||
for (const tag of editorEffectElementTags) {
|
||||
customElements.define(tag, editorElements[tag]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
export const sharedEffectElementTags = [
|
||||
'ai-error-wrapper',
|
||||
'ai-slides-renderer',
|
||||
'ai-answer-wrapper',
|
||||
'chat-content-images',
|
||||
'chat-content-pure-text',
|
||||
'chat-content-rich-text',
|
||||
'chat-content-stream-objects',
|
||||
'text-renderer',
|
||||
'tool-call-card',
|
||||
'tool-result-card',
|
||||
'tool-call-failed',
|
||||
'doc-semantic-search-result',
|
||||
'doc-keyword-search-result',
|
||||
'doc-read-result',
|
||||
'doc-write-tool',
|
||||
'web-crawl-tool',
|
||||
'web-search-tool',
|
||||
'section-edit-tool',
|
||||
'doc-compose-tool',
|
||||
'code-artifact-tool',
|
||||
'code-highlighter',
|
||||
'artifact-preview-panel',
|
||||
'doc-edit-tool',
|
||||
'artifact-skeleton',
|
||||
] as const;
|
||||
|
||||
export type SharedEffectElementTag = (typeof sharedEffectElementTags)[number];
|
||||
|
||||
export const editorEffectElementTags = [
|
||||
'ask-ai-icon',
|
||||
'ask-ai-button',
|
||||
'ask-ai-toolbar-button',
|
||||
'ask-ai-panel',
|
||||
'affine-edgeless-ai-chat',
|
||||
'affine-ai-chat',
|
||||
'ai-chat-block-message',
|
||||
'ai-chat-block-messages',
|
||||
'ai-scrollable-text-renderer',
|
||||
'image-placeholder',
|
||||
'chat-image',
|
||||
'chat-images',
|
||||
'user-info',
|
||||
'generating-placeholder',
|
||||
'ai-finish-tip',
|
||||
'ai-panel-divider',
|
||||
'ai-panel-answer',
|
||||
'ai-panel-input',
|
||||
'ai-panel-generating',
|
||||
'ai-panel-error',
|
||||
'ai-block-diff-options',
|
||||
'affine-block-diff-playground',
|
||||
'affine-block-diff-playground-modal',
|
||||
'affine-ai-panel-widget',
|
||||
'affine-edgeless-copilot-widget',
|
||||
'affine-block-diff-widget-for-block',
|
||||
'affine-block-diff-widget-for-page',
|
||||
'edgeless-copilot-panel',
|
||||
'edgeless-copilot-toolbar-entry',
|
||||
'transcription-block',
|
||||
] as const;
|
||||
|
||||
export type EditorEffectElementTag = (typeof editorEffectElementTags)[number];
|
||||
|
||||
export const appEffectElementTags = [
|
||||
'chat-action-list',
|
||||
'chat-copy-more',
|
||||
'image-preview-grid',
|
||||
'action-wrapper',
|
||||
'action-image-to-text',
|
||||
'action-image',
|
||||
'action-make-real',
|
||||
'action-mindmap',
|
||||
'action-slides',
|
||||
'action-text',
|
||||
'ai-loading',
|
||||
'ai-chat-content',
|
||||
'ai-chat-toolbar',
|
||||
'ai-session-history',
|
||||
'ai-chat-messages',
|
||||
'ai-chat-input',
|
||||
'ai-chat-add-context',
|
||||
'ai-chat-embedding-status-tooltip',
|
||||
'ai-chat-composer',
|
||||
'chat-panel-chips',
|
||||
'ai-history-clear',
|
||||
'chat-panel-add-popover',
|
||||
'chat-input-preference',
|
||||
'chat-panel-candidates-popover',
|
||||
'chat-panel-doc-chip',
|
||||
'chat-panel-file-chip',
|
||||
'chat-panel-tag-chip',
|
||||
'chat-panel-collection-chip',
|
||||
'chat-panel-selected-chip',
|
||||
'chat-panel-attachment-chip',
|
||||
'chat-panel-chip',
|
||||
'chat-assistant-avatar',
|
||||
'chat-message-action',
|
||||
'chat-message-assistant',
|
||||
'chat-message-user',
|
||||
'ai-chat-block-peek-view',
|
||||
'date-time',
|
||||
'chat-panel-split-view',
|
||||
] as const;
|
||||
|
||||
export type AppEffectElementTag = (typeof appEffectElementTags)[number];
|
||||
@@ -0,0 +1,74 @@
|
||||
import { effects as tooltipEffects } from '@blocksuite/affine-components/tooltip';
|
||||
|
||||
import { ArtifactSkeleton } from '../components/ai-artifact-skeleton';
|
||||
import { ChatContentImages } from '../components/ai-message-content/images';
|
||||
import { ChatContentPureText } from '../components/ai-message-content/pure-text';
|
||||
import { ChatContentRichText } from '../components/ai-message-content/rich-text';
|
||||
import { ChatContentStreamObjects } from '../components/ai-message-content/stream-objects';
|
||||
import { ArtifactPreviewPanel } from '../components/ai-tools/artifacts-preview-panel';
|
||||
import {
|
||||
CodeArtifactTool,
|
||||
CodeHighlighter,
|
||||
} from '../components/ai-tools/code-artifact';
|
||||
import { DocComposeTool } from '../components/ai-tools/doc-compose';
|
||||
import { DocEditTool } from '../components/ai-tools/doc-edit';
|
||||
import { DocKeywordSearchResult } from '../components/ai-tools/doc-keyword-search-result';
|
||||
import { DocReadResult } from '../components/ai-tools/doc-read-result';
|
||||
import { DocSemanticSearchResult } from '../components/ai-tools/doc-semantic-search-result';
|
||||
import { DocWriteTool } from '../components/ai-tools/doc-write';
|
||||
import { SectionEditTool } from '../components/ai-tools/section-edit';
|
||||
import { ToolCallCard } from '../components/ai-tools/tool-call-card';
|
||||
import { ToolFailedCard } from '../components/ai-tools/tool-failed-card';
|
||||
import { ToolResultCard } from '../components/ai-tools/tool-result-card';
|
||||
import { WebCrawlTool } from '../components/ai-tools/web-crawl';
|
||||
import { WebSearchTool } from '../components/ai-tools/web-search';
|
||||
import { TextRenderer } from '../components/text-renderer';
|
||||
import { AIErrorWrapper } from '../messages/error';
|
||||
import { AISlidesRenderer } from '../messages/slides-renderer';
|
||||
import { AIAnswerWrapper } from '../messages/wrapper';
|
||||
import { registerMiniMindmapBlocks } from '../mini-mindmap';
|
||||
import {
|
||||
type SharedEffectElementTag,
|
||||
sharedEffectElementTags,
|
||||
} from './registry';
|
||||
|
||||
const sharedRegistries = new WeakSet<CustomElementRegistry>();
|
||||
const sharedElements = {
|
||||
'ai-error-wrapper': AIErrorWrapper,
|
||||
'ai-slides-renderer': AISlidesRenderer,
|
||||
'ai-answer-wrapper': AIAnswerWrapper,
|
||||
'chat-content-images': ChatContentImages,
|
||||
'chat-content-pure-text': ChatContentPureText,
|
||||
'chat-content-rich-text': ChatContentRichText,
|
||||
'chat-content-stream-objects': ChatContentStreamObjects,
|
||||
'text-renderer': TextRenderer,
|
||||
'tool-call-card': ToolCallCard,
|
||||
'tool-result-card': ToolResultCard,
|
||||
'tool-call-failed': ToolFailedCard,
|
||||
'doc-semantic-search-result': DocSemanticSearchResult,
|
||||
'doc-keyword-search-result': DocKeywordSearchResult,
|
||||
'doc-read-result': DocReadResult,
|
||||
'doc-write-tool': DocWriteTool,
|
||||
'web-crawl-tool': WebCrawlTool,
|
||||
'web-search-tool': WebSearchTool,
|
||||
'section-edit-tool': SectionEditTool,
|
||||
'doc-compose-tool': DocComposeTool,
|
||||
'code-artifact-tool': CodeArtifactTool,
|
||||
'code-highlighter': CodeHighlighter,
|
||||
'artifact-preview-panel': ArtifactPreviewPanel,
|
||||
'doc-edit-tool': DocEditTool,
|
||||
'artifact-skeleton': ArtifactSkeleton,
|
||||
} satisfies Record<SharedEffectElementTag, CustomElementConstructor>;
|
||||
|
||||
export function registerAISharedEffects() {
|
||||
const registry = customElements;
|
||||
if (sharedRegistries.has(registry)) return;
|
||||
sharedRegistries.add(registry);
|
||||
|
||||
registerMiniMindmapBlocks();
|
||||
tooltipEffects();
|
||||
|
||||
for (const tag of sharedEffectElementTags) {
|
||||
customElements.define(tag, sharedElements[tag]);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from './_common/config';
|
||||
export * from './actions';
|
||||
export { ChatPanel } from './chat-panel';
|
||||
export * from './entries';
|
||||
export * from './entries/edgeless/actions-config';
|
||||
export * from './messages';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { registerAIEffects } from '@affine/core/blocksuite/ai/effects';
|
||||
import { registerAIEditorEffects } from '@affine/core/blocksuite/ai/effects/editor';
|
||||
import { editorEffects } from '@affine/core/blocksuite/editors';
|
||||
|
||||
import { registerTemplates } from './register-templates';
|
||||
|
||||
editorEffects();
|
||||
registerAIEffects();
|
||||
registerAIEditorEffects();
|
||||
registerTemplates();
|
||||
|
||||
export * from './blocksuite-editor';
|
||||
|
||||
Reference in New Issue
Block a user