mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-26 14:58:55 +08:00
feat(editor): schema extension (#10447)
1. **Major Architectural Change: Schema Management**
- Moved from `workspace.schema` to `store.schema` throughout the codebase
- Removed schema property from Workspace and Doc interfaces
- Added `BlockSchemaExtension` pattern across multiple block types
2. **Block Schema Extensions Added**
- Added new `BlockSchemaExtension` to numerous block types including:
- DataView, Surface, Attachment, Bookmark, Code
- Database, Divider, EdgelessText, Embed blocks (Figma, Github, HTML, etc.)
- Frame, Image, Latex, List, Note, Paragraph
- Root, Surface Reference, Table blocks
3. **Import/Export System Updates**
- Updated import functions to accept `schema` parameter:
- `importHTMLToDoc`
- `importHTMLZip`
- `importMarkdownToDoc`
- `importMarkdownZip`
- `importNotionZip`
- Modified export functions to use new schema pattern
4. **Test Infrastructure Updates**
- Updated test files to use new schema extensions
- Modified test document creation to include schema extensions
- Removed direct schema registration in favor of extensions
5. **Service Layer Changes**
- Updated various services to use `getAFFiNEWorkspaceSchema()`
- Modified transformer initialization to use document schema
- Updated collection initialization patterns
6. **Version Management**
- Removed version-related properties and methods from:
- `WorkspaceMetaImpl`
- `TestMeta`
- `DocImpl`
- Removed `blockVersions` and `workspaceVersion/pageVersion`
7. **Store and Extension Updates**
- Added new store extensions and adapters
- Updated store initialization patterns
- Added new schema-related functionality in store extension
This PR represents a significant architectural shift in how schemas are managed, moving from a workspace-centric to a store-centric approach, while introducing a more extensible block schema system through `BlockSchemaExtension`. The changes touch multiple layers of the application including core functionality, services, testing infrastructure, and import/export capabilities.
This commit is contained in:
+8
-1
@@ -2,7 +2,11 @@ import {
|
||||
type GfxCommonBlockProps,
|
||||
GfxCompatible,
|
||||
} from '@blocksuite/affine/block-std/gfx';
|
||||
import { BlockModel, defineBlockSchema } from '@blocksuite/affine/store';
|
||||
import {
|
||||
BlockModel,
|
||||
BlockSchemaExtension,
|
||||
defineBlockSchema,
|
||||
} from '@blocksuite/affine/store';
|
||||
|
||||
type AIChatProps = {
|
||||
messages: string; // JSON string of ChatMessage[]
|
||||
@@ -33,4 +37,7 @@ export const AIChatBlockSchema = defineBlockSchema({
|
||||
},
|
||||
});
|
||||
|
||||
export const AIChatBlockSchemaExtension =
|
||||
BlockSchemaExtension(AIChatBlockSchema);
|
||||
|
||||
export class AIChatBlockModel extends GfxCompatible<AIChatProps>(BlockModel) {}
|
||||
|
||||
@@ -214,7 +214,7 @@ export class TextRenderer extends WithDisposable(ShadowlessElement) {
|
||||
if (this._answers.length > 0) {
|
||||
const latestAnswer = this._answers.pop();
|
||||
this._answers = [];
|
||||
const schema = this.schema ?? this.host?.std.store.workspace.schema;
|
||||
const schema = this.schema ?? this.host?.std.store.schema;
|
||||
let provider: ServiceProvider;
|
||||
if (this.host) {
|
||||
provider = this.host.std.provider;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SpecProvider } from '@blocksuite/affine/blocks';
|
||||
|
||||
import { AIChatBlockComponent } from './blocks/ai-chat-block/ai-chat-block';
|
||||
import { EdgelessAIChatBlockComponent } from './blocks/ai-chat-block/ai-chat-edgeless-block';
|
||||
import {
|
||||
@@ -10,6 +12,7 @@ import {
|
||||
} 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 { AIChatBlockSchemaExtension } from './blocks/ai-chat-block/model';
|
||||
import { ChatPanel } from './chat-panel';
|
||||
import { ActionWrapper } from './chat-panel/actions/action-wrapper';
|
||||
import { ChatText } from './chat-panel/actions/chat-text';
|
||||
@@ -126,4 +129,6 @@ export function registerAIEffects() {
|
||||
'edgeless-copilot-toolbar-entry',
|
||||
EdgelessCopilotToolbarEntry
|
||||
);
|
||||
|
||||
SpecProvider._.extendSpec('store', [AIChatBlockSchemaExtension]);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace';
|
||||
import { BlockStdScope, type EditorHost } from '@blocksuite/affine/block-std';
|
||||
import { SpecProvider } from '@blocksuite/affine/blocks';
|
||||
import { AffineSchemas } from '@blocksuite/affine/blocks/schemas';
|
||||
import { WithDisposable } from '@blocksuite/affine/global/utils';
|
||||
import { Schema, type Store } from '@blocksuite/affine/store';
|
||||
import type { Store } from '@blocksuite/affine/store';
|
||||
import { css, html, LitElement, nothing } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { createRef, type Ref, ref } from 'lit/directives/ref.js';
|
||||
@@ -216,9 +215,7 @@ export class AISlidesRenderer extends WithDisposable(LitElement) {
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
const schema = new Schema().register(AffineSchemas);
|
||||
const collection = new WorkspaceImpl({
|
||||
schema,
|
||||
id: 'SLIDES_PREVIEW',
|
||||
});
|
||||
collection.meta.initialize();
|
||||
|
||||
+3
-4
@@ -4,7 +4,6 @@ import {
|
||||
MarkdownInlineToDeltaAdapterExtensions,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import { Container } from '@blocksuite/affine/global/di';
|
||||
import { Schema } from '@blocksuite/affine/store';
|
||||
import { TestWorkspace } from '@blocksuite/affine/store/test';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
@@ -29,7 +28,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
|
||||
- Text D
|
||||
- Text E
|
||||
`;
|
||||
const collection = new TestWorkspace({ schema: new Schema() });
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
@@ -67,7 +66,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
|
||||
- Text D
|
||||
- Text E
|
||||
`;
|
||||
const collection = new TestWorkspace({ schema: new Schema() });
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
@@ -99,7 +98,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
|
||||
|
||||
test('empty case', () => {
|
||||
const markdown = '';
|
||||
const collection = new TestWorkspace({ schema: new Schema() });
|
||||
const collection = new TestWorkspace();
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc, provider);
|
||||
|
||||
@@ -99,7 +99,6 @@ export class MiniMindmapPreview extends WithDisposable(LitElement) {
|
||||
|
||||
const collection = new WorkspaceImpl({
|
||||
id: 'MINI_MINDMAP_TEMPORARY',
|
||||
schema,
|
||||
});
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: 'doc:home' }).load();
|
||||
@@ -237,7 +236,7 @@ export const markdownToMindmap = (
|
||||
) => {
|
||||
let result: Node | null = null;
|
||||
const transformer = new Transformer({
|
||||
schema: doc.workspace.schema,
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.workspace.createDoc({ id }),
|
||||
|
||||
@@ -197,7 +197,7 @@ function getNoteBlockModels(doc: Store) {
|
||||
|
||||
async function getTransformer(doc: Store) {
|
||||
return new Transformer({
|
||||
schema: doc.workspace.schema,
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.workspace.createDoc({ id }),
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||
import { DocsService } from '@affine/core/modules/doc';
|
||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace';
|
||||
import { type DocMode } from '@blocksuite/affine/blocks';
|
||||
import type { Workspace } from '@blocksuite/affine/store';
|
||||
import { useServices } from '@toeverything/infra';
|
||||
@@ -110,6 +111,7 @@ export const usePageHelper = (docCollection: Workspace) => {
|
||||
};
|
||||
showImportModal({
|
||||
collection: docCollection,
|
||||
schema: getAFFiNEWorkspaceSchema(),
|
||||
onSuccess,
|
||||
onFail: message => {
|
||||
reject(new Error(message));
|
||||
|
||||
@@ -81,7 +81,7 @@ export async function getContentFromSlice(
|
||||
type: 'markdown' | 'plain-text' = 'markdown'
|
||||
) {
|
||||
const transformer = new Transformer({
|
||||
schema: host.std.store.workspace.schema,
|
||||
schema: host.std.store.schema,
|
||||
blobCRUD: host.std.store.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => host.std.store.workspace.createDoc({ id }),
|
||||
@@ -114,7 +114,7 @@ export const markdownToSnapshot = async (
|
||||
host: EditorHost
|
||||
) => {
|
||||
const transformer = new Transformer({
|
||||
schema: host.std.store.workspace.schema,
|
||||
schema: host.std.store.schema,
|
||||
blobCRUD: host.std.store.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => host.std.store.workspace.createDoc({ id }),
|
||||
@@ -174,12 +174,10 @@ export async function markDownToDoc(
|
||||
middlewares?: TransformerMiddleware[]
|
||||
) {
|
||||
// Should not create a new doc in the original collection
|
||||
const collection = new WorkspaceImpl({
|
||||
schema,
|
||||
});
|
||||
const collection = new WorkspaceImpl();
|
||||
collection.meta.initialize();
|
||||
const transformer = new Transformer({
|
||||
schema: collection.schema,
|
||||
schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
|
||||
Reference in New Issue
Block a user