From ce87dcf58ec980f882cfce85a9492db2215ae6e2 Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Wed, 26 Feb 2025 11:31:28 +0000 Subject: [PATCH] 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. --- .../block-data-view/src/data-view-model.ts | 9 +- .../src/edgeless/clipboard/clipboard.ts | 4 +- .../src/edgeless/services/template.ts | 5 +- .../src/edgeless/utils/clone-utils.ts | 2 +- .../block-root/src/transformers/html.ts | 17 ++- .../block-root/src/transformers/markdown.ts | 12 ++- .../src/transformers/notion-html.ts | 12 ++- .../affine/block-root/src/transformers/zip.ts | 18 +++- .../src/widgets/linked-doc/config.ts | 1 + .../linked-doc/import-doc/import-doc.ts | 6 +- .../widgets/linked-doc/import-doc/index.ts | 5 +- blocksuite/affine/block-surface/src/index.ts | 6 +- .../affine/block-surface/src/surface-model.ts | 5 +- .../src/blocks/attachment/attachment-model.ts | 10 +- .../src/blocks/bookmark/bookmark-model.ts | 9 +- .../model/src/blocks/code/code-model.ts | 9 +- .../src/blocks/database/database-model.ts | 9 +- .../model/src/blocks/divider/divider-model.ts | 9 +- .../edgeless-text/edgeless-text-model.ts | 10 +- .../src/blocks/embed/figma/figma-schema.ts | 6 ++ .../src/blocks/embed/github/github-schema.ts | 6 ++ .../src/blocks/embed/html/html-schema.ts | 5 + .../embed/linked-doc/linked-doc-schema.ts | 6 ++ .../src/blocks/embed/loom/loom-schema.ts | 5 + .../embed/synced-doc/synced-doc-schema.ts | 6 ++ .../blocks/embed/youtube/youtube-schema.ts | 6 ++ .../model/src/blocks/frame/frame-model.ts | 9 +- .../model/src/blocks/image/image-model.ts | 8 +- .../model/src/blocks/latex/latex-model.ts | 8 +- .../model/src/blocks/list/list-model.ts | 8 +- .../model/src/blocks/note/note-model.ts | 9 +- .../src/blocks/paragraph/paragraph-model.ts | 10 +- .../model/src/blocks/root/root-block-model.ts | 8 +- .../blocks/surface-ref/surface-ref-model.ts | 10 +- .../model/src/blocks/table/table-model.ts | 8 +- .../adapters/notion-text.unit.spec.ts | 3 + .../__tests__/database/database.unit.spec.ts | 26 +++-- .../blocks/src/__tests__/utils/create-job.ts | 6 +- blocksuite/blocks/src/extensions/common.ts | 55 +--------- blocksuite/blocks/src/extensions/index.ts | 1 + blocksuite/blocks/src/extensions/register.ts | 2 +- blocksuite/blocks/src/extensions/store.ts | 101 ++++++++++++++++++ .../src/__tests__/editor-host.unit.spec.ts | 19 ++-- .../block-std/src/__tests__/test-schema.ts | 13 ++- .../block-std/src/scope/block-std-scope.ts | 2 +- .../store/src/__tests__/block.unit.spec.ts | 19 +++- .../src/__tests__/collection.unit.spec.ts | 43 ++++---- .../store/src/__tests__/doc.unit.spec.ts | 46 ++++---- .../store/src/__tests__/schema.unit.spec.ts | 42 ++++---- .../store/src/__tests__/test-schema.ts | 13 +++ .../src/__tests__/transformer.unit.spec.ts | 13 +-- .../framework/store/src/extension/index.ts | 1 + .../framework/store/src/extension/schema.ts | 20 ++++ blocksuite/framework/store/src/model/doc.ts | 2 - .../framework/store/src/model/store/store.ts | 24 ++--- .../store/src/model/workspace-meta.ts | 4 - .../framework/store/src/model/workspace.ts | 2 - .../framework/store/src/test/test-doc.ts | 16 --- .../framework/store/src/test/test-meta.ts | 55 ---------- .../store/src/test/test-workspace.ts | 21 ++-- .../src/__tests__/main/snapshot.spec.ts | 11 +- .../src/__tests__/utils/misc.ts | 5 +- .../_common/components/starter-debug-menu.ts | 17 ++- blocksuite/playground/apps/_common/helper.ts | 6 +- .../apps/starter/data/affine-snapshot.ts | 8 +- .../apps/starter/utils/collection.ts | 3 +- blocksuite/tests-legacy/e2e/utils/asserts.ts | 27 ----- packages/frontend/apps/android/src/app.tsx | 2 +- packages/frontend/apps/ios/src/app.tsx | 2 +- .../ai-chat-block/model/ai-chat-model.ts | 9 +- .../blocksuite/ai/components/text-renderer.ts | 2 +- .../core/src/blocksuite/ai/effects.ts | 5 + .../blocksuite/ai/messages/slides-renderer.ts | 5 +- .../__tests__/mindmap-preview.unit.spec.ts | 7 +- .../ai/mini-mindmap/mindmap-preview.ts | 3 +- .../core/src/blocksuite/ai/utils/extract.ts | 2 +- .../block-suite-page-list/utils.tsx | 2 + .../src/blocksuite/utils/markdown-utils.ts | 10 +- .../affine/page-history-modal/data.ts | 2 - .../hooks/affine/use-export-page.ts | 9 +- .../use-block-suite-page-preview.spec.ts | 11 +- .../providers/workspace-side-effects.tsx | 6 +- .../core/src/desktop/dialogs/import/index.tsx | 17 ++- .../editor/edgeless/docs/index.ts | 10 +- .../src/desktop/pages/workspace/index.tsx | 3 + .../core/src/modules/doc/services/docs.ts | 3 +- .../modules/docs-search/worker/in-worker.ts | 3 +- .../import-template/services/import.ts | 7 +- .../modules/workspace-engine/impls/cloud.ts | 14 ++- .../modules/workspace-engine/impls/local.ts | 12 +-- .../modules/workspace/entities/workspace.ts | 2 - .../core/src/modules/workspace/impls/doc.ts | 16 --- .../core/src/modules/workspace/impls/meta.ts | 55 ---------- .../src/modules/workspace/impls/workspace.ts | 13 +-- .../frontend/core/src/utils/first-app-data.ts | 11 +- 95 files changed, 655 insertions(+), 490 deletions(-) create mode 100644 blocksuite/blocks/src/extensions/store.ts create mode 100644 blocksuite/framework/store/src/extension/schema.ts diff --git a/blocksuite/affine/block-data-view/src/data-view-model.ts b/blocksuite/affine/block-data-view/src/data-view-model.ts index b4445deb4c232..46a7c636f2d9b 100644 --- a/blocksuite/affine/block-data-view/src/data-view-model.ts +++ b/blocksuite/affine/block-data-view/src/data-view-model.ts @@ -5,7 +5,11 @@ import { type InsertToPosition, } from '@blocksuite/affine-shared/utils'; import type { DataViewDataType } from '@blocksuite/data-view'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; type Props = { title: string; @@ -93,3 +97,6 @@ export const DataViewBlockSchema = defineBlockSchema({ return new DataViewBlockModel(); }, }); + +export const DataViewBlockSchemaExtension = + BlockSchemaExtension(DataViewBlockSchema); diff --git a/blocksuite/affine/block-root/src/edgeless/clipboard/clipboard.ts b/blocksuite/affine/block-root/src/edgeless/clipboard/clipboard.ts index f5d172ed96c9b..ed9bf3a434929 100644 --- a/blocksuite/affine/block-root/src/edgeless/clipboard/clipboard.ts +++ b/blocksuite/affine/block-root/src/edgeless/clipboard/clipboard.ts @@ -374,7 +374,7 @@ export class EdgelessClipboardController extends PageClipboard { const elementsRawData = JSON.parse(mayBeSurfaceDataJson); const { snapshot, blobs } = elementsRawData; const job = new Transformer({ - schema: this.std.workspace.schema, + schema: this.std.store.schema, blobCRUD: this.std.workspace.blobSync, docCRUD: { create: (id: string) => this.std.workspace.createDoc({ id }), @@ -1378,7 +1378,7 @@ export async function prepareClipboardData( std: BlockStdScope ) { const job = new Transformer({ - schema: std.workspace.schema, + schema: std.store.schema, blobCRUD: std.workspace.blobSync, docCRUD: { create: (id: string) => std.workspace.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/edgeless/services/template.ts b/blocksuite/affine/block-root/src/edgeless/services/template.ts index 91acc938943f2..80556a59eb179 100644 --- a/blocksuite/affine/block-root/src/edgeless/services/template.ts +++ b/blocksuite/affine/block-root/src/edgeless/services/template.ts @@ -91,7 +91,7 @@ export class TemplateJob { constructor({ model, type, middlewares }: TemplateJobConfig) { this.job = new Transformer({ - schema: model.doc.workspace.schema, + schema: model.doc.schema, blobCRUD: model.doc.workspace.blobSync, docCRUD: { create: (id: string) => model.doc.workspace.createDoc({ id }), @@ -320,8 +320,7 @@ export class TemplateJob { from: Record>, to: Y.Map> ) { - const schema = - this.model.doc.workspace.schema.flavourSchemaMap.get('affine:surface'); + const schema = this.model.doc.schema.get('affine:surface'); const surfaceTransformer = schema?.transformer?.( new Map() ) as SurfaceBlockTransformer; diff --git a/blocksuite/affine/block-root/src/edgeless/utils/clone-utils.ts b/blocksuite/affine/block-root/src/edgeless/utils/clone-utils.ts index ac5f8e15aad61..5b9c810184576 100644 --- a/blocksuite/affine/block-root/src/edgeless/utils/clone-utils.ts +++ b/blocksuite/affine/block-root/src/edgeless/utils/clone-utils.ts @@ -41,7 +41,7 @@ export function getSortedCloneElements(elements: GfxModel[]) { export function prepareCloneData(elements: GfxModel[], std: BlockStdScope) { elements = sortEdgelessElements(elements); const job = new Transformer({ - schema: std.workspace.schema, + schema: std.store.schema, blobCRUD: std.workspace.blobSync, docCRUD: { create: (id: string) => std.workspace.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/transformers/html.ts b/blocksuite/affine/block-root/src/transformers/html.ts index 64ac0599190c9..c9802d11cd64e 100644 --- a/blocksuite/affine/block-root/src/transformers/html.ts +++ b/blocksuite/affine/block-root/src/transformers/html.ts @@ -8,19 +8,21 @@ import { import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { Container } from '@blocksuite/global/di'; import { sha } from '@blocksuite/global/utils'; -import type { Store, Workspace } from '@blocksuite/store'; +import type { Schema, Store, Workspace } from '@blocksuite/store'; import { extMimeMap, Transformer } from '@blocksuite/store'; import { createAssetsArchive, download, Unzip } from './utils.js'; type ImportHTMLToDocOptions = { collection: Workspace; + schema: Schema; html: string; fileName?: string; }; type ImportHTMLZipOptions = { collection: Workspace; + schema: Schema; imported: Blob; }; @@ -87,18 +89,20 @@ async function exportDoc(doc: Store) { * * @param options - The import options. * @param options.collection - The target doc collection. + * @param options.schema - The schema of the target doc collection. * @param options.html - The HTML content to import. * @param options.fileName - Optional filename for the imported doc. * @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails. */ async function importHTMLToDoc({ collection, + schema, html, fileName, }: ImportHTMLToDocOptions) { const provider = getProvider(); const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), @@ -127,10 +131,15 @@ async function importHTMLToDoc({ * * @param options - The import options. * @param options.collection - The target doc collection. + * @param options.schema - The schema of the target doc collection. * @param options.imported - The zip file as a Blob. * @returns A Promise that resolves to an array of IDs of the newly created docs. */ -async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) { +async function importHTMLZip({ + collection, + schema, + imported, +}: ImportHTMLZipOptions) { const provider = getProvider(); const unzip = new Unzip(); await unzip.load(imported); @@ -161,7 +170,7 @@ async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) { htmlBlobs.map(async ([fileName, blob]) => { const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, ''); const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/transformers/markdown.ts b/blocksuite/affine/block-root/src/transformers/markdown.ts index ce620706b8379..991d38d7d7a76 100644 --- a/blocksuite/affine/block-root/src/transformers/markdown.ts +++ b/blocksuite/affine/block-root/src/transformers/markdown.ts @@ -9,7 +9,7 @@ import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { Container } from '@blocksuite/global/di'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { assertExists, sha } from '@blocksuite/global/utils'; -import type { Store, Workspace } from '@blocksuite/store'; +import type { Schema, Store, Workspace } from '@blocksuite/store'; import { extMimeMap, Transformer } from '@blocksuite/store'; import { createAssetsArchive, download, Unzip } from './utils.js'; @@ -31,12 +31,14 @@ type ImportMarkdownToBlockOptions = { type ImportMarkdownToDocOptions = { collection: Workspace; + schema: Schema; markdown: string; fileName?: string; }; type ImportMarkdownZipOptions = { collection: Workspace; + schema: Schema; imported: Blob; }; @@ -143,18 +145,20 @@ async function importMarkdownToBlock({ * Imports Markdown content into a new doc within a collection. * @param options Object containing import options * @param options.collection The target doc collection + * @param options.schema The schema of the target doc collection * @param options.markdown The Markdown content to import * @param options.fileName Optional filename for the imported doc * @returns A Promise that resolves to the ID of the newly created doc, or undefined if import fails */ async function importMarkdownToDoc({ collection, + schema, markdown, fileName, }: ImportMarkdownToDocOptions) { const provider = getProvider(); const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), @@ -182,11 +186,13 @@ async function importMarkdownToDoc({ * Imports a zip file containing Markdown files and assets into a collection. * @param options Object containing import options * @param options.collection The target doc collection + * @param options.schema The schema of the target doc collection * @param options.imported The zip file as a Blob * @returns A Promise that resolves to an array of IDs of the newly created docs */ async function importMarkdownZip({ collection, + schema, imported, }: ImportMarkdownZipOptions) { const provider = getProvider(); @@ -219,7 +225,7 @@ async function importMarkdownZip({ markdownBlobs.map(async ([fileName, blob]) => { const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, ''); const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/transformers/notion-html.ts b/blocksuite/affine/block-root/src/transformers/notion-html.ts index 052d90999ea87..a9e29bd2ebd8e 100644 --- a/blocksuite/affine/block-root/src/transformers/notion-html.ts +++ b/blocksuite/affine/block-root/src/transformers/notion-html.ts @@ -3,12 +3,18 @@ import { NotionHtmlAdapter } from '@blocksuite/affine-shared/adapters'; import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { Container } from '@blocksuite/global/di'; import { sha } from '@blocksuite/global/utils'; -import { extMimeMap, Transformer, type Workspace } from '@blocksuite/store'; +import { + extMimeMap, + type Schema, + Transformer, + type Workspace, +} from '@blocksuite/store'; import { Unzip } from './utils.js'; type ImportNotionZipOptions = { collection: Workspace; + schema: Schema; imported: Blob; }; @@ -26,6 +32,7 @@ function getProvider() { * * @param options - The options for importing. * @param options.collection - The BlockSuite document collection. + * @param options.schema - The schema of the BlockSuite document collection. * @param options.imported - The imported zip file as a Blob. * * @returns A promise that resolves to an object containing: @@ -36,6 +43,7 @@ function getProvider() { */ async function importNotionZip({ collection, + schema, imported, }: ImportNotionZipOptions) { const provider = getProvider(); @@ -117,7 +125,7 @@ async function importNotionZip({ } const pagePromises = Array.from(pagePaths).map(async path => { const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/transformers/zip.ts b/blocksuite/affine/block-root/src/transformers/zip.ts index 6db2c6c8fb29f..34a0e334f939e 100644 --- a/blocksuite/affine/block-root/src/transformers/zip.ts +++ b/blocksuite/affine/block-root/src/transformers/zip.ts @@ -3,15 +3,19 @@ import { titleMiddleware, } from '@blocksuite/affine-shared/adapters'; import { sha } from '@blocksuite/global/utils'; -import type { DocSnapshot, Store, Workspace } from '@blocksuite/store'; +import type { DocSnapshot, Schema, Store, Workspace } from '@blocksuite/store'; import { extMimeMap, getAssetName, Transformer } from '@blocksuite/store'; import { download, Unzip, Zip } from '../transformers/utils.js'; -async function exportDocs(collection: Workspace, docs: Store[]) { +async function exportDocs( + collection: Workspace, + schema: Schema, + docs: Store[] +) { const zip = new Zip(); const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), @@ -70,7 +74,11 @@ async function exportDocs(collection: Workspace, docs: Store[]) { return download(downloadBlob, `${collection.id}.bs.zip`); } -async function importDocs(collection: Workspace, imported: Blob) { +async function importDocs( + collection: Workspace, + schema: Schema, + imported: Blob +) { const unzip = new Unzip(); await unzip.load(imported); @@ -98,7 +106,7 @@ async function importDocs(collection: Workspace, imported: Blob) { } const job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/affine/block-root/src/widgets/linked-doc/config.ts b/blocksuite/affine/block-root/src/widgets/linked-doc/config.ts index e74cfe73e3dbf..0b27db500e8ad 100644 --- a/blocksuite/affine/block-root/src/widgets/linked-doc/config.ts +++ b/blocksuite/affine/block-root/src/widgets/linked-doc/config.ts @@ -233,6 +233,7 @@ export function createNewDocMenuGroup( }; showImportModal({ collection: doc.workspace, + schema: doc.schema, onSuccess, onFail, }); diff --git a/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/import-doc.ts b/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/import-doc.ts index 196de21997feb..2a5a2366baece 100644 --- a/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/import-doc.ts +++ b/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/import-doc.ts @@ -8,7 +8,7 @@ import { } from '@blocksuite/affine-components/icons'; import { openFileOrFiles } from '@blocksuite/affine-shared/utils'; import { WithDisposable } from '@blocksuite/global/utils'; -import type { Workspace } from '@blocksuite/store'; +import type { Schema, Workspace } from '@blocksuite/store'; import { html, LitElement, type PropertyValues } from 'lit'; import { query, state } from 'lit/decorators.js'; @@ -31,6 +31,7 @@ export class ImportDoc extends WithDisposable(LitElement) { constructor( private readonly collection: Workspace, + private readonly schema: Schema, private readonly onSuccess?: OnSuccessHandler, private readonly onFail?: OnFailHandler, private readonly abortController = new AbortController() @@ -63,6 +64,7 @@ export class ImportDoc extends WithDisposable(LitElement) { } const pageId = await HtmlTransformer.importHTMLToDoc({ collection: this.collection, + schema: this.schema, html: text, fileName, }); @@ -93,6 +95,7 @@ export class ImportDoc extends WithDisposable(LitElement) { } const pageId = await MarkdownTransformer.importMarkdownToDoc({ collection: this.collection, + schema: this.schema, markdown: text, fileName, }); @@ -117,6 +120,7 @@ export class ImportDoc extends WithDisposable(LitElement) { const { entryId, pageIds, isWorkspaceFile, hasMarkdown } = await NotionHtmlTransformer.importNotionZip({ collection: this.collection, + schema: this.schema, imported: file, }); needLoading && this.abortController.abort(); diff --git a/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/index.ts b/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/index.ts index 0de77bcceb9a4..1970bc2fcad26 100644 --- a/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/index.ts +++ b/blocksuite/affine/block-root/src/widgets/linked-doc/import-doc/index.ts @@ -1,4 +1,4 @@ -import type { Workspace } from '@blocksuite/store'; +import type { Schema, Workspace } from '@blocksuite/store'; import { ImportDoc, @@ -7,12 +7,14 @@ import { } from './import-doc.js'; export function showImportModal({ + schema, collection, onSuccess, onFail, container = document.body, abortController = new AbortController(), }: { + schema: Schema; collection: Workspace; onSuccess?: OnSuccessHandler; onFail?: OnFailHandler; @@ -22,6 +24,7 @@ export function showImportModal({ }) { const importDoc = new ImportDoc( collection, + schema, onSuccess, onFail, abortController diff --git a/blocksuite/affine/block-surface/src/index.ts b/blocksuite/affine/block-surface/src/index.ts index 7842d3c7d9a9a..039171407724a 100644 --- a/blocksuite/affine/block-surface/src/index.ts +++ b/blocksuite/affine/block-surface/src/index.ts @@ -50,7 +50,11 @@ export { } from './adapters/index.js'; export type { SurfaceContext } from './surface-block.js'; export { SurfaceBlockComponent } from './surface-block.js'; -export { SurfaceBlockModel, SurfaceBlockSchema } from './surface-model.js'; +export { + SurfaceBlockModel, + SurfaceBlockSchema, + SurfaceBlockSchemaExtension, +} from './surface-model.js'; export type { SurfaceBlockService } from './surface-service.js'; export { EdgelessSurfaceBlockSpec, diff --git a/blocksuite/affine/block-surface/src/surface-model.ts b/blocksuite/affine/block-surface/src/surface-model.ts index 460318018fede..28487480a5fe7 100644 --- a/blocksuite/affine/block-surface/src/surface-model.ts +++ b/blocksuite/affine/block-surface/src/surface-model.ts @@ -5,7 +5,7 @@ import type { import type { SurfaceBlockProps } from '@blocksuite/block-std/gfx'; import { SurfaceBlockModel as BaseSurfaceModel } from '@blocksuite/block-std/gfx'; import { DisposableGroup } from '@blocksuite/global/utils'; -import { defineBlockSchema } from '@blocksuite/store'; +import { BlockSchemaExtension, defineBlockSchema } from '@blocksuite/store'; import * as Y from 'yjs'; import { elementsCtorMap } from './element-model/index.js'; @@ -36,6 +36,9 @@ export const SurfaceBlockSchema = defineBlockSchema({ toModel: () => new SurfaceBlockModel(), }); +export const SurfaceBlockSchemaExtension = + BlockSchemaExtension(SurfaceBlockSchema); + export type SurfaceMiddleware = (surface: SurfaceBlockModel) => () => void; export class SurfaceBlockModel extends BaseSurfaceModel { diff --git a/blocksuite/affine/model/src/blocks/attachment/attachment-model.ts b/blocksuite/affine/model/src/blocks/attachment/attachment-model.ts index bb0d293998687..9120f3eaedea4 100644 --- a/blocksuite/affine/model/src/blocks/attachment/attachment-model.ts +++ b/blocksuite/affine/model/src/blocks/attachment/attachment-model.ts @@ -3,7 +3,11 @@ import type { GfxElementGeometry, } from '@blocksuite/block-std/gfx'; import { GfxCompatible } from '@blocksuite/block-std/gfx'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import type { EmbedCardStyle } from '../../utils/index.js'; import { AttachmentBlockTransformer } from './attachment-transformer.js'; @@ -86,6 +90,10 @@ export const AttachmentBlockSchema = defineBlockSchema({ toModel: () => new AttachmentBlockModel(), }); +export const AttachmentBlockSchemaExtension = BlockSchemaExtension( + AttachmentBlockSchema +); + export class AttachmentBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry {} diff --git a/blocksuite/affine/model/src/blocks/bookmark/bookmark-model.ts b/blocksuite/affine/model/src/blocks/bookmark/bookmark-model.ts index 6f5027b9d7173..b75c1791ea2b6 100644 --- a/blocksuite/affine/model/src/blocks/bookmark/bookmark-model.ts +++ b/blocksuite/affine/model/src/blocks/bookmark/bookmark-model.ts @@ -3,7 +3,11 @@ import type { GfxElementGeometry, } from '@blocksuite/block-std/gfx'; import { GfxCompatible } from '@blocksuite/block-std/gfx'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import type { EmbedCardStyle, LinkPreviewData } from '../../utils/index.js'; @@ -54,6 +58,9 @@ export const BookmarkBlockSchema = defineBlockSchema({ toModel: () => new BookmarkBlockModel(), }); +export const BookmarkBlockSchemaExtension = + BlockSchemaExtension(BookmarkBlockSchema); + export class BookmarkBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry {} diff --git a/blocksuite/affine/model/src/blocks/code/code-model.ts b/blocksuite/affine/model/src/blocks/code/code-model.ts index f93e94323defc..a06e1a7b516a9 100644 --- a/blocksuite/affine/model/src/blocks/code/code-model.ts +++ b/blocksuite/affine/model/src/blocks/code/code-model.ts @@ -1,4 +1,9 @@ -import { BlockModel, defineBlockSchema, type Text } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, + type Text, +} from '@blocksuite/store'; interface CodeBlockProps { text: Text; @@ -30,6 +35,8 @@ export const CodeBlockSchema = defineBlockSchema({ toModel: () => new CodeBlockModel(), }); +export const CodeBlockSchemaExtension = BlockSchemaExtension(CodeBlockSchema); + export class CodeBlockModel extends BlockModel { override text!: Text; } diff --git a/blocksuite/affine/model/src/blocks/database/database-model.ts b/blocksuite/affine/model/src/blocks/database/database-model.ts index eda993465296e..35d1f860c0312 100644 --- a/blocksuite/affine/model/src/blocks/database/database-model.ts +++ b/blocksuite/affine/model/src/blocks/database/database-model.ts @@ -1,5 +1,9 @@ import type { Text } from '@blocksuite/store'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import type { Column, SerializedCells, ViewBasicDataType } from './types.js'; @@ -28,3 +32,6 @@ export const DatabaseBlockSchema = defineBlockSchema({ }, toModel: () => new DatabaseBlockModel(), }); + +export const DatabaseBlockSchemaExtension = + BlockSchemaExtension(DatabaseBlockSchema); diff --git a/blocksuite/affine/model/src/blocks/divider/divider-model.ts b/blocksuite/affine/model/src/blocks/divider/divider-model.ts index ef5bd6a1f9051..0746c61297a7d 100644 --- a/blocksuite/affine/model/src/blocks/divider/divider-model.ts +++ b/blocksuite/affine/model/src/blocks/divider/divider-model.ts @@ -1,4 +1,8 @@ -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export const DividerBlockSchema = defineBlockSchema({ flavour: 'affine:divider', @@ -15,3 +19,6 @@ type Props = { }; export class DividerBlockModel extends BlockModel {} + +export const DividerBlockSchemaExtension = + BlockSchemaExtension(DividerBlockSchema); diff --git a/blocksuite/affine/model/src/blocks/edgeless-text/edgeless-text-model.ts b/blocksuite/affine/model/src/blocks/edgeless-text/edgeless-text-model.ts index 4559a9a11b1d2..126542f420157 100644 --- a/blocksuite/affine/model/src/blocks/edgeless-text/edgeless-text-model.ts +++ b/blocksuite/affine/model/src/blocks/edgeless-text/edgeless-text-model.ts @@ -3,7 +3,11 @@ import type { GfxElementGeometry, } from '@blocksuite/block-std/gfx'; import { GfxCompatible } from '@blocksuite/block-std/gfx'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import { z } from 'zod'; import { @@ -76,6 +80,10 @@ export const EdgelessTextBlockSchema = defineBlockSchema({ }, }); +export const EdgelessTextBlockSchemaExtension = BlockSchemaExtension( + EdgelessTextBlockSchema +); + export class EdgelessTextBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry {} diff --git a/blocksuite/affine/model/src/blocks/embed/figma/figma-schema.ts b/blocksuite/affine/model/src/blocks/embed/figma/figma-schema.ts index 9c56dcd55022a..0d7c04c635205 100644 --- a/blocksuite/affine/model/src/blocks/embed/figma/figma-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/figma/figma-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedFigmaBlockProps, @@ -20,3 +22,7 @@ export const EmbedFigmaBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedFigmaModel(), props: (): EmbedFigmaBlockProps => defaultEmbedFigmaProps, }); + +export const EmbedFigmaBlockSchemaExtension = BlockSchemaExtension( + EmbedFigmaBlockSchema +); diff --git a/blocksuite/affine/model/src/blocks/embed/github/github-schema.ts b/blocksuite/affine/model/src/blocks/embed/github/github-schema.ts index e84f52eaf398e..fc857601180c4 100644 --- a/blocksuite/affine/model/src/blocks/embed/github/github-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/github/github-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedGithubBlockProps, @@ -29,3 +31,7 @@ export const EmbedGithubBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedGithubModel(), props: (): EmbedGithubBlockProps => defaultEmbedGithubProps, }); + +export const EmbedGithubBlockSchemaExtension = BlockSchemaExtension( + EmbedGithubBlockSchema +); diff --git a/blocksuite/affine/model/src/blocks/embed/html/html-schema.ts b/blocksuite/affine/model/src/blocks/embed/html/html-schema.ts index 6d55c74e5fe9c..31b229e841cb2 100644 --- a/blocksuite/affine/model/src/blocks/embed/html/html-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/html/html-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedHtmlBlockProps, @@ -18,3 +20,6 @@ export const EmbedHtmlBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedHtmlModel(), props: (): EmbedHtmlBlockProps => defaultEmbedHtmlProps, }); + +export const EmbedHtmlBlockSchemaExtension = + BlockSchemaExtension(EmbedHtmlBlockSchema); diff --git a/blocksuite/affine/model/src/blocks/embed/linked-doc/linked-doc-schema.ts b/blocksuite/affine/model/src/blocks/embed/linked-doc/linked-doc-schema.ts index f50002338202f..43055d0cc5afd 100644 --- a/blocksuite/affine/model/src/blocks/embed/linked-doc/linked-doc-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/linked-doc/linked-doc-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedLinkedDocBlockProps, @@ -20,3 +22,7 @@ export const EmbedLinkedDocBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedLinkedDocModel(), props: (): EmbedLinkedDocBlockProps => defaultEmbedLinkedDocBlockProps, }); + +export const EmbedLinkedDocBlockSchemaExtension = BlockSchemaExtension( + EmbedLinkedDocBlockSchema +); diff --git a/blocksuite/affine/model/src/blocks/embed/loom/loom-schema.ts b/blocksuite/affine/model/src/blocks/embed/loom/loom-schema.ts index 9035d915e7b61..32e4514b2053c 100644 --- a/blocksuite/affine/model/src/blocks/embed/loom/loom-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/loom/loom-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedLoomBlockProps, @@ -22,3 +24,6 @@ export const EmbedLoomBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedLoomModel(), props: (): EmbedLoomBlockProps => defaultEmbedLoomProps, }); + +export const EmbedLoomBlockSchemaExtension = + BlockSchemaExtension(EmbedLoomBlockSchema); diff --git a/blocksuite/affine/model/src/blocks/embed/synced-doc/synced-doc-schema.ts b/blocksuite/affine/model/src/blocks/embed/synced-doc/synced-doc-schema.ts index 135603e1555d6..d3235845a9f7c 100644 --- a/blocksuite/affine/model/src/blocks/embed/synced-doc/synced-doc-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/synced-doc/synced-doc-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedSyncedDocBlockProps, @@ -21,3 +23,7 @@ export const EmbedSyncedDocBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedSyncedDocModel(), props: (): EmbedSyncedDocBlockProps => defaultEmbedSyncedDocBlockProps, }); + +export const EmbedSyncedDocBlockSchemaExtension = BlockSchemaExtension( + EmbedSyncedDocBlockSchema +); diff --git a/blocksuite/affine/model/src/blocks/embed/youtube/youtube-schema.ts b/blocksuite/affine/model/src/blocks/embed/youtube/youtube-schema.ts index a26dbdcc467cc..d6ac7584cc6d7 100644 --- a/blocksuite/affine/model/src/blocks/embed/youtube/youtube-schema.ts +++ b/blocksuite/affine/model/src/blocks/embed/youtube/youtube-schema.ts @@ -1,3 +1,5 @@ +import { BlockSchemaExtension } from '@blocksuite/store'; + import { createEmbedBlockSchema } from '../../../utils/index.js'; import { type EmbedYoutubeBlockProps, @@ -25,3 +27,7 @@ export const EmbedYoutubeBlockSchema = createEmbedBlockSchema({ toModel: () => new EmbedYoutubeModel(), props: (): EmbedYoutubeBlockProps => defaultEmbedYoutubeProps, }); + +export const EmbedYoutubeBlockSchemaExtension = BlockSchemaExtension( + EmbedYoutubeBlockSchema +); diff --git a/blocksuite/affine/model/src/blocks/frame/frame-model.ts b/blocksuite/affine/model/src/blocks/frame/frame-model.ts index 6dd9a003cc0fa..1670d48d8e9f8 100644 --- a/blocksuite/affine/model/src/blocks/frame/frame-model.ts +++ b/blocksuite/affine/model/src/blocks/frame/frame-model.ts @@ -15,7 +15,12 @@ import { hasDescendantElementImpl, } from '@blocksuite/block-std/gfx'; import { Bound } from '@blocksuite/global/utils'; -import { BlockModel, defineBlockSchema, type Text } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, + type Text, +} from '@blocksuite/store'; import { z } from 'zod'; import { type Color, ColorSchema, DefaultTheme } from '../../themes/index.js'; @@ -57,6 +62,8 @@ export const FrameBlockSchema = defineBlockSchema({ }, }); +export const FrameBlockSchemaExtension = BlockSchemaExtension(FrameBlockSchema); + export class FrameBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry, GfxGroupCompatibleInterface diff --git a/blocksuite/affine/model/src/blocks/image/image-model.ts b/blocksuite/affine/model/src/blocks/image/image-model.ts index 9856d72dd2d36..2cf85f6fbff08 100644 --- a/blocksuite/affine/model/src/blocks/image/image-model.ts +++ b/blocksuite/affine/model/src/blocks/image/image-model.ts @@ -3,7 +3,11 @@ import type { GfxElementGeometry, } from '@blocksuite/block-std/gfx'; import { GfxCompatible } from '@blocksuite/block-std/gfx'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import { ImageBlockTransformer } from './image-transformer.js'; @@ -40,6 +44,8 @@ export const ImageBlockSchema = defineBlockSchema({ toModel: () => new ImageBlockModel(), }); +export const ImageBlockSchemaExtension = BlockSchemaExtension(ImageBlockSchema); + export class ImageBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry {} diff --git a/blocksuite/affine/model/src/blocks/latex/latex-model.ts b/blocksuite/affine/model/src/blocks/latex/latex-model.ts index 7081fca61a10d..ca5dc1226aedd 100644 --- a/blocksuite/affine/model/src/blocks/latex/latex-model.ts +++ b/blocksuite/affine/model/src/blocks/latex/latex-model.ts @@ -3,7 +3,11 @@ import { GfxCompatible, type GfxElementGeometry, } from '@blocksuite/block-std/gfx'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export type LatexProps = { latex: string; @@ -34,6 +38,8 @@ export const LatexBlockSchema = defineBlockSchema({ }, }); +export const LatexBlockSchemaExtension = BlockSchemaExtension(LatexBlockSchema); + export class LatexBlockModel extends GfxCompatible(BlockModel) implements GfxElementGeometry {} diff --git a/blocksuite/affine/model/src/blocks/list/list-model.ts b/blocksuite/affine/model/src/blocks/list/list-model.ts index 2ea461e029d95..af9be6c20544b 100644 --- a/blocksuite/affine/model/src/blocks/list/list-model.ts +++ b/blocksuite/affine/model/src/blocks/list/list-model.ts @@ -1,5 +1,9 @@ import type { Text } from '@blocksuite/store'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; // `toggle` type has been deprecated, do not use it export type ListType = 'bulleted' | 'numbered' | 'todo' | 'toggle'; @@ -38,6 +42,8 @@ export const ListBlockSchema = defineBlockSchema({ toModel: () => new ListBlockModel(), }); +export const ListBlockSchemaExtension = BlockSchemaExtension(ListBlockSchema); + export class ListBlockModel extends BlockModel { override text!: Text; } diff --git a/blocksuite/affine/model/src/blocks/note/note-model.ts b/blocksuite/affine/model/src/blocks/note/note-model.ts index cf17e61addbe7..9f8ee66c2b1a6 100644 --- a/blocksuite/affine/model/src/blocks/note/note-model.ts +++ b/blocksuite/affine/model/src/blocks/note/note-model.ts @@ -4,7 +4,11 @@ import type { } from '@blocksuite/block-std/gfx'; import { GfxCompatible } from '@blocksuite/block-std/gfx'; import { Bound } from '@blocksuite/global/utils'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; import { z } from 'zod'; import { @@ -21,6 +25,7 @@ import { StrokeStyleSchema, } from '../../consts/note'; import { type Color, ColorSchema, DefaultTheme } from '../../themes'; +import { TableModelFlavour } from '../table'; export const NoteZodSchema = z .object({ @@ -47,7 +52,6 @@ export const NoteZodSchema = z }, }, }); -import { TableModelFlavour } from '../table'; export const NoteBlockSchema = defineBlockSchema({ flavour: 'affine:note', @@ -92,6 +96,7 @@ export const NoteBlockSchema = defineBlockSchema({ }, }); +export const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema); export type NoteProps = { background: Color; displayMode: NoteDisplayMode; diff --git a/blocksuite/affine/model/src/blocks/paragraph/paragraph-model.ts b/blocksuite/affine/model/src/blocks/paragraph/paragraph-model.ts index a51e51c569e0c..40f94522c8d4e 100644 --- a/blocksuite/affine/model/src/blocks/paragraph/paragraph-model.ts +++ b/blocksuite/affine/model/src/blocks/paragraph/paragraph-model.ts @@ -1,4 +1,9 @@ -import { BlockModel, defineBlockSchema, type Text } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, + type Text, +} from '@blocksuite/store'; export type ParagraphType = | 'text' @@ -37,6 +42,9 @@ export const ParagraphBlockSchema = defineBlockSchema({ toModel: () => new ParagraphBlockModel(), }); +export const ParagraphBlockSchemaExtension = + BlockSchemaExtension(ParagraphBlockSchema); + export class ParagraphBlockModel extends BlockModel { override text!: Text; diff --git a/blocksuite/affine/model/src/blocks/root/root-block-model.ts b/blocksuite/affine/model/src/blocks/root/root-block-model.ts index afb22d5f42e87..854aa82947b17 100644 --- a/blocksuite/affine/model/src/blocks/root/root-block-model.ts +++ b/blocksuite/affine/model/src/blocks/root/root-block-model.ts @@ -1,5 +1,9 @@ import type { Text } from '@blocksuite/store'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export type RootBlockProps = { title: Text; @@ -51,3 +55,5 @@ export const RootBlockSchema = defineBlockSchema({ }, toModel: () => new RootBlockModel(), }); + +export const RootBlockSchemaExtension = BlockSchemaExtension(RootBlockSchema); diff --git a/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts b/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts index f6a7306e489dd..e604645bd4266 100644 --- a/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts +++ b/blocksuite/affine/model/src/blocks/surface-ref/surface-ref-model.ts @@ -1,4 +1,8 @@ -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export type SurfaceRefProps = { reference: string; @@ -21,4 +25,8 @@ export const SurfaceRefBlockSchema = defineBlockSchema({ toModel: () => new SurfaceRefBlockModel(), }); +export const SurfaceRefBlockSchemaExtension = BlockSchemaExtension( + SurfaceRefBlockSchema +); + export class SurfaceRefBlockModel extends BlockModel {} diff --git a/blocksuite/affine/model/src/blocks/table/table-model.ts b/blocksuite/affine/model/src/blocks/table/table-model.ts index a8e5277147f42..6d1d0c45c0882 100644 --- a/blocksuite/affine/model/src/blocks/table/table-model.ts +++ b/blocksuite/affine/model/src/blocks/table/table-model.ts @@ -1,6 +1,10 @@ import type { DeltaInsert } from '@blocksuite/inline'; import type { Text } from '@blocksuite/store'; -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export type TableCell = { text: Text; @@ -56,3 +60,5 @@ export const TableBlockSchema = defineBlockSchema({ }, toModel: () => new TableBlockModel(), }); + +export const TableBlockSchemaExtension = BlockSchemaExtension(TableBlockSchema); diff --git a/blocksuite/blocks/src/__tests__/adapters/notion-text.unit.spec.ts b/blocksuite/blocks/src/__tests__/adapters/notion-text.unit.spec.ts index dd9a531746c51..17bf524166105 100644 --- a/blocksuite/blocks/src/__tests__/adapters/notion-text.unit.spec.ts +++ b/blocksuite/blocks/src/__tests__/adapters/notion-text.unit.spec.ts @@ -4,8 +4,11 @@ import type { SliceSnapshot } from '@blocksuite/store'; import { describe, expect, test } from 'vitest'; import { createJob } from '../utils/create-job.js'; +import { getProvider } from '../utils/get-provider.js'; import { nanoidReplacement } from '../utils/nanoid-replacement.js'; +getProvider(); + describe('notion-text to snapshot', () => { test('basic', () => { const notionText = diff --git a/blocksuite/blocks/src/__tests__/database/database.unit.spec.ts b/blocksuite/blocks/src/__tests__/database/database.unit.spec.ts index c16d507b6b487..96cd19f93e31c 100644 --- a/blocksuite/blocks/src/__tests__/database/database.unit.spec.ts +++ b/blocksuite/blocks/src/__tests__/database/database.unit.spec.ts @@ -11,39 +11,37 @@ import { type Cell, type Column, type DatabaseBlockModel, - DatabaseBlockSchema, - NoteBlockSchema, - ParagraphBlockSchema, - RootBlockSchema, + DatabaseBlockSchemaExtension, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, } from '@blocksuite/affine-model'; import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets'; import type { BlockModel, Store } from '@blocksuite/store'; -import { Schema, Text } from '@blocksuite/store'; +import { Text } from '@blocksuite/store'; import { createAutoIncrementIdGenerator, TestWorkspace, } from '@blocksuite/store/test'; import { beforeEach, describe, expect, test } from 'vitest'; -const AffineSchemas = [ - RootBlockSchema, - NoteBlockSchema, - ParagraphBlockSchema, - DatabaseBlockSchema, +const extensions = [ + RootBlockSchemaExtension, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + DatabaseBlockSchemaExtension, ]; function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register(AffineSchemas); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } function createTestDoc(docId = 'doc0') { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: docId }); + const doc = collection.createDoc({ id: docId, extensions }); doc.load(); return doc; } diff --git a/blocksuite/blocks/src/__tests__/utils/create-job.ts b/blocksuite/blocks/src/__tests__/utils/create-job.ts index 6586647d3a56c..a9a274781f741 100644 --- a/blocksuite/blocks/src/__tests__/utils/create-job.ts +++ b/blocksuite/blocks/src/__tests__/utils/create-job.ts @@ -1,5 +1,5 @@ import { defaultImageProxyMiddleware } from '@blocksuite/affine-block-image'; -import { FeatureFlagService } from '@blocksuite/affine-shared/services'; +import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { Schema, Transformer, @@ -26,8 +26,8 @@ export function createJob(middlewares?: TransformerMiddleware[]) { const testMiddlewares = middlewares ?? []; testMiddlewares.push(defaultImageProxyMiddleware); const schema = new Schema().register(AffineSchemas); - const docCollection = new TestWorkspace({ schema }); - docCollection.storeExtensions = [FeatureFlagService]; + const docCollection = new TestWorkspace(); + docCollection.storeExtensions = SpecProvider._.getSpec('store').value; docCollection.meta.initialize(); return new Transformer({ schema, diff --git a/blocksuite/blocks/src/extensions/common.ts b/blocksuite/blocks/src/extensions/common.ts index 50d5caea88cf0..b2ea9607de14a 100644 --- a/blocksuite/blocks/src/extensions/common.ts +++ b/blocksuite/blocks/src/extensions/common.ts @@ -2,15 +2,12 @@ import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment'; import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark'; import { CodeBlockSpec } from '@blocksuite/affine-block-code'; import { DataViewBlockSpec } from '@blocksuite/affine-block-data-view'; -import { - DatabaseBlockSpec, - DatabaseSelectionExtension, -} from '@blocksuite/affine-block-database'; +import { DatabaseBlockSpec } from '@blocksuite/affine-block-database'; import { DividerBlockSpec } from '@blocksuite/affine-block-divider'; import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text'; import { EmbedExtensions } from '@blocksuite/affine-block-embed'; import { FrameBlockSpec } from '@blocksuite/affine-block-frame'; -import { ImageBlockSpec, ImageStoreSpec } from '@blocksuite/affine-block-image'; +import { ImageBlockSpec } from '@blocksuite/affine-block-image'; import { LatexBlockSpec } from '@blocksuite/affine-block-latex'; import { ListBlockSpec } from '@blocksuite/affine-block-list'; import { @@ -26,43 +23,19 @@ import { EdgelessSurfaceRefBlockSpec, PageSurfaceRefBlockSpec, } from '@blocksuite/affine-block-surface-ref'; -import { - TableBlockSpec, - TableSelectionExtension, -} from '@blocksuite/affine-block-table'; +import { TableBlockSpec } from '@blocksuite/affine-block-table'; import { RefNodeSlotsExtension, RichTextExtensions, } from '@blocksuite/affine-components/rich-text'; -import { - HighlightSelectionExtension, - ImageSelectionExtension, -} from '@blocksuite/affine-shared/selection'; import { DefaultOpenDocExtension, DocDisplayMetaService, EditPropsStore, - FeatureFlagService, - FileSizeLimitService, FontLoaderService, - LinkPreviewerService, } from '@blocksuite/affine-shared/services'; -import { - BlockSelectionExtension, - CursorSelectionExtension, - SurfaceSelectionExtension, - TextSelectionExtension, -} from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; -import { - AdapterFactoryExtensions, - HtmlAdapterExtension, - MarkdownAdapterExtension, - NotionHtmlAdapterExtension, - PlainTextAdapterExtension, -} from '../adapters/extension.js'; - export const CommonBlockSpecs: ExtensionType[] = [ DocDisplayMetaService, RefNodeSlotsExtension, @@ -82,7 +55,6 @@ export const CommonBlockSpecs: ExtensionType[] = [ ParagraphBlockSpec, DefaultOpenDocExtension, FontLoaderService, - AdapterFactoryExtensions, ].flat(); export const PageFirstPartyBlockSpecs: ExtensionType[] = [ @@ -101,24 +73,3 @@ export const EdgelessFirstPartyBlockSpecs: ExtensionType[] = [ FrameBlockSpec, EdgelessTextBlockSpec, ].flat(); - -export const StoreExtensions: ExtensionType[] = [ - BlockSelectionExtension, - TextSelectionExtension, - SurfaceSelectionExtension, - CursorSelectionExtension, - HighlightSelectionExtension, - ImageSelectionExtension, - DatabaseSelectionExtension, - TableSelectionExtension, - - FeatureFlagService, - LinkPreviewerService, - FileSizeLimitService, - ImageStoreSpec, - - HtmlAdapterExtension, - MarkdownAdapterExtension, - NotionHtmlAdapterExtension, - PlainTextAdapterExtension, -].flat(); diff --git a/blocksuite/blocks/src/extensions/index.ts b/blocksuite/blocks/src/extensions/index.ts index 4e1f407c37913..5c2111a0de6eb 100644 --- a/blocksuite/blocks/src/extensions/index.ts +++ b/blocksuite/blocks/src/extensions/index.ts @@ -1,3 +1,4 @@ export * from './common.js'; export * from './editor-specs.js'; export * from './preview-specs.js'; +export * from './store.js'; diff --git a/blocksuite/blocks/src/extensions/register.ts b/blocksuite/blocks/src/extensions/register.ts index 0fba57c9a39cf..fe8c14a91f9f1 100644 --- a/blocksuite/blocks/src/extensions/register.ts +++ b/blocksuite/blocks/src/extensions/register.ts @@ -1,6 +1,5 @@ import { SpecProvider } from '@blocksuite/affine-shared/utils'; -import { StoreExtensions } from './common.js'; import { EdgelessEditorBlockSpecs, PageEditorBlockSpecs, @@ -9,6 +8,7 @@ import { PreviewEdgelessEditorBlockSpecs, PreviewPageEditorBlockSpecs, } from './preview-specs.js'; +import { StoreExtensions } from './store.js'; export function registerSpecs() { SpecProvider._.addSpec('store', StoreExtensions); diff --git a/blocksuite/blocks/src/extensions/store.ts b/blocksuite/blocks/src/extensions/store.ts new file mode 100644 index 0000000000000..e59780dd6ce18 --- /dev/null +++ b/blocksuite/blocks/src/extensions/store.ts @@ -0,0 +1,101 @@ +import { DataViewBlockSchemaExtension } from '@blocksuite/affine-block-data-view'; +import { DatabaseSelectionExtension } from '@blocksuite/affine-block-database'; +import { ImageStoreSpec } from '@blocksuite/affine-block-image'; +import { SurfaceBlockSchemaExtension } from '@blocksuite/affine-block-surface'; +import { TableSelectionExtension } from '@blocksuite/affine-block-table'; +import { + AttachmentBlockSchemaExtension, + BookmarkBlockSchemaExtension, + CodeBlockSchemaExtension, + DatabaseBlockSchemaExtension, + DividerBlockSchemaExtension, + EdgelessTextBlockSchemaExtension, + EmbedFigmaBlockSchemaExtension, + EmbedGithubBlockSchemaExtension, + EmbedHtmlBlockSchemaExtension, + EmbedLinkedDocBlockSchemaExtension, + EmbedLoomBlockSchemaExtension, + EmbedSyncedDocBlockSchemaExtension, + EmbedYoutubeBlockSchemaExtension, + FrameBlockSchemaExtension, + ImageBlockSchemaExtension, + LatexBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, + SurfaceRefBlockSchemaExtension, + TableBlockSchemaExtension, +} from '@blocksuite/affine-model'; +import { + HighlightSelectionExtension, + ImageSelectionExtension, +} from '@blocksuite/affine-shared/selection'; +import { + FeatureFlagService, + FileSizeLimitService, + LinkPreviewerService, +} from '@blocksuite/affine-shared/services'; +import { + BlockSelectionExtension, + CursorSelectionExtension, + SurfaceSelectionExtension, + TextSelectionExtension, +} from '@blocksuite/block-std'; +import type { ExtensionType } from '@blocksuite/store'; + +import { + AdapterFactoryExtensions, + HtmlAdapterExtension, + MarkdownAdapterExtension, + NotionHtmlAdapterExtension, + PlainTextAdapterExtension, +} from '../adapters/extension.js'; + +export const StoreExtensions: ExtensionType[] = [ + CodeBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + DividerBlockSchemaExtension, + ImageBlockSchemaExtension, + SurfaceBlockSchemaExtension, + BookmarkBlockSchemaExtension, + FrameBlockSchemaExtension, + DatabaseBlockSchemaExtension, + SurfaceRefBlockSchemaExtension, + DataViewBlockSchemaExtension, + AttachmentBlockSchemaExtension, + EmbedSyncedDocBlockSchemaExtension, + EmbedLinkedDocBlockSchemaExtension, + EmbedHtmlBlockSchemaExtension, + EmbedGithubBlockSchemaExtension, + EmbedFigmaBlockSchemaExtension, + EmbedLoomBlockSchemaExtension, + EmbedYoutubeBlockSchemaExtension, + EdgelessTextBlockSchemaExtension, + LatexBlockSchemaExtension, + TableBlockSchemaExtension, + + BlockSelectionExtension, + TextSelectionExtension, + SurfaceSelectionExtension, + CursorSelectionExtension, + HighlightSelectionExtension, + ImageSelectionExtension, + DatabaseSelectionExtension, + TableSelectionExtension, + + FeatureFlagService, + LinkPreviewerService, + FileSizeLimitService, + ImageStoreSpec, + + HtmlAdapterExtension, + MarkdownAdapterExtension, + NotionHtmlAdapterExtension, + PlainTextAdapterExtension, + + AdapterFactoryExtensions, +].flat(); diff --git a/blocksuite/framework/block-std/src/__tests__/editor-host.unit.spec.ts b/blocksuite/framework/block-std/src/__tests__/editor-host.unit.spec.ts index 1af2ab846fcc4..389ae10e28067 100644 --- a/blocksuite/framework/block-std/src/__tests__/editor-host.unit.spec.ts +++ b/blocksuite/framework/block-std/src/__tests__/editor-host.unit.spec.ts @@ -1,4 +1,3 @@ -import { Schema } from '@blocksuite/store'; import { createAutoIncrementIdGenerator, TestWorkspace, @@ -9,19 +8,23 @@ import { effects } from '../effects.js'; import { TestEditorContainer } from './test-editor.js'; import { type HeadingBlockModel, - HeadingBlockSchema, - NoteBlockSchema, - RootBlockSchema, + HeadingBlockSchemaExtension, + NoteBlockSchemaExtension, + RootBlockSchemaExtension, } from './test-schema.js'; import { testSpecs } from './test-spec.js'; effects(); +const extensions = [ + RootBlockSchemaExtension, + NoteBlockSchemaExtension, + HeadingBlockSchemaExtension, +]; + function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register([RootBlockSchema, NoteBlockSchema, HeadingBlockSchema]); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } function wait(time: number) { @@ -33,7 +36,7 @@ describe('editor host', () => { const collection = new TestWorkspace(createTestOptions()); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); const rootId = doc.addBlock('test:page'); const noteId = doc.addBlock('test:note', {}, rootId); diff --git a/blocksuite/framework/block-std/src/__tests__/test-schema.ts b/blocksuite/framework/block-std/src/__tests__/test-schema.ts index 39f7446d18039..5bd854c31fa17 100644 --- a/blocksuite/framework/block-std/src/__tests__/test-schema.ts +++ b/blocksuite/framework/block-std/src/__tests__/test-schema.ts @@ -1,4 +1,8 @@ -import { BlockModel, defineBlockSchema } from '@blocksuite/store'; +import { + BlockModel, + BlockSchemaExtension, + defineBlockSchema, +} from '@blocksuite/store'; export const RootBlockSchema = defineBlockSchema({ flavour: 'test:page', @@ -15,6 +19,8 @@ export const RootBlockSchema = defineBlockSchema({ }, }); +export const RootBlockSchemaExtension = BlockSchemaExtension(RootBlockSchema); + export class RootBlockModel extends BlockModel< ReturnType<(typeof RootBlockSchema)['model']['props']> > {} @@ -30,6 +36,8 @@ export const NoteBlockSchema = defineBlockSchema({ }, }); +export const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema); + export class NoteBlockModel extends BlockModel< ReturnType<(typeof NoteBlockSchema)['model']['props']> > {} @@ -47,6 +55,9 @@ export const HeadingBlockSchema = defineBlockSchema({ }, }); +export const HeadingBlockSchemaExtension = + BlockSchemaExtension(HeadingBlockSchema); + export class HeadingBlockModel extends BlockModel< ReturnType<(typeof HeadingBlockSchema)['model']['props']> > {} diff --git a/blocksuite/framework/block-std/src/scope/block-std-scope.ts b/blocksuite/framework/block-std/src/scope/block-std-scope.ts index bf228e9928ee8..72173430726cd 100644 --- a/blocksuite/framework/block-std/src/scope/block-std-scope.ts +++ b/blocksuite/framework/block-std/src/scope/block-std-scope.ts @@ -142,7 +142,7 @@ export class BlockStdScope { getTransformer(middlewares: TransformerMiddleware[] = []) { return new Transformer({ - schema: this.workspace.schema, + schema: this.store.schema, blobCRUD: this.workspace.blobSync, docCRUD: { create: (id: string) => this.workspace.createDoc({ id }), diff --git a/blocksuite/framework/store/src/__tests__/block.unit.spec.ts b/blocksuite/framework/store/src/__tests__/block.unit.spec.ts index 7c2bc5a4c0530..286acfacbd3c8 100644 --- a/blocksuite/framework/store/src/__tests__/block.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/block.unit.spec.ts @@ -2,6 +2,7 @@ import { computed, effect } from '@preact/signals-core'; import { describe, expect, test, vi } from 'vitest'; import * as Y from 'yjs'; +import { BlockSchemaExtension } from '../extension/schema.js'; import { Block, BlockModel, @@ -9,7 +10,6 @@ import { internalPrimitives, } from '../model/block/index.js'; import type { YBlock } from '../model/block/types.js'; -import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; @@ -27,6 +27,7 @@ const pageSchema = defineBlockSchema({ version: 1, }, }); +const pageSchemaExtension = BlockSchemaExtension(pageSchema); const tableSchema = defineBlockSchema({ flavour: 'table', @@ -39,6 +40,7 @@ const tableSchema = defineBlockSchema({ version: 1, }, }); +const tableSchemaExtension = BlockSchemaExtension(tableSchema); const flatTableSchema = defineBlockSchema({ flavour: 'flat-table', @@ -54,6 +56,8 @@ const flatTableSchema = defineBlockSchema({ isFlatData: true, }, }); +const flatTableSchemaExtension = BlockSchemaExtension(flatTableSchema); + class RootModel extends BlockModel< ReturnType<(typeof pageSchema)['model']['props']> > {} @@ -66,9 +70,7 @@ class FlatTableModel extends BlockModel< function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register([pageSchema, tableSchema, flatTableSchema]); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } const defaultDocId = 'doc:home'; @@ -76,7 +78,14 @@ function createTestDoc(docId = defaultDocId) { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: docId }); + const doc = collection.createDoc({ + id: docId, + extensions: [ + pageSchemaExtension, + tableSchemaExtension, + flatTableSchemaExtension, + ], + }); doc.load(); return doc; } diff --git a/blocksuite/framework/store/src/__tests__/collection.unit.spec.ts b/blocksuite/framework/store/src/__tests__/collection.unit.spec.ts index 41da9562743b2..138fdd70ce26a 100644 --- a/blocksuite/framework/store/src/__tests__/collection.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/collection.unit.spec.ts @@ -4,29 +4,20 @@ import type { Slot } from '@blocksuite/global/utils'; import { assert, beforeEach, describe, expect, it, vi } from 'vitest'; import { applyUpdate, type Doc, encodeStateAsUpdate } from 'yjs'; -import type { BlockModel, BlockSchemaType, DocMeta, Store } from '../index.js'; -import { Schema } from '../index.js'; +import type { BlockModel, DocMeta, Store } from '../index.js'; import { Text } from '../reactive/text.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; import { - NoteBlockSchema, - ParagraphBlockSchema, - RootBlockSchema, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, } from './test-schema.js'; import { assertExists } from './test-utils-dom.js'; -export const BlockSchemas = [ - ParagraphBlockSchema, - RootBlockSchema, - NoteBlockSchema, -] as BlockSchemaType[]; - function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register(BlockSchemas); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } const defaultDocId = 'doc:home'; @@ -58,11 +49,20 @@ function createRoot(doc: Store) { return doc.root; } +const extensions = [ + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, +]; + function createTestDoc(docId = defaultDocId) { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: docId }); + const doc = collection.createDoc({ + id: docId, + extensions, + }); doc.load(); return doc; } @@ -113,13 +113,6 @@ describe('basic', () => { tags: [], }, ], - workspaceVersion: 2, - pageVersion: 2, - blockVersions: { - 'affine:note': 1, - 'affine:page': 2, - 'affine:paragraph': 1, - }, }, spaces: { [spaceId]: { @@ -155,6 +148,7 @@ describe('basic', () => { collection.meta.initialize(); const doc = collection.createDoc({ id: 'space:0', + extensions, }); const readyCallback = vi.fn(); @@ -181,6 +175,7 @@ describe('basic', () => { const collection2 = new TestWorkspace(options); const doc = collection.createDoc({ id: 'space:0', + extensions, }); doc.load(() => { doc.addBlock('affine:page', { @@ -209,7 +204,9 @@ describe('basic', () => { // apply doc update const update = encodeStateAsUpdate(doc.spaceDoc); expect(collection2.docs.size).toBe(1); - const doc2 = collection2.getDoc('space:0'); + const doc2 = collection2.getDoc('space:0', { + extensions, + }); assertExists(doc2); applyUpdate(doc2.spaceDoc, update); expect(serializCollection(collection2.doc)['spaces']).toEqual({ diff --git a/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts b/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts index ef9a1e5e255a8..cda8c78d95999 100644 --- a/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts @@ -2,31 +2,28 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import * as Y from 'yjs'; import type { BlockModel, Store } from '../model/index.js'; -import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; import { - DividerBlockSchema, - ListBlockSchema, - NoteBlockSchema, - ParagraphBlockSchema, + DividerBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, type RootBlockModel, - RootBlockSchema, + RootBlockSchemaExtension, } from './test-schema.js'; -const BlockSchemas = [ - RootBlockSchema, - ParagraphBlockSchema, - ListBlockSchema, - NoteBlockSchema, - DividerBlockSchema, +const extensions = [ + RootBlockSchemaExtension, + ParagraphBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + DividerBlockSchemaExtension, ]; function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register(BlockSchemas); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } test('trigger props updated', () => { @@ -34,7 +31,7 @@ test('trigger props updated', () => { const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); doc.addBlock('affine:page'); @@ -94,7 +91,7 @@ test('stash and pop', () => { const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); doc.addBlock('affine:page'); @@ -164,7 +161,7 @@ test('always get latest value in onChange', () => { const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); doc.addBlock('affine:page'); @@ -210,11 +207,12 @@ test('query', () => { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc1 = collection.createDoc({ id: 'home' }); + const doc1 = collection.createDoc({ id: 'home', extensions }); doc1.load(); - const doc2 = collection.getDoc('home'); + const doc2 = collection.getDoc('home', { extensions }); const doc3 = collection.getDoc('home', { + extensions, query: { mode: 'loose', match: [ @@ -247,10 +245,10 @@ test('local readonly', () => { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc1 = collection.createDoc({ id: 'home' }); + const doc1 = collection.createDoc({ id: 'home', extensions }); doc1.load(); - const doc2 = collection.getDoc('home', { readonly: true }); - const doc3 = collection.getDoc('home', { readonly: false }); + const doc2 = collection.getDoc('home', { readonly: true, extensions }); + const doc3 = collection.getDoc('home', { readonly: false, extensions }); expect(doc1.readonly).toBeFalsy(); expect(doc2?.readonly).toBeTruthy(); @@ -276,7 +274,7 @@ describe('move blocks', () => { const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); const pageId = doc.addBlock('affine:page'); const page = doc.getBlock(pageId)!.model; diff --git a/blocksuite/framework/store/src/__tests__/schema.unit.spec.ts b/blocksuite/framework/store/src/__tests__/schema.unit.spec.ts index 4343f328f0278..7828e6e24560f 100644 --- a/blocksuite/framework/store/src/__tests__/schema.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/schema.unit.spec.ts @@ -1,25 +1,23 @@ import { literal } from 'lit/static-html.js'; import { describe, expect, it, vi } from 'vitest'; +import { BlockSchemaExtension } from '../extension/schema.js'; import { defineBlockSchema } from '../model/block/zod.js'; // import some blocks import { SchemaValidateError } from '../schema/error.js'; -import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; import { - DividerBlockSchema, - ListBlockSchema, - NoteBlockSchema, - ParagraphBlockSchema, - RootBlockSchema, + DividerBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + ParagraphBlockSchemaExtension, + RootBlockSchemaExtension, } from './test-schema.js'; function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register(BlockSchemas); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } const TestCustomNoteBlockSchema = defineBlockSchema({ @@ -35,6 +33,10 @@ const TestCustomNoteBlockSchema = defineBlockSchema({ }, }); +const TestCustomNoteBlockSchemaExtension = BlockSchemaExtension( + TestCustomNoteBlockSchema +); + const TestInvalidNoteBlockSchema = defineBlockSchema({ flavour: 'affine:note-invalid-block-video', props: internal => ({ @@ -48,14 +50,18 @@ const TestInvalidNoteBlockSchema = defineBlockSchema({ }, }); -const BlockSchemas = [ - RootBlockSchema, - ParagraphBlockSchema, - ListBlockSchema, - NoteBlockSchema, - DividerBlockSchema, - TestCustomNoteBlockSchema, - TestInvalidNoteBlockSchema, +const TestInvalidNoteBlockSchemaExtension = BlockSchemaExtension( + TestInvalidNoteBlockSchema +); + +const extensions = [ + RootBlockSchemaExtension, + ParagraphBlockSchemaExtension, + ListBlockSchemaExtension, + NoteBlockSchemaExtension, + DividerBlockSchemaExtension, + TestCustomNoteBlockSchemaExtension, + TestInvalidNoteBlockSchemaExtension, ]; const defaultDocId = 'doc0'; @@ -63,7 +69,7 @@ function createTestDoc(docId = defaultDocId) { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: docId }); + const doc = collection.createDoc({ id: docId, extensions }); doc.load(); return doc; } diff --git a/blocksuite/framework/store/src/__tests__/test-schema.ts b/blocksuite/framework/store/src/__tests__/test-schema.ts index 4d91ab8672f14..13e2bc78e59fc 100644 --- a/blocksuite/framework/store/src/__tests__/test-schema.ts +++ b/blocksuite/framework/store/src/__tests__/test-schema.ts @@ -1,3 +1,4 @@ +import { BlockSchemaExtension } from '../extension/schema.js'; import { BlockModel, defineBlockSchema } from '../model/index.js'; export const RootBlockSchema = defineBlockSchema({ @@ -14,6 +15,8 @@ export const RootBlockSchema = defineBlockSchema({ }, }); +export const RootBlockSchemaExtension = BlockSchemaExtension(RootBlockSchema); + export class RootBlockModel extends BlockModel< ReturnType<(typeof RootBlockSchema)['model']['props']> > {} @@ -42,6 +45,8 @@ export const NoteBlockSchema = defineBlockSchema({ }, }); +export const NoteBlockSchemaExtension = BlockSchemaExtension(NoteBlockSchema); + export const ParagraphBlockSchema = defineBlockSchema({ flavour: 'affine:paragraph', props: internal => ({ @@ -60,6 +65,9 @@ export const ParagraphBlockSchema = defineBlockSchema({ }, }); +export const ParagraphBlockSchemaExtension = + BlockSchemaExtension(ParagraphBlockSchema); + export const ListBlockSchema = defineBlockSchema({ flavour: 'affine:list', props: internal => ({ @@ -80,6 +88,8 @@ export const ListBlockSchema = defineBlockSchema({ }, }); +export const ListBlockSchemaExtension = BlockSchemaExtension(ListBlockSchema); + export const DividerBlockSchema = defineBlockSchema({ flavour: 'affine:divider', metadata: { @@ -88,3 +98,6 @@ export const DividerBlockSchema = defineBlockSchema({ children: [], }, }); + +export const DividerBlockSchemaExtension = + BlockSchemaExtension(DividerBlockSchema); diff --git a/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts b/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts index 3e98474ecf07d..2248cd660924e 100644 --- a/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/transformer.unit.spec.ts @@ -2,10 +2,10 @@ import { expect, test } from 'vitest'; import * as Y from 'yjs'; import { MemoryBlobCRUD } from '../adapter/index.js'; +import { BlockSchemaExtension } from '../extension/schema.js'; import { BlockModel } from '../model/block/block-model.js'; import { defineBlockSchema } from '../model/block/zod.js'; import { Text } from '../reactive/index.js'; -import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; import { AssetsManager, BaseBlockTransformer } from '../transformer/index.js'; @@ -39,15 +39,16 @@ const docSchema = defineBlockSchema({ }, }); +const docSchemaExtension = BlockSchemaExtension(docSchema); class RootBlockModel extends BlockModel< ReturnType<(typeof docSchema)['model']['props']> > {} +const extensions = [docSchemaExtension]; + function createTestOptions() { const idGenerator = createAutoIncrementIdGenerator(); - const schema = new Schema(); - schema.register([docSchema]); - return { id: 'test-collection', idGenerator, schema }; + return { id: 'test-collection', idGenerator }; } const transformer = new BaseBlockTransformer(new Map()); @@ -58,7 +59,7 @@ test('model to snapshot', () => { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); doc.addBlock('page'); const rootModel = doc.root as RootBlockModel; @@ -75,7 +76,7 @@ test('snapshot to model', async () => { const options = createTestOptions(); const collection = new TestWorkspace(options); collection.meta.initialize(); - const doc = collection.createDoc({ id: 'home' }); + const doc = collection.createDoc({ id: 'home', extensions }); doc.load(); doc.addBlock('page'); const rootModel = doc.root as RootBlockModel; diff --git a/blocksuite/framework/store/src/extension/index.ts b/blocksuite/framework/store/src/extension/index.ts index 554a044e137e5..49b142cd99a74 100644 --- a/blocksuite/framework/store/src/extension/index.ts +++ b/blocksuite/framework/store/src/extension/index.ts @@ -1,3 +1,4 @@ export * from './extension'; +export * from './schema'; export * from './selection'; export * from './store-extension'; diff --git a/blocksuite/framework/store/src/extension/schema.ts b/blocksuite/framework/store/src/extension/schema.ts new file mode 100644 index 0000000000000..50128874df863 --- /dev/null +++ b/blocksuite/framework/store/src/extension/schema.ts @@ -0,0 +1,20 @@ +import { createIdentifier } from '@blocksuite/global/di'; + +import type { BlockSchemaType } from '../model/block/zod'; +import type { ExtensionType } from './extension'; + +export const BlockSchemaIdentifier = + createIdentifier('BlockSchema'); + +export function BlockSchemaExtension( + blockSchema: BlockSchemaType +): ExtensionType { + return { + setup: di => { + di.addImpl( + BlockSchemaIdentifier(blockSchema.model.flavour), + () => blockSchema + ); + }, + }; +} diff --git a/blocksuite/framework/store/src/model/doc.ts b/blocksuite/framework/store/src/model/doc.ts index 382393fadf0e1..9eeb10d8ad1db 100644 --- a/blocksuite/framework/store/src/model/doc.ts +++ b/blocksuite/framework/store/src/model/doc.ts @@ -1,7 +1,6 @@ import type { Slot } from '@blocksuite/global/utils'; import type * as Y from 'yjs'; -import type { Schema } from '../schema/schema.js'; import type { AwarenessStore } from '../yjs/awareness.js'; import type { YBlock } from './block/types.js'; import type { Query } from './store/query.js'; @@ -18,7 +17,6 @@ export type YBlocks = Y.Map; export interface Doc { readonly id: string; get meta(): DocMeta | undefined; - get schema(): Schema; remove(): void; load(initFn?: () => void): void; diff --git a/blocksuite/framework/store/src/model/store/store.ts b/blocksuite/framework/store/src/model/store/store.ts index 6fe832e7e663b..683d422b12ad2 100644 --- a/blocksuite/framework/store/src/model/store/store.ts +++ b/blocksuite/framework/store/src/model/store/store.ts @@ -4,8 +4,11 @@ import { type Disposable, Slot } from '@blocksuite/global/utils'; import { computed, signal } from '@preact/signals-core'; import type { ExtensionType } from '../../extension/extension.js'; -import { StoreSelectionExtension } from '../../extension/index.js'; -import type { Schema } from '../../schema/index.js'; +import { + BlockSchemaIdentifier, + StoreSelectionExtension, +} from '../../extension/index.js'; +import { Schema } from '../../schema/index.js'; import { Block, type BlockModel, @@ -20,7 +23,6 @@ import { type Query, runQuery } from './query.js'; import { syncBlockProps } from './utils.js'; export type StoreOptions = { - schema: Schema; doc: Doc; id?: string; readonly?: boolean; @@ -298,14 +300,7 @@ export class Store { return this._doc.withoutTransact.bind(this._doc); } - constructor({ - schema, - doc, - readonly, - query, - provider, - extensions, - }: StoreOptions) { + constructor({ doc, readonly, query, provider, extensions }: StoreOptions) { const container = new Container(); container.addImpl(StoreIdentifier, () => this); @@ -331,8 +326,11 @@ export class Store { yBlockUpdated: this._doc.slots.yBlockUpdated, }; - this._crud = new DocCRUD(this._yBlocks, doc.schema); - this._schema = schema; + this._schema = new Schema(); + this._provider.getAll(BlockSchemaIdentifier).forEach(schema => { + this._schema.register([schema]); + }); + this._crud = new DocCRUD(this._yBlocks, this._schema); if (readonly !== undefined) { this._readonly.value = readonly; } diff --git a/blocksuite/framework/store/src/model/workspace-meta.ts b/blocksuite/framework/store/src/model/workspace-meta.ts index 028ef78dd6197..58eec4fc8630a 100644 --- a/blocksuite/framework/store/src/model/workspace-meta.ts +++ b/blocksuite/framework/store/src/model/workspace-meta.ts @@ -1,7 +1,5 @@ import type { Slot } from '@blocksuite/global/utils'; -import type { Workspace } from './workspace.js'; - export type Tag = { id: string; value: string; @@ -38,8 +36,6 @@ export interface WorkspaceMeta { get name(): string | undefined; setName(name: string): void; - hasVersion: boolean; - writeVersion(workspace: Workspace): void; get docs(): unknown[] | undefined; initialize(): void; diff --git a/blocksuite/framework/store/src/model/workspace.ts b/blocksuite/framework/store/src/model/workspace.ts index bf0a5b962e998..8a005ba96d6aa 100644 --- a/blocksuite/framework/store/src/model/workspace.ts +++ b/blocksuite/framework/store/src/model/workspace.ts @@ -3,7 +3,6 @@ import type { BlobEngine } from '@blocksuite/sync'; import type { Awareness } from 'y-protocols/awareness.js'; import type * as Y from 'yjs'; -import type { Schema } from '../schema/schema.js'; import type { IdGenerator } from '../utils/id-generator.js'; import type { AwarenessStore } from '../yjs/awareness.js'; import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js'; @@ -19,7 +18,6 @@ export interface Workspace { readonly onLoadDoc?: (doc: Y.Doc) => void; readonly onLoadAwareness?: (awareness: Awareness) => void; - get schema(): Schema; get doc(): Y.Doc; get docs(): Map; diff --git a/blocksuite/framework/store/src/test/test-doc.ts b/blocksuite/framework/store/src/test/test-doc.ts index fc169f0a06c96..9f2ef136aad4a 100644 --- a/blocksuite/framework/store/src/test/test-doc.ts +++ b/blocksuite/framework/store/src/test/test-doc.ts @@ -162,10 +162,6 @@ export class TestDoc implements Doc { return this._ready; } - get schema() { - return this.workspace.schema; - } - get spaceDoc() { return this._ySpaceDoc; } @@ -189,13 +185,6 @@ export class TestDoc implements Doc { return (readonly?.toString() as 'true' | 'false') ?? 'false'; } - private _handleVersion() { - // Initialization from empty yDoc, indicating that the document is new. - if (!this.workspace.meta.hasVersion) { - this.workspace.meta.writeVersion(this.workspace); - } - } - private _handleYBlockAdd(id: string) { this.slots.yBlockUpdated.emit({ type: 'add', id }); } @@ -306,7 +295,6 @@ export class TestDoc implements Doc { const doc = new Store({ doc: this, - schema: this.workspace.schema, readonly, query, provider, @@ -327,10 +315,6 @@ export class TestDoc implements Doc { this._ySpaceDoc.load(); - if ((this.workspace.meta.docs?.length ?? 0) <= 1) { - this._handleVersion(); - } - this._initYBlocks(); this._yBlocks.forEach((_, id) => { diff --git a/blocksuite/framework/store/src/test/test-meta.ts b/blocksuite/framework/store/src/test/test-meta.ts index 8ec3ee91c235a..6e916ce376bbe 100644 --- a/blocksuite/framework/store/src/test/test-meta.ts +++ b/blocksuite/framework/store/src/test/test-meta.ts @@ -4,20 +4,13 @@ import type * as Y from 'yjs'; import type { DocMeta, DocsPropertiesMeta, - Workspace, WorkspaceMeta, } from '../model/index.js'; import { createYProxy } from '../reactive/proxy.js'; -const COLLECTION_VERSION = 2; -const PAGE_VERSION = 2; - type DocCollectionMetaState = { pages?: unknown[]; properties?: DocsPropertiesMeta; - workspaceVersion?: number; - pageVersion?: number; - blockVersions?: Record; name?: string; avatar?: string; }; @@ -68,10 +61,6 @@ export class TestMeta implements WorkspaceMeta { return this._proxy.avatar; } - get blockVersions() { - return this._proxy.blockVersions; - } - get docMetas() { if (!this._proxy.pages) { return [] as DocMeta[]; @@ -83,21 +72,10 @@ export class TestMeta implements WorkspaceMeta { return this._proxy.pages; } - get hasVersion() { - if (!this.blockVersions || !this.pageVersion || !this.workspaceVersion) { - return false; - } - return Object.keys(this.blockVersions).length > 0; - } - get name() { return this._proxy.name; } - get pageVersion() { - return this._proxy.pageVersion; - } - get properties(): DocsPropertiesMeta { const meta = this._proxy.properties; if (!meta) { @@ -110,10 +88,6 @@ export class TestMeta implements WorkspaceMeta { return meta; } - get workspaceVersion() { - return this._proxy.workspaceVersion; - } - get yDocs() { return this._yMap.get('pages') as unknown as Y.Array; } @@ -232,33 +206,4 @@ export class TestMeta implements WorkspaceMeta { this._proxy.properties = meta; this.docMetaUpdated.emit(); } - - /** - * @internal Only for doc initialization - */ - writeVersion(collection: Workspace) { - const { blockVersions, pageVersion, workspaceVersion } = this._proxy; - - if (!workspaceVersion) { - this._proxy.workspaceVersion = COLLECTION_VERSION; - } else { - console.error('Workspace version is already set'); - } - - if (!pageVersion) { - this._proxy.pageVersion = PAGE_VERSION; - } else { - console.error('Doc version is already set'); - } - - if (!blockVersions) { - const _versions: Record = {}; - collection.schema.flavourSchemaMap.forEach((schema, flavour) => { - _versions[flavour] = schema.version; - }); - this._proxy.blockVersions = _versions; - } else { - console.error('Block versions is already set'); - } - } } diff --git a/blocksuite/framework/store/src/test/test-workspace.ts b/blocksuite/framework/store/src/test/test-workspace.ts index ef5a743fcf104..c2e3ec782c19b 100644 --- a/blocksuite/framework/store/src/test/test-workspace.ts +++ b/blocksuite/framework/store/src/test/test-workspace.ts @@ -21,14 +21,12 @@ import type { Workspace, WorkspaceMeta, } from '../model/index.js'; -import type { Schema } from '../schema/index.js'; import { type IdGenerator, nanoid } from '../utils/id-generator.js'; import { AwarenessStore } from '../yjs/index.js'; import { TestDoc } from './test-doc.js'; import { TestMeta } from './test-meta.js'; export type DocCollectionOptions = { - schema: Schema; id?: string; idGenerator?: IdGenerator; docSources?: { @@ -47,8 +45,6 @@ export type DocCollectionOptions = { * Do not use this in production */ export class TestWorkspace implements Workspace { - protected readonly _schema: Schema; - storeExtensions: ExtensionType[] = []; readonly awarenessStore: AwarenessStore; @@ -79,13 +75,8 @@ export class TestWorkspace implements Workspace { return this.blockCollections; } - get schema() { - return this._schema; - } - constructor({ id, - schema, idGenerator, awarenessSources = [], docSources = { @@ -94,9 +85,7 @@ export class TestWorkspace implements Workspace { blobSources = { main: new MemoryBlobSource(), }, - }: DocCollectionOptions) { - this._schema = schema; - + }: DocCollectionOptions = {}) { this.id = id || ''; this.doc = new Y.Doc({ guid: id }); this.awarenessStore = new AwarenessStore(new Awareness(this.doc)); @@ -165,7 +154,12 @@ export class TestWorkspace implements Workspace { * will be created in the doc simultaneously. */ createDoc(options: CreateBlocksOptions = {}) { - const { id: docId = this.idGenerator(), query, readonly } = options; + const { + id: docId = this.idGenerator(), + query, + readonly, + extensions, + } = options; if (this._hasDoc(docId)) { throw new BlockSuiteError( ErrorCode.DocCollectionError, @@ -184,6 +178,7 @@ export class TestWorkspace implements Workspace { id: docId, query, readonly, + extensions, }) as Store; } diff --git a/blocksuite/integration-test/src/__tests__/main/snapshot.spec.ts b/blocksuite/integration-test/src/__tests__/main/snapshot.spec.ts index da513bb789c1a..43b395f4cd922 100644 --- a/blocksuite/integration-test/src/__tests__/main/snapshot.spec.ts +++ b/blocksuite/integration-test/src/__tests__/main/snapshot.spec.ts @@ -1,5 +1,10 @@ -import { type SurfaceBlockModel, ZipTransformer } from '@blocksuite/blocks'; +import { + AffineSchemas, + type SurfaceBlockModel, + ZipTransformer, +} from '@blocksuite/blocks'; import type { PointLocation } from '@blocksuite/global/utils'; +import { Schema } from '@blocksuite/store'; import { beforeEach, expect, test } from 'vitest'; import { wait } from '../utils/common.js'; @@ -25,6 +30,8 @@ const skipFields = new Set(['_lastXYWH']); const snapshotTest = async (snapshotUrl: string, elementsCount: number) => { const transformer = ZipTransformer; + const schema = new Schema(); + schema.register(AffineSchemas); const snapshotFile = await fetch(snapshotUrl) .then(res => res.blob()) @@ -32,8 +39,10 @@ const snapshotTest = async (snapshotUrl: string, elementsCount: number) => { console.error(e); throw e; }); + const [newDoc] = await transformer.importDocs( window.editor.doc.workspace, + schema, snapshotFile ); diff --git a/blocksuite/integration-test/src/__tests__/utils/misc.ts b/blocksuite/integration-test/src/__tests__/utils/misc.ts index bf70a04efdc35..11c7fed403c14 100644 --- a/blocksuite/integration-test/src/__tests__/utils/misc.ts +++ b/blocksuite/integration-test/src/__tests__/utils/misc.ts @@ -1,6 +1,7 @@ -import { replaceIdMiddleware } from '@blocksuite/blocks'; +import { AffineSchemas, replaceIdMiddleware } from '@blocksuite/blocks'; import { type DocSnapshot, + Schema, Transformer, type Workspace, } from '@blocksuite/store'; @@ -10,7 +11,7 @@ export async function importFromSnapshot( snapshot: DocSnapshot ) { const job = new Transformer({ - schema: collection.schema, + schema: new Schema().register(AffineSchemas), blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/playground/apps/_common/components/starter-debug-menu.ts b/blocksuite/playground/apps/_common/components/starter-debug-menu.ts index 4e16c665705b8..f4d734d2ece35 100644 --- a/blocksuite/playground/apps/_common/components/starter-debug-menu.ts +++ b/blocksuite/playground/apps/_common/components/starter-debug-menu.ts @@ -238,7 +238,7 @@ export class StarterDebugMenu extends ShadowlessElement { private async _exportFile(config: AdapterConfig) { const doc = this.editor.doc; const job = new Transformer({ - schema: this.collection.schema, + schema: doc.schema, blobCRUD: this.collection.blobSync, docCRUD: { create: (id: string) => this.collection.createDoc({ id }), @@ -325,6 +325,7 @@ export class StarterDebugMenu extends ShadowlessElement { private async _exportSnapshot() { await ZipTransformer.exportDocs( this.collection, + this.editor.doc.schema, Array.from(this.collection.docs.values()).map(collection => collection.getStore() ) @@ -346,6 +347,7 @@ export class StarterDebugMenu extends ShadowlessElement { const fileName = file.name.split('.').slice(0, -1).join('.'); const pageId = await HtmlTransformer.importHTMLToDoc({ collection: this.collection, + schema: this.editor.doc.schema, html: text, fileName, }); @@ -369,6 +371,7 @@ export class StarterDebugMenu extends ShadowlessElement { if (!file) return; const result = await HtmlTransformer.importHTMLZip({ collection: this.collection, + schema: this.editor.doc.schema, imported: file, }); if (!this.editor.host) return; @@ -396,6 +399,7 @@ export class StarterDebugMenu extends ShadowlessElement { const fileName = file.name.split('.').slice(0, -1).join('.'); const pageId = await MarkdownTransformer.importMarkdownToDoc({ collection: this.collection, + schema: this.editor.doc.schema, markdown: text, fileName, }); @@ -419,6 +423,7 @@ export class StarterDebugMenu extends ShadowlessElement { if (!file) return; const result = await MarkdownTransformer.importMarkdownZip({ collection: this.collection, + schema: this.editor.doc.schema, imported: file, }); if (!this.editor.host) return; @@ -438,8 +443,9 @@ export class StarterDebugMenu extends ShadowlessElement { multiple: false, }); if (!file) return; + const doc = this.editor.doc; const job = new Transformer({ - schema: this.collection.schema, + schema: doc.schema, blobCRUD: this.collection.blobSync, docCRUD: { create: (id: string) => this.collection.createDoc({ id }), @@ -465,6 +471,7 @@ export class StarterDebugMenu extends ShadowlessElement { if (!file) return; const result = await NotionHtmlTransformer.importNotionZip({ collection: this.collection, + schema: this.editor.doc.schema, imported: file, }); if (!this.editor.host) return; @@ -488,7 +495,11 @@ export class StarterDebugMenu extends ShadowlessElement { return; } try { - const docs = await ZipTransformer.importDocs(this.collection, file); + const docs = await ZipTransformer.importDocs( + this.collection, + this.editor.doc.schema, + file + ); for (const doc of docs) { if (doc) { const noteBlock = window.doc.getBlockByFlavour('affine:note'); diff --git a/blocksuite/playground/apps/_common/helper.ts b/blocksuite/playground/apps/_common/helper.ts index 6a0f72e4dc4e6..5112342a876d6 100644 --- a/blocksuite/playground/apps/_common/helper.ts +++ b/blocksuite/playground/apps/_common/helper.ts @@ -1,10 +1,8 @@ -import { AffineSchemas, SpecProvider } from '@blocksuite/blocks'; -import { Schema } from '@blocksuite/store'; +import { SpecProvider } from '@blocksuite/blocks'; import { TestWorkspace } from '@blocksuite/store/test'; export function createEmptyDoc() { - const schema = new Schema().register(AffineSchemas); - const collection = new TestWorkspace({ schema }); + const collection = new TestWorkspace(); collection.storeExtensions = SpecProvider._.getSpec('store').value; collection.meta.initialize(); const doc = collection.createDoc(); diff --git a/blocksuite/playground/apps/starter/data/affine-snapshot.ts b/blocksuite/playground/apps/starter/data/affine-snapshot.ts index 8b1a297ffb53e..eab8faead32aa 100644 --- a/blocksuite/playground/apps/starter/data/affine-snapshot.ts +++ b/blocksuite/playground/apps/starter/data/affine-snapshot.ts @@ -1,5 +1,5 @@ -import { ZipTransformer } from '@blocksuite/blocks'; -import { Text, type Workspace } from '@blocksuite/store'; +import { AffineSchemas, ZipTransformer } from '@blocksuite/blocks'; +import { Schema, Text, type Workspace } from '@blocksuite/store'; export async function affineSnapshot(collection: Workspace, id: string) { const doc = collection.createDoc({ id }); @@ -13,7 +13,9 @@ export async function affineSnapshot(collection: Workspace, id: string) { const path = '/apps/starter/data/snapshots/affine-default.zip'; const response = await fetch(path); const file = await response.blob(); - await ZipTransformer.importDocs(collection, file); + const schema = new Schema(); + schema.register(AffineSchemas); + await ZipTransformer.importDocs(collection, schema, file); } affineSnapshot.id = 'affine-snapshot'; diff --git a/blocksuite/playground/apps/starter/utils/collection.ts b/blocksuite/playground/apps/starter/utils/collection.ts index 57f944e123f96..ca38057bd6d9c 100644 --- a/blocksuite/playground/apps/starter/utils/collection.ts +++ b/blocksuite/playground/apps/starter/utils/collection.ts @@ -49,7 +49,6 @@ export function createStarterDocCollection() { const options: DocCollectionOptions = { id: collectionId, - schema, idGenerator, awarenessSources: [new BroadcastChannelAwarenessSource(id)], docSources, @@ -63,7 +62,7 @@ export function createStarterDocCollection() { window.collection = collection; window.blockSchemas = AffineSchemas; window.job = new Transformer({ - schema: collection.schema, + schema, blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/blocksuite/tests-legacy/e2e/utils/asserts.ts b/blocksuite/tests-legacy/e2e/utils/asserts.ts index b4ba179813698..d9bdfb0ac82ef 100644 --- a/blocksuite/tests-legacy/e2e/utils/asserts.ts +++ b/blocksuite/tests-legacy/e2e/utils/asserts.ts @@ -66,33 +66,6 @@ export const defaultStore = { tags: [], }, ], - blockVersions: { - 'affine:paragraph': 1, - 'affine:page': 2, - 'affine:database': 3, - 'affine:data-view': 1, - 'affine:list': 1, - 'affine:note': 1, - 'affine:divider': 1, - 'affine:embed-youtube': 1, - 'affine:embed-figma': 1, - 'affine:embed-github': 1, - 'affine:embed-loom': 1, - 'affine:embed-html': 1, - 'affine:embed-linked-doc': 1, - 'affine:embed-synced-doc': 1, - 'affine:image': 1, - 'affine:latex': 1, - 'affine:frame': 1, - 'affine:code': 1, - 'affine:surface': 5, - 'affine:bookmark': 1, - 'affine:attachment': 1, - 'affine:surface-ref': 1, - 'affine:edgeless-text': 1, - }, - workspaceVersion: 2, - pageVersion: 2, }, spaces: { 'doc:home': { diff --git a/packages/frontend/apps/android/src/app.tsx b/packages/frontend/apps/android/src/app.tsx index 49cad24e9640c..590d62d9b2e4f 100644 --- a/packages/frontend/apps/android/src/app.tsx +++ b/packages/frontend/apps/android/src/app.tsx @@ -166,7 +166,7 @@ framework.impl(AIButtonProvider, { const blockSuiteDoc = doc.blockSuiteDoc; const transformer = new Transformer({ - schema: blockSuiteDoc.workspace.schema, + schema: blockSuiteDoc.schema, blobCRUD: blockSuiteDoc.workspace.blobSync, docCRUD: { create: (id: string) => blockSuiteDoc.workspace.createDoc({ id }), diff --git a/packages/frontend/apps/ios/src/app.tsx b/packages/frontend/apps/ios/src/app.tsx index faa8e509f6b5e..8423fd080e6d7 100644 --- a/packages/frontend/apps/ios/src/app.tsx +++ b/packages/frontend/apps/ios/src/app.tsx @@ -224,7 +224,7 @@ const frameworkProvider = framework.provider(); const blockSuiteDoc = doc.blockSuiteDoc; const transformer = new Transformer({ - schema: blockSuiteDoc.workspace.schema, + schema: blockSuiteDoc.schema, blobCRUD: blockSuiteDoc.workspace.blobSync, docCRUD: { create: (id: string) => blockSuiteDoc.workspace.createDoc({ id }), diff --git a/packages/frontend/core/src/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model.ts b/packages/frontend/core/src/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model.ts index 150a6e56885cf..48165496b5dad 100644 --- a/packages/frontend/core/src/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model.ts +++ b/packages/frontend/core/src/blocksuite/ai/blocks/ai-chat-block/model/ai-chat-model.ts @@ -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(BlockModel) {} diff --git a/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts b/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts index 11b8ad25d3211..e632a60595377 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/text-renderer.ts @@ -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; diff --git a/packages/frontend/core/src/blocksuite/ai/effects.ts b/packages/frontend/core/src/blocksuite/ai/effects.ts index 337b518d07606..e1bb284f9aac8 100644 --- a/packages/frontend/core/src/blocksuite/ai/effects.ts +++ b/packages/frontend/core/src/blocksuite/ai/effects.ts @@ -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]); } diff --git a/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts b/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts index 72242688df95c..95549e3eb34b4 100644 --- a/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts +++ b/packages/frontend/core/src/blocksuite/ai/messages/slides-renderer.ts @@ -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(); diff --git a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/__tests__/mindmap-preview.unit.spec.ts b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/__tests__/mindmap-preview.unit.spec.ts index 9d69b24a1160e..fbf6bb4200c39 100644 --- a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/__tests__/mindmap-preview.unit.spec.ts +++ b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/__tests__/mindmap-preview.unit.spec.ts @@ -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); diff --git a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/mindmap-preview.ts b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/mindmap-preview.ts index eb55378bfec56..b26cc9e6825ab 100644 --- a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/mindmap-preview.ts +++ b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/mindmap-preview.ts @@ -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 }), diff --git a/packages/frontend/core/src/blocksuite/ai/utils/extract.ts b/packages/frontend/core/src/blocksuite/ai/utils/extract.ts index 1f9111246357c..d78acea2d8406 100644 --- a/packages/frontend/core/src/blocksuite/ai/utils/extract.ts +++ b/packages/frontend/core/src/blocksuite/ai/utils/extract.ts @@ -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 }), diff --git a/packages/frontend/core/src/blocksuite/block-suite-page-list/utils.tsx b/packages/frontend/core/src/blocksuite/block-suite-page-list/utils.tsx index 5033e6aeabf0a..4b586fdf18df3 100644 --- a/packages/frontend/core/src/blocksuite/block-suite-page-list/utils.tsx +++ b/packages/frontend/core/src/blocksuite/block-suite-page-list/utils.tsx @@ -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)); diff --git a/packages/frontend/core/src/blocksuite/utils/markdown-utils.ts b/packages/frontend/core/src/blocksuite/utils/markdown-utils.ts index 6edfc76ce91df..99c14ff0d60ff 100644 --- a/packages/frontend/core/src/blocksuite/utils/markdown-utils.ts +++ b/packages/frontend/core/src/blocksuite/utils/markdown-utils.ts @@ -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 }), diff --git a/packages/frontend/core/src/components/affine/page-history-modal/data.ts b/packages/frontend/core/src/components/affine/page-history-modal/data.ts index ed95bde3f5e3e..ad2a6ea7589be 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/data.ts +++ b/packages/frontend/core/src/components/affine/page-history-modal/data.ts @@ -2,7 +2,6 @@ import { useDocMetaHelper } from '@affine/core/components/hooks/use-block-suite- import { useDocCollectionPage } from '@affine/core/components/hooks/use-block-suite-workspace-page'; import { FetchService, GraphQLService } from '@affine/core/modules/cloud'; import { - getAFFiNEWorkspaceSchema, type WorkspaceFlavourProvider, WorkspaceService, WorkspacesService, @@ -131,7 +130,6 @@ const getOrCreateShellWorkspace = ( return Promise.resolve([]); }, }, - schema: getAFFiNEWorkspaceSchema(), }); docCollectionMap.set(workspaceId, docCollection); docCollection.doc.emit('sync', [true, docCollection.doc]); diff --git a/packages/frontend/core/src/components/hooks/affine/use-export-page.ts b/packages/frontend/core/src/components/hooks/affine/use-export-page.ts index 8452302f40cba..d6bd646d2a1f6 100644 --- a/packages/frontend/core/src/components/hooks/affine/use-export-page.ts +++ b/packages/frontend/core/src/components/hooks/affine/use-export-page.ts @@ -4,6 +4,7 @@ import { resolveGlobalLoadingEventAtom, } from '@affine/component/global-loading'; import { EditorService } from '@affine/core/modules/editor'; +import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace/global-schema'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; import type { BlockStdScope } from '@blocksuite/affine/block-std'; @@ -59,7 +60,7 @@ async function exportDoc( config: AdapterConfig ) { const transformer = new Transformer({ - schema: doc.workspace.schema, + schema: getAFFiNEWorkspaceSchema(), blobCRUD: doc.workspace.blobSync, docCRUD: { create: (id: string) => doc.workspace.createDoc({ id }), @@ -148,7 +149,11 @@ async function exportHandler({ await exportToMarkdown(page, editorRoot?.std); return; case 'snapshot': - await ZipTransformer.exportDocs(page.workspace, [page]); + await ZipTransformer.exportDocs( + page.workspace, + getAFFiNEWorkspaceSchema(), + [page] + ); return; case 'pdf': await printToPdf(editorContainer); diff --git a/packages/frontend/core/src/components/page-list/__tests__/use-block-suite-page-preview.spec.ts b/packages/frontend/core/src/components/page-list/__tests__/use-block-suite-page-preview.spec.ts index 15cf13b2605bb..ccb130b5204ff 100644 --- a/packages/frontend/core/src/components/page-list/__tests__/use-block-suite-page-preview.spec.ts +++ b/packages/frontend/core/src/components/page-list/__tests__/use-block-suite-page-preview.spec.ts @@ -3,9 +3,9 @@ */ import 'fake-indexeddb/auto'; -import { AffineSchemas } from '@blocksuite/affine/blocks/schemas'; +import { StoreExtensions } from '@blocksuite/affine/blocks'; import { assertExists } from '@blocksuite/affine/global/utils'; -import { Schema, type Store, Text } from '@blocksuite/affine/store'; +import { type Store, Text } from '@blocksuite/affine/store'; import { TestWorkspace } from '@blocksuite/affine/store/test'; import { renderHook } from '@testing-library/react'; import { useAtomValue } from 'jotai'; @@ -14,12 +14,11 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import { useBlockSuitePagePreview } from '../use-block-suite-page-preview'; let docCollection: TestWorkspace; -const schema = new Schema(); -schema.register(AffineSchemas); +const extensions = StoreExtensions; beforeEach(async () => { vi.useFakeTimers({ toFake: ['requestIdleCallback'] }); - docCollection = new TestWorkspace({ id: 'test', schema }); + docCollection = new TestWorkspace({ id: 'test' }); docCollection.meta.initialize(); const initPage = async (page: Store) => { page.load(); @@ -31,7 +30,7 @@ beforeEach(async () => { const frameId = page.addBlock('affine:note', {}, pageBlockId); page.addBlock('affine:paragraph', {}, frameId); }; - await initPage(docCollection.createDoc({ id: 'page0' })); + await initPage(docCollection.createDoc({ id: 'page0', extensions })); }); describe('useBlockSuitePagePreview', () => { diff --git a/packages/frontend/core/src/components/providers/workspace-side-effects.tsx b/packages/frontend/core/src/components/providers/workspace-side-effects.tsx index b451681e6ef8b..b5e16abb8a48b 100644 --- a/packages/frontend/core/src/components/providers/workspace-side-effects.tsx +++ b/packages/frontend/core/src/components/providers/workspace-side-effects.tsx @@ -26,7 +26,10 @@ import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { useRegisterNavigationCommands } from '@affine/core/modules/navigation/view/use-register-navigation-commands'; import { QuickSearchContainer } from '@affine/core/modules/quicksearch'; import { WorkbenchService } from '@affine/core/modules/workbench'; -import { WorkspaceService } from '@affine/core/modules/workspace'; +import { + getAFFiNEWorkspaceSchema, + WorkspaceService, +} from '@affine/core/modules/workspace'; import { useI18n } from '@affine/i18n'; import track from '@affine/track'; import { type DocMode, ZipTransformer } from '@blocksuite/affine/blocks'; @@ -74,6 +77,7 @@ export const WorkspaceSideEffects = () => { throwIfAborted(abort); const [doc] = await ZipTransformer.importDocs( currentWorkspace.docCollection, + getAFFiNEWorkspaceSchema(), templateBlob ); if (doc) { diff --git a/packages/frontend/core/src/desktop/dialogs/import/index.tsx b/packages/frontend/core/src/desktop/dialogs/import/index.tsx index 2959fea0a81c0..ef8b22c147e63 100644 --- a/packages/frontend/core/src/desktop/dialogs/import/index.tsx +++ b/packages/frontend/core/src/desktop/dialogs/import/index.tsx @@ -5,7 +5,10 @@ import type { WORKSPACE_DIALOG_SCHEMA, } from '@affine/core/modules/dialogs'; import { UrlService } from '@affine/core/modules/url'; -import { WorkspaceService } from '@affine/core/modules/workspace'; +import { + getAFFiNEWorkspaceSchema, + WorkspaceService, +} from '@affine/core/modules/workspace'; import { DebugLogger } from '@affine/debug'; import { useI18n } from '@affine/i18n'; import track from '@affine/track'; @@ -141,6 +144,7 @@ const importConfigs: Record = { const fileName = file.name.split('.').slice(0, -1).join('.'); const docId = await MarkdownTransformer.importMarkdownToDoc({ collection: docCollection, + schema: getAFFiNEWorkspaceSchema(), markdown: text, fileName, }); @@ -159,6 +163,7 @@ const importConfigs: Record = { } const docIds = await MarkdownTransformer.importMarkdownZip({ collection: docCollection, + schema: getAFFiNEWorkspaceSchema(), imported: file, }); return { @@ -178,6 +183,7 @@ const importConfigs: Record = { const fileName = file.name.split('.').slice(0, -1).join('.'); const docId = await HtmlTransformer.importHTMLToDoc({ collection: docCollection, + schema: getAFFiNEWorkspaceSchema(), html: text, fileName, }); @@ -197,6 +203,7 @@ const importConfigs: Record = { const { entryId, pageIds, isWorkspaceFile } = await NotionHtmlTransformer.importNotionZip({ collection: docCollection, + schema: getAFFiNEWorkspaceSchema(), imported: file, }); return { @@ -212,7 +219,13 @@ const importConfigs: Record = { if (Array.isArray(file)) { throw new Error('Expected a single zip file for snapshot import'); } - const docIds = (await ZipTransformer.importDocs(docCollection, file)) + const docIds = ( + await ZipTransformer.importDocs( + docCollection, + getAFFiNEWorkspaceSchema(), + file + ) + ) .filter(doc => doc !== undefined) .map(doc => doc.id); diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/docs/index.ts b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/docs/index.ts index 15a399e3f42a7..6bf74bd2e8b39 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/docs/index.ts +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/docs/index.ts @@ -1,7 +1,7 @@ +import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace'; import { WorkspaceImpl } from '@affine/core/modules/workspace/impls/workspace'; -import { AffineSchemas } from '@blocksuite/affine/blocks'; import type { DocSnapshot, Store } from '@blocksuite/affine/store'; -import { Schema, Transformer } from '@blocksuite/affine/store'; +import { Transformer } from '@blocksuite/affine/store'; const getCollection = (() => { let collection: WorkspaceImpl | null = null; @@ -9,9 +9,7 @@ const getCollection = (() => { if (collection) { return collection; } - const schema = new Schema(); - schema.register(AffineSchemas); - collection = new WorkspaceImpl({ schema }); + collection = new WorkspaceImpl({}); collection.meta.initialize(); return collection; }; @@ -86,7 +84,7 @@ async function initDoc(name: DocName) { const snapshot = (await loaders[name]()) as DocSnapshot; const collection = await getCollection(); const transformer = new Transformer({ - schema: collection.schema, + schema: getAFFiNEWorkspaceSchema(), blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/packages/frontend/core/src/desktop/pages/workspace/index.tsx b/packages/frontend/core/src/desktop/pages/workspace/index.tsx index 0c997cae0a470..2cdba10b260ec 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/index.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/index.tsx @@ -10,6 +10,7 @@ import { DndService } from '@affine/core/modules/dnd/services'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { OpenInAppGuard } from '@affine/core/modules/open-in-app'; import { + getAFFiNEWorkspaceSchema, type Workspace, type WorkspaceMetadata, WorkspacesService, @@ -279,6 +280,7 @@ const WorkspacePage = ({ meta }: { meta: WorkspaceMetadata }) => { window.exportWorkspaceSnapshot = async (docs?: string[]) => { await ZipTransformer.exportDocs( workspace.docCollection, + getAFFiNEWorkspaceSchema(), Array.from(workspace.docCollection.docs.values()) .filter(doc => (docs ? docs.includes(doc.id) : true)) .map(doc => doc.getStore()) @@ -294,6 +296,7 @@ const WorkspacePage = ({ meta }: { meta: WorkspaceMetadata }) => { const blob = new Blob([file], { type: 'application/zip' }); const newDocs = await ZipTransformer.importDocs( workspace.docCollection, + getAFFiNEWorkspaceSchema(), blob ); console.log( diff --git a/packages/frontend/core/src/modules/doc/services/docs.ts b/packages/frontend/core/src/modules/doc/services/docs.ts index 3520aebd13673..11be2445bbb37 100644 --- a/packages/frontend/core/src/modules/doc/services/docs.ts +++ b/packages/frontend/core/src/modules/doc/services/docs.ts @@ -16,6 +16,7 @@ import { initDocFromProps, } from '../../../blocksuite/initialization'; import type { DocProperties } from '../../db'; +import { getAFFiNEWorkspaceSchema } from '../../workspace'; import type { Doc } from '../entities/doc'; import { DocPropertyList } from '../entities/property-list'; import { DocRecordList } from '../entities/record-list'; @@ -202,7 +203,7 @@ export class DocsService extends Service { const collection = this.store.getBlocksuiteCollection(); const transformer = new Transformer({ - schema: collection.schema, + schema: getAFFiNEWorkspaceSchema(), blobCRUD: collection.blobSync, docCRUD: { create: (id: string) => collection.createDoc({ id }), diff --git a/packages/frontend/core/src/modules/docs-search/worker/in-worker.ts b/packages/frontend/core/src/modules/docs-search/worker/in-worker.ts index 57b137b343256..a583679c603ed 100644 --- a/packages/frontend/core/src/modules/docs-search/worker/in-worker.ts +++ b/packages/frontend/core/src/modules/docs-search/worker/in-worker.ts @@ -116,7 +116,6 @@ const bookmarkFlavours = new Set([ const markdownPreviewDocCollection = new WorkspaceImpl({ id: 'indexer', - schema: blocksuiteSchema, }); function generateMarkdownPreviewBuilder( @@ -190,7 +189,7 @@ function generateMarkdownPreviewBuilder( const provider = container.provider(); const markdownAdapter = new MarkdownAdapter( new Transformer({ - schema: markdownPreviewDocCollection.schema, + schema: getAFFiNEWorkspaceSchema(), blobCRUD: markdownPreviewDocCollection.blobSync, docCRUD: { create: (id: string) => markdownPreviewDocCollection.createDoc({ id }), diff --git a/packages/frontend/core/src/modules/import-template/services/import.ts b/packages/frontend/core/src/modules/import-template/services/import.ts index f1cf516ec2a66..4bfedd518a29f 100644 --- a/packages/frontend/core/src/modules/import-template/services/import.ts +++ b/packages/frontend/core/src/modules/import-template/services/import.ts @@ -2,7 +2,11 @@ import { type DocMode, ZipTransformer } from '@blocksuite/affine/blocks'; import { Service } from '@toeverything/infra'; import { DocsService } from '../../doc'; -import type { WorkspaceMetadata, WorkspacesService } from '../../workspace'; +import { + getAFFiNEWorkspaceSchema, + type WorkspaceMetadata, + type WorkspacesService, +} from '../../workspace'; export class ImportTemplateService extends Service { constructor(private readonly workspacesService: WorkspacesService) { @@ -21,6 +25,7 @@ export class ImportTemplateService extends Service { await workspace.engine.doc.waitForDocReady(workspace.id); // wait for root doc ready const [importedDoc] = await ZipTransformer.importDocs( workspace.docCollection, + getAFFiNEWorkspaceSchema(), new Blob([docBinary], { type: 'application/zip', }) diff --git a/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts b/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts index ad07916c6f5c7..b2f74638c8ce1 100644 --- a/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts +++ b/packages/frontend/core/src/modules/workspace-engine/impls/cloud.ts @@ -53,13 +53,12 @@ import { WorkspaceServerService, } from '../../cloud'; import type { GlobalState } from '../../storage'; -import { - getAFFiNEWorkspaceSchema, - type Workspace, - type WorkspaceFlavourProvider, - type WorkspaceFlavoursProvider, - type WorkspaceMetadata, - type WorkspaceProfileInfo, +import type { + Workspace, + WorkspaceFlavourProvider, + WorkspaceFlavoursProvider, + WorkspaceMetadata, + WorkspaceProfileInfo, } from '../../workspace'; import { WorkspaceImpl } from '../../workspace/impls/workspace'; import { getWorkspaceProfileWorker } from './out-worker'; @@ -163,7 +162,6 @@ class CloudWorkspaceFlavourProvider implements WorkspaceFlavourProvider { const docCollection = new WorkspaceImpl({ id: workspaceId, - schema: getAFFiNEWorkspaceSchema(), blobSource: { get: async key => { const record = await blobStorage.get(key); diff --git a/packages/frontend/core/src/modules/workspace-engine/impls/local.ts b/packages/frontend/core/src/modules/workspace-engine/impls/local.ts index 93a63c5834561..4d0a71cb0f20d 100644 --- a/packages/frontend/core/src/modules/workspace-engine/impls/local.ts +++ b/packages/frontend/core/src/modules/workspace-engine/impls/local.ts @@ -32,12 +32,11 @@ import { Observable } from 'rxjs'; import { type Doc as YDoc, encodeStateAsUpdate } from 'yjs'; import { DesktopApiService } from '../../desktop-api'; -import { - getAFFiNEWorkspaceSchema, - type WorkspaceFlavourProvider, - type WorkspaceFlavoursProvider, - type WorkspaceMetadata, - type WorkspaceProfileInfo, +import type { + WorkspaceFlavourProvider, + WorkspaceFlavoursProvider, + WorkspaceMetadata, + WorkspaceProfileInfo, } from '../../workspace'; import { WorkspaceImpl } from '../../workspace/impls/workspace'; import { getWorkspaceProfileWorker } from './out-worker'; @@ -145,7 +144,6 @@ class LocalWorkspaceFlavourProvider implements WorkspaceFlavourProvider { const docCollection = new WorkspaceImpl({ id: id, - schema: getAFFiNEWorkspaceSchema(), blobSource: { get: async key => { const record = await blobStorage.get(key); diff --git a/packages/frontend/core/src/modules/workspace/entities/workspace.ts b/packages/frontend/core/src/modules/workspace/entities/workspace.ts index 6dea85e09a8ae..bc40da16fc618 100644 --- a/packages/frontend/core/src/modules/workspace/entities/workspace.ts +++ b/packages/frontend/core/src/modules/workspace/entities/workspace.ts @@ -3,7 +3,6 @@ import { Entity, LiveData } from '@toeverything/infra'; import { Observable } from 'rxjs'; import type { Awareness } from 'y-protocols/awareness.js'; -import { getAFFiNEWorkspaceSchema } from '../global-schema'; import { WorkspaceImpl } from '../impls/workspace'; import type { WorkspaceScope } from '../scopes/workspace'; import { WorkspaceEngineService } from '../services/engine'; @@ -51,7 +50,6 @@ export class Workspace extends Entity { name: 'blob', readonly: false, }, - schema: getAFFiNEWorkspaceSchema(), onLoadDoc: doc => this.engine.doc.connectDoc(doc), onLoadAwareness: awareness => this.engine.awareness.connectAwareness(awareness), diff --git a/packages/frontend/core/src/modules/workspace/impls/doc.ts b/packages/frontend/core/src/modules/workspace/impls/doc.ts index 9da68e39bd1f5..73a5a04b15217 100644 --- a/packages/frontend/core/src/modules/workspace/impls/doc.ts +++ b/packages/frontend/core/src/modules/workspace/impls/doc.ts @@ -147,10 +147,6 @@ export class DocImpl implements Doc { return this._ready; } - get schema() { - return this.workspace.schema; - } - get spaceDoc() { return this._ySpaceDoc; } @@ -174,13 +170,6 @@ export class DocImpl implements Doc { return (readonly?.toString() as 'true' | 'false') ?? 'false'; } - private _handleVersion() { - // Initialization from empty yDoc, indicating that the document is new. - if (!this.workspace.meta.hasVersion) { - this.workspace.meta.writeVersion(this.workspace); - } - } - private _handleYBlockAdd(id: string) { this.slots.yBlockUpdated.emit({ type: 'add', id }); } @@ -296,7 +285,6 @@ export class DocImpl implements Doc { const doc = new Store({ doc: this, - schema: this.workspace.schema, readonly, query, provider, @@ -316,10 +304,6 @@ export class DocImpl implements Doc { this.spaceDoc.load(); this.workspace.onLoadDoc?.(this.spaceDoc); - if ((this.workspace.meta.docs?.length ?? 0) <= 1) { - this._handleVersion(); - } - this._initYBlocks(); this._yBlocks.forEach((_, id) => { diff --git a/packages/frontend/core/src/modules/workspace/impls/meta.ts b/packages/frontend/core/src/modules/workspace/impls/meta.ts index 7647947b7e121..e0c9ed3dff271 100644 --- a/packages/frontend/core/src/modules/workspace/impls/meta.ts +++ b/packages/frontend/core/src/modules/workspace/impls/meta.ts @@ -3,20 +3,13 @@ import { createYProxy, type DocMeta, type DocsPropertiesMeta, - type Workspace, type WorkspaceMeta, } from '@blocksuite/affine/store'; import type * as Y from 'yjs'; -const COLLECTION_VERSION = 2; -const PAGE_VERSION = 2; - type MetaState = { pages?: unknown[]; properties?: DocsPropertiesMeta; - workspaceVersion?: number; - pageVersion?: number; - blockVersions?: Record; name?: string; avatar?: string; }; @@ -102,25 +95,6 @@ export class WorkspaceMetaImpl implements WorkspaceMeta { return this._proxy.pages; } - get hasVersion() { - if (!this._blockVersions || !this._pageVersion || !this._workspaceVersion) { - return false; - } - return Object.keys(this._blockVersions).length > 0; - } - - private get _blockVersions() { - return this._proxy.blockVersions; - } - - private get _pageVersion() { - return this._proxy.pageVersion; - } - - private get _workspaceVersion() { - return this._proxy.workspaceVersion; - } - get yDocs() { return this._yMap.get('pages') as unknown as Y.Array; } @@ -220,33 +194,4 @@ export class WorkspaceMetaImpl implements WorkspaceMeta { }); }, this._doc.clientID); } - - /** - * @internal Only for doc initialization - */ - writeVersion(collection: Workspace) { - const { blockVersions, pageVersion, workspaceVersion } = this._proxy; - - if (!workspaceVersion) { - this._proxy.workspaceVersion = COLLECTION_VERSION; - } else { - console.error('Workspace version is already set'); - } - - if (!pageVersion) { - this._proxy.pageVersion = PAGE_VERSION; - } else { - console.error('Doc version is already set'); - } - - if (!blockVersions) { - const _versions: Record = {}; - collection.schema.flavourSchemaMap.forEach((schema, flavour) => { - _versions[flavour] = schema.version; - }); - this._proxy.blockVersions = _versions; - } else { - console.error('Block versions is already set'); - } - } } diff --git a/packages/frontend/core/src/modules/workspace/impls/workspace.ts b/packages/frontend/core/src/modules/workspace/impls/workspace.ts index d0e6844428ebb..6e1bc02fdf710 100644 --- a/packages/frontend/core/src/modules/workspace/impls/workspace.ts +++ b/packages/frontend/core/src/modules/workspace/impls/workspace.ts @@ -10,7 +10,6 @@ import { type GetBlocksOptions, type IdGenerator, nanoid, - type Schema, type Store, type Workspace, type WorkspaceMeta, @@ -28,15 +27,12 @@ import { WorkspaceMetaImpl } from './meta'; type WorkspaceOptions = { id?: string; - schema: Schema; blobSource?: BlobSource; onLoadDoc?: (doc: Y.Doc) => void; onLoadAwareness?: (awareness: Awareness) => void; }; export class WorkspaceImpl implements Workspace { - protected readonly _schema: Schema; - readonly awarenessStore: AwarenessStore; readonly blobSync: BlobEngine; @@ -61,22 +57,15 @@ export class WorkspaceImpl implements Workspace { return this.blockCollections; } - get schema() { - return this._schema; - } - readonly onLoadDoc?: (doc: Y.Doc) => void; readonly onLoadAwareness?: (awareness: Awareness) => void; constructor({ id, - schema, blobSource, onLoadDoc, onLoadAwareness, - }: WorkspaceOptions) { - this._schema = schema; - + }: WorkspaceOptions = {}) { this.id = id || ''; this.doc = new Y.Doc({ guid: id }); this.awarenessStore = new AwarenessStore(new Awareness(this.doc)); diff --git a/packages/frontend/core/src/utils/first-app-data.ts b/packages/frontend/core/src/utils/first-app-data.ts index 110bb9e3d3581..facadfca77423 100644 --- a/packages/frontend/core/src/utils/first-app-data.ts +++ b/packages/frontend/core/src/utils/first-app-data.ts @@ -7,7 +7,10 @@ import onboardingUrl from '@affine/templates/onboarding.zip'; import { ZipTransformer } from '@blocksuite/affine/blocks'; import { DocsService } from '../modules/doc'; -import type { WorkspacesService } from '../modules/workspace'; +import { + getAFFiNEWorkspaceSchema, + type WorkspacesService, +} from '../modules/workspace'; export async function buildShowcaseWorkspace( workspacesService: WorkspacesService, @@ -19,7 +22,11 @@ export async function buildShowcaseWorkspace( docCollection.meta.setName(workspaceName); const blob = await (await fetch(onboardingUrl)).blob(); - await ZipTransformer.importDocs(docCollection, blob); + await ZipTransformer.importDocs( + docCollection, + getAFFiNEWorkspaceSchema(), + blob + ); }); const { workspace, dispose } = workspacesService.open({ metadata: meta });