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