feat: seperate createDoc and createStore (#11182)

This commit is contained in:
Saul-Mirone
2025-03-26 11:03:47 +00:00
parent d6093e1d66
commit 0a8d8e0a6b
70 changed files with 337 additions and 312 deletions

View File

@@ -41,9 +41,9 @@ function createTestDoc(docId = 'doc0') {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: docId, extensions }); const doc = collection.createDoc(docId);
doc.load(); doc.load();
return doc; return doc.getStore({ extensions });
} }
describe('DatabaseManager', () => { describe('DatabaseManager', () => {

View File

@@ -34,8 +34,8 @@ export function createJob(middlewares?: TransformerMiddleware[]) {
blobCRUD: docCollection.blobSync, blobCRUD: docCollection.blobSync,
middlewares: testMiddlewares, middlewares: testMiddlewares,
docCRUD: { docCRUD: {
create: (id: string) => docCollection.createDoc({ id }), create: (id: string) => docCollection.createDoc(id).getStore({ id }),
get: (id: string) => docCollection.getDoc(id), get: (id: string) => docCollection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => docCollection.removeDoc(id), delete: (id: string) => docCollection.removeDoc(id),
}, },
}); });

View File

@@ -86,7 +86,7 @@ export class BlockQueryDataSource extends DataSourceBase {
this.workspace.slots.docCreated.subscribe(id => { this.workspace.slots.docCreated.subscribe(id => {
const doc = this.workspace.getDoc(id); const doc = this.workspace.getDoc(id);
if (doc) { if (doc) {
this.listenToDoc(doc); this.listenToDoc(doc.getStore());
} }
}); });
this.workspace.slots.docRemoved.subscribe(id => { this.workspace.slots.docRemoved.subscribe(id => {

View File

@@ -412,7 +412,8 @@ export function createLinkedDocFromSlice(
snapshots: BlockSnapshot[], snapshots: BlockSnapshot[],
docTitle?: string docTitle?: string
) { ) {
const linkedDoc = doc.workspace.createDoc({}); const _doc = doc.workspace.createDoc();
const linkedDoc = _doc.getStore();
linkedDoc.load(() => { linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', { const rootId = linkedDoc.addBlock('affine:page', {
title: new Text(docTitle), title: new Text(docTitle),

View File

@@ -239,9 +239,8 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
} }
get linkedDoc() { get linkedDoc() {
return this.std.workspace.getDoc(this.model.props.pageId, { const doc = this.std.workspace.getDoc(this.model.props.pageId);
id: this.model.props.pageId, return doc?.getStore({ id: this.model.props.pageId });
});
} }
private _handleDoubleClick(event: MouseEvent) { private _handleDoubleClick(event: MouseEvent) {

View File

@@ -409,7 +409,8 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
get syncedDoc() { get syncedDoc() {
const options: GetBlocksOptions = { readonly: true }; const options: GetBlocksOptions = { readonly: true };
if (this.isPageMode) options.query = this._pageFilter; if (this.isPageMode) options.query = this._pageFilter;
return this.std.workspace.getDoc(this.model.props.pageId, options); const doc = this.std.workspace.getDoc(this.model.props.pageId);
return doc?.getStore(options) ?? null;
} }
private _checkCycle() { private _checkCycle() {

View File

@@ -32,7 +32,8 @@ export function createLinkedDocFromNote(
note: NoteBlockModel, note: NoteBlockModel,
docTitle?: string docTitle?: string
) { ) {
const linkedDoc = doc.workspace.createDoc({}); const _doc = doc.workspace.createDoc();
const linkedDoc = _doc.getStore();
linkedDoc.load(() => { linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', { const rootId = linkedDoc.addBlock('affine:page', {
title: new Text(docTitle), title: new Text(docTitle),
@@ -63,7 +64,8 @@ export function createLinkedDocFromEdgelessElements(
elements: GfxModel[], elements: GfxModel[],
docTitle?: string docTitle?: string
) { ) {
const linkedDoc = host.doc.workspace.createDoc({}); const _doc = host.doc.workspace.createDoc();
const linkedDoc = _doc.getStore();
linkedDoc.load(() => { linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', { const rootId = linkedDoc.addBlock('affine:page', {
title: new Text(docTitle), title: new Text(docTitle),

View File

@@ -96,8 +96,8 @@ async function importHTMLToDoc({
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [
@@ -164,8 +164,8 @@ async function importHTMLZip({
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [

View File

@@ -148,8 +148,8 @@ async function importMarkdownToDoc({
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [
@@ -215,8 +215,8 @@ async function importMarkdownZip({
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [

View File

@@ -128,8 +128,8 @@ async function importNotionZip({
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [defaultImageProxyMiddleware], middlewares: [defaultImageProxyMiddleware],

View File

@@ -18,8 +18,8 @@ async function exportDocs(
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [
@@ -109,8 +109,8 @@ async function importDocs(
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [ middlewares: [

View File

@@ -289,9 +289,8 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
this._referencedModel = this._referencedModel =
referencedModel && referencedModel.xywh ? referencedModel : null; referencedModel && referencedModel.xywh ? referencedModel : null;
// TODO(@L-Sun): clear query cache // TODO(@L-Sun): clear query cache
this._previewDoc = this.doc.workspace.getDoc(docId, { const doc = this.doc.workspace.getDoc(docId);
readonly: true, this._previewDoc = doc?.getStore({ readonly: true }) ?? null;
});
}; };
init(); init();

View File

@@ -102,13 +102,9 @@ export class FramePreview extends WithDisposable(ShadowlessElement) {
} }
private _initPreviewDoc() { private _initPreviewDoc() {
this._previewDoc = this._originalDoc.workspace.getDoc( const doc = this._originalDoc.workspace.getDoc(this._originalDoc.id);
this._originalDoc.id, this._previewDoc =
{ doc?.getStore({ readonly: true, query: this._docFilter }) ?? null;
query: this._docFilter,
readonly: true,
}
);
this.disposables.add(() => { this.disposables.add(() => {
this._originalDoc.doc.clearQuery(this._docFilter); this._originalDoc.doc.clearQuery(this._docFilter);
}); });

View File

@@ -60,7 +60,8 @@ export function affine(strings: TemplateStringsArray, ...values: any[]) {
// Create a new doc // Create a new doc
const workspace = new TestWorkspace({}); const workspace = new TestWorkspace({});
workspace.meta.initialize(); workspace.meta.initialize();
const doc = workspace.createDoc({ id: 'test-doc', extensions }); const doc = workspace.createDoc('test-doc');
const store = doc.getStore({ extensions });
// Use DOMParser to parse HTML string // Use DOMParser to parse HTML string
doc.load(() => { doc.load(() => {
@@ -72,11 +73,11 @@ export function affine(strings: TemplateStringsArray, ...values: any[]) {
throw new Error('Template must contain a root element'); throw new Error('Template must contain a root element');
} }
buildDocFromElement(doc, root, null); buildDocFromElement(store, root, null);
}); });
// Create and return a host object with the document // Create and return a host object with the document
return createTestHost(doc); return createTestHost(store);
} }
/** /**
@@ -103,7 +104,8 @@ export function block(
// Create a temporary doc to hold the block // Create a temporary doc to hold the block
const workspace = new TestWorkspace({}); const workspace = new TestWorkspace({});
workspace.meta.initialize(); workspace.meta.initialize();
const doc = workspace.createDoc({ id: 'temp-doc', extensions }); const doc = workspace.createDoc('temp-doc');
const store = doc.getStore({ extensions });
let blockId: string | null = null; let blockId: string | null = null;
@@ -117,11 +119,11 @@ export function block(
throw new Error('Template must contain a root element'); throw new Error('Template must contain a root element');
} }
blockId = buildDocFromElement(doc, root, null); blockId = buildDocFromElement(store, root, null);
}); });
// Return the created block // Return the created block
return blockId ? (doc.getBlock(blockId) ?? null) : null; return blockId ? (store.getBlock(blockId) ?? null) : null;
} }
/** /**

View File

@@ -100,7 +100,9 @@ export class DocDisplayMetaService
return signal(DocDisplayMetaService.icons.deleted); return signal(DocDisplayMetaService.icons.deleted);
} }
let icon$ = this.iconMap.get(doc); const store = doc.getStore();
let icon$ = this.iconMap.get(store);
if (!icon$) { if (!icon$) {
icon$ = signal( icon$ = signal(
@@ -128,11 +130,11 @@ export class DocDisplayMetaService
this.disposables.splice(index, 1); this.disposables.splice(index, 1);
disposable.unsubscribe(); disposable.unsubscribe();
} }
this.iconMap.delete(doc); this.iconMap.delete(store);
} }
}); });
this.disposables.push(docRemovedSubscription); this.disposables.push(docRemovedSubscription);
this.iconMap.set(doc, icon$); this.iconMap.set(store, icon$);
} }
return computed(() => { return computed(() => {
@@ -165,7 +167,9 @@ export class DocDisplayMetaService
return signal(title || 'Deleted doc'); return signal(title || 'Deleted doc');
} }
let title$ = this.titleMap.get(doc); const store = doc.getStore();
let title$ = this.titleMap.get(store);
if (!title$) { if (!title$) {
title$ = signal(doc.meta?.title || 'Untitled'); title$ = signal(doc.meta?.title || 'Untitled');
@@ -185,11 +189,11 @@ export class DocDisplayMetaService
this.disposables.splice(index, 1); this.disposables.splice(index, 1);
disposable.unsubscribe(); disposable.unsubscribe();
} }
this.iconMap.delete(doc); this.iconMap.delete(store);
} }
}); });
this.disposables.push(docRemovedSubscription); this.disposables.push(docRemovedSubscription);
this.titleMap.set(doc, title$); this.titleMap.set(store, title$);
} }
return computed(() => { return computed(() => {

View File

@@ -9,29 +9,30 @@ export function createDefaultDoc(
collection: Workspace, collection: Workspace,
options: { id?: string; title?: string } = {} options: { id?: string; title?: string } = {}
) { ) {
const doc = collection.createDoc({ id: options.id }); const doc = collection.createDoc(options.id);
doc.load(); doc.load();
const store = doc.getStore();
const title = options.title ?? ''; const title = options.title ?? '';
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(title), title: new Text(title),
}); });
collection.meta.setDocMeta(doc.id, { collection.meta.setDocMeta(doc.id, {
title, title,
}); });
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
const noteId = doc.addBlock( const noteId = store.addBlock(
'affine:note', 'affine:note',
{ {
xywh: `[0, 0, ${DEFAULT_PAGE_BLOCK_WIDTH}, ${DEFAULT_PAGE_BLOCK_HEIGHT}]`, xywh: `[0, 0, ${DEFAULT_PAGE_BLOCK_WIDTH}, ${DEFAULT_PAGE_BLOCK_HEIGHT}]`,
}, },
rootId rootId
); );
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
// To make sure the content of new doc would not be clear // To make sure the content of new doc would not be clear
// By undo operation for the first time // By undo operation for the first time
doc.resetHistory(); doc.resetHistory();
return doc; return store;
} }

View File

@@ -36,15 +36,16 @@ describe('editor host', () => {
const collection = new TestWorkspace(createTestOptions()); const collection = new TestWorkspace(createTestOptions());
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
const store = doc.getStore({ extensions });
doc.load(); doc.load();
const rootId = doc.addBlock('test:page'); const rootId = store.addBlock('test:page');
const noteId = doc.addBlock('test:note', {}, rootId); const noteId = store.addBlock('test:note', {}, rootId);
const headingId = doc.addBlock('test:heading', { type: 'h1' }, noteId); const headingId = store.addBlock('test:heading', { type: 'h1' }, noteId);
const headingBlock = doc.getBlock(headingId)!; const headingBlock = store.getBlock(headingId)!;
const editorContainer = new TestEditorContainer(); const editorContainer = new TestEditorContainer();
editorContainer.doc = doc; editorContainer.doc = store;
editorContainer.specs = testSpecs; editorContainer.specs = testSpecs;
document.body.append(editorContainer); document.body.append(editorContainer);

View File

@@ -80,16 +80,16 @@ function createTestDoc(docId = defaultDocId) {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ const doc = collection.createDoc(docId);
id: docId, doc.load();
const store = doc.getStore({
extensions: [ extensions: [
pageSchemaExtension, pageSchemaExtension,
tableSchemaExtension, tableSchemaExtension,
flatTableSchemaExtension, flatTableSchemaExtension,
], ],
}); });
doc.load(); return store;
return doc;
} }
test('init block without props should add default props', () => { test('init block without props should add default props', () => {

View File

@@ -61,12 +61,12 @@ function createTestDoc(docId = defaultDocId) {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ const doc = collection.createDoc(docId);
id: docId, const store = doc.getStore({
extensions, extensions,
}); });
doc.load(); doc.load();
return doc; return store;
} }
function requestIdleCallbackPolyfill( function requestIdleCallbackPolyfill(
@@ -97,7 +97,7 @@ describe('basic', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'doc:home' }); const doc = collection.createDoc('doc:home');
doc.load(); doc.load();
const actual = serializCollection(collection.doc); const actual = serializCollection(collection.doc);
const actualDoc = actual[spaceMetaId].pages[0] as DocMeta; const actualDoc = actual[spaceMetaId].pages[0] as DocMeta;
@@ -148,23 +148,23 @@ describe('basic', () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ const doc = collection.createDoc('space:0');
id: 'space:0', const store = doc.getStore({
extensions, extensions,
}); });
const readyCallback = vi.fn(); const readyCallback = vi.fn();
const rootAddedCallback = vi.fn(); const rootAddedCallback = vi.fn();
doc.slots.ready.subscribe(readyCallback); store.slots.ready.subscribe(readyCallback);
doc.slots.rootAdded.subscribe(rootAddedCallback); store.slots.rootAdded.subscribe(rootAddedCallback);
doc.load(() => { store.load(() => {
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
expect(rootAddedCallback).toBeCalledTimes(1); expect(rootAddedCallback).toBeCalledTimes(1);
doc.addBlock('affine:note', {}, rootId); store.addBlock('affine:note', {}, rootId);
}); });
expect(readyCallback).toBeCalledTimes(1); expect(readyCallback).toBeCalledTimes(1);
@@ -175,12 +175,12 @@ describe('basic', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const collection2 = new TestWorkspace(options); const collection2 = new TestWorkspace(options);
const doc = collection.createDoc({ const doc = collection.createDoc('space:0');
id: 'space:0', const store = doc.getStore({
extensions, extensions,
}); });
doc.load(() => { doc.load(() => {
doc.addBlock('affine:page', { store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
}); });
@@ -206,9 +206,7 @@ describe('basic', () => {
// apply doc update // apply doc update
const update = encodeStateAsUpdate(doc.spaceDoc); const update = encodeStateAsUpdate(doc.spaceDoc);
expect(collection2.docs.size).toBe(1); expect(collection2.docs.size).toBe(1);
const doc2 = collection2.getDoc('space:0', { const doc2 = collection2.getDoc('space:0');
extensions,
});
if (!doc2) { if (!doc2) {
throw new Error('doc2 is not found'); throw new Error('doc2 is not found');
} }
@@ -382,12 +380,15 @@ describe('addBlock', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc0 = collection.createDoc({ id: 'doc:home' }); const doc0 = collection.createDoc('doc:home');
const doc1 = collection.createDoc({ id: 'space:doc1' }); const doc1 = collection.createDoc('space:doc1');
await Promise.all([doc0.load(), doc1.load()]); await Promise.all([doc0.load(), doc1.load()]);
assert.equal(collection.docs.size, 2); assert.equal(collection.docs.size, 2);
const store0 = doc0.getStore({
extensions,
});
doc0.addBlock('affine:page', { store0.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
collection.removeDoc(doc0.id); collection.removeDoc(doc0.id);
@@ -407,8 +408,7 @@ describe('addBlock', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc0 = collection.createDoc({ id: 'doc:home' }); const doc0 = collection.createDoc('doc:home');
collection.removeDoc(doc0.id); collection.removeDoc(doc0.id);
assert.equal(collection.docs.size, 0); assert.equal(collection.docs.size, 0);
}); });
@@ -417,7 +417,7 @@ describe('addBlock', () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
collection.createDoc({ id: 'doc:home' }); collection.createDoc('doc:home');
assert.deepEqual( assert.deepEqual(
collection.meta.docMetas.map(({ id, title }) => ({ collection.meta.docMetas.map(({ id, title }) => ({

View File

@@ -31,12 +31,15 @@ test('trigger props updated', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc.load(); doc.load();
const store = doc.getStore({
extensions,
});
doc.addBlock('affine:page'); store.addBlock('affine:page');
const rootModel = doc.root as RootBlockModel; const rootModel = store.root as RootBlockModel;
expect(rootModel).not.toBeNull(); expect(rootModel).not.toBeNull();
@@ -91,12 +94,15 @@ test('stash and pop', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc.load(); doc.load();
const store = doc.getStore({
extensions,
});
doc.addBlock('affine:page'); store.addBlock('affine:page');
const rootModel = doc.root as RootBlockModel; const rootModel = store.root as RootBlockModel;
expect(rootModel).not.toBeNull(); expect(rootModel).not.toBeNull();
@@ -161,12 +167,15 @@ test('always get latest value in onChange', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc.load(); doc.load();
const store = doc.getStore({
extensions,
});
doc.addBlock('affine:page'); store.addBlock('affine:page');
const rootModel = doc.root as RootBlockModel; const rootModel = store.root as RootBlockModel;
expect(rootModel).not.toBeNull(); expect(rootModel).not.toBeNull();
@@ -207,11 +216,15 @@ test('query', () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc1 = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc1.load(); doc.load();
const doc2 = collection.getDoc('home', { extensions }); const store1 = doc.getStore({
extensions,
const doc3 = collection.getDoc('home', { });
const store2 = doc.getStore({
extensions,
});
const store3 = doc.getStore({
extensions, extensions,
query: { query: {
mode: 'loose', mode: 'loose',
@@ -223,48 +236,53 @@ test('query', () => {
], ],
}, },
}); });
expect(doc1).toBe(doc2); expect(store1).toBe(store2);
expect(doc1).not.toBe(doc3); expect(store1).not.toBe(store3);
const page = doc1.addBlock('affine:page'); const page = store1.addBlock('affine:page');
const note = doc1.addBlock('affine:note', {}, page); const note = store1.addBlock('affine:note', {}, page);
const paragraph1 = doc1.addBlock('affine:paragraph', {}, note); const paragraph1 = store1.addBlock('affine:paragraph', {}, note);
const list1 = doc1.addBlock('affine:list' as never, {}, note); const list1 = store1.addBlock('affine:list' as never, {}, note);
expect(doc2?.getBlock(paragraph1)?.blockViewType).toBe('display'); expect(store2?.getBlock(paragraph1)?.blockViewType).toBe('display');
expect(doc2?.getBlock(list1)?.blockViewType).toBe('display'); expect(store2?.getBlock(list1)?.blockViewType).toBe('display');
expect(doc3?.getBlock(list1)?.blockViewType).toBe('hidden'); expect(store3?.getBlock(list1)?.blockViewType).toBe('hidden');
const list2 = doc1.addBlock('affine:list' as never, {}, note); const list2 = store1.addBlock('affine:list' as never, {}, note);
expect(doc2?.getBlock(list2)?.blockViewType).toBe('display'); expect(store2?.getBlock(list2)?.blockViewType).toBe('display');
expect(doc3?.getBlock(list2)?.blockViewType).toBe('hidden'); expect(store3?.getBlock(list2)?.blockViewType).toBe('hidden');
}); });
test('local readonly', () => { test('local readonly', () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc1 = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc1.load(); const store1 = doc.getStore({
const doc2 = collection.getDoc('home', { readonly: true, extensions }); extensions,
const doc3 = collection.getDoc('home', { readonly: false, extensions }); });
const store2 = doc.getStore({
readonly: true,
extensions,
});
const store3 = doc.getStore({ readonly: false, extensions });
expect(doc1.readonly).toBeFalsy(); expect(store1.readonly).toBeFalsy();
expect(doc2?.readonly).toBeTruthy(); expect(store2.readonly).toBeTruthy();
expect(doc3?.readonly).toBeFalsy(); expect(store3.readonly).toBeFalsy();
doc1.readonly = true; store1.readonly = true;
expect(doc1.readonly).toBeTruthy(); expect(store1.readonly).toBeTruthy();
expect(doc2?.readonly).toBeTruthy(); expect(store2.readonly).toBeTruthy();
expect(doc3?.readonly).toBeFalsy(); expect(store3.readonly).toBeFalsy();
doc1.readonly = false; store1.readonly = false;
expect(doc1.readonly).toBeFalsy(); expect(store1.readonly).toBeFalsy();
expect(doc2?.readonly).toBeTruthy(); expect(store2.readonly).toBeTruthy();
expect(doc3?.readonly).toBeFalsy(); expect(store3.readonly).toBeFalsy();
}); });
describe('move blocks', () => { describe('move blocks', () => {
@@ -274,21 +292,22 @@ describe('move blocks', () => {
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
doc.load(); doc.load();
const pageId = doc.addBlock('affine:page'); const store = doc.getStore({ extensions });
const page = doc.getBlock(pageId)!.model; const pageId = store.addBlock('affine:page');
const page = store.getBlock(pageId)!.model;
const noteIds = doc.addBlocks( const noteIds = store.addBlocks(
[1, 2, 3].map(i => ({ [1, 2, 3].map(i => ({
flavour: 'affine:note', flavour: 'affine:note',
blockProps: { id: `${i}` }, blockProps: { id: `${i}` },
})), })),
page page
); );
const notes = noteIds.map(id => doc.getBlock(id)!.model); const notes = noteIds.map(id => store.getBlock(id)!.model);
context.doc = doc; context.doc = store;
context.page = page; context.page = page;
context.notes = notes; context.notes = notes;
}); });

View File

@@ -99,9 +99,10 @@ function createTestDoc(docId = defaultDocId) {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: docId, extensions }); const doc = collection.createDoc(docId);
doc.load(); doc.load();
return doc; const store = doc.getStore({ extensions });
return store;
} }
describe('schema', () => { describe('schema', () => {

View File

@@ -59,10 +59,11 @@ test('model to snapshot', () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
const store = doc.getStore({ extensions });
doc.load(); doc.load();
doc.addBlock('page'); store.addBlock('page');
const rootModel = doc.root as RootBlockModel; const rootModel = store.root as RootBlockModel;
expect(rootModel).not.toBeNull(); expect(rootModel).not.toBeNull();
const snapshot = transformer.toSnapshot({ const snapshot = transformer.toSnapshot({
@@ -76,10 +77,11 @@ test('snapshot to model', async () => {
const options = createTestOptions(); const options = createTestOptions();
const collection = new TestWorkspace(options); const collection = new TestWorkspace(options);
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'home', extensions }); const doc = collection.createDoc('home');
const store = doc.getStore({ extensions });
doc.load(); doc.load();
doc.addBlock('page'); store.addBlock('page');
const rootModel = doc.root as RootBlockModel; const rootModel = store.root as RootBlockModel;
const tempDoc = new Y.Doc(); const tempDoc = new Y.Doc();
const map = tempDoc.getMap('temp'); const map = tempDoc.getMap('temp');

View File

@@ -1127,8 +1127,9 @@ export class Store {
schema: this.schema, schema: this.schema,
blobCRUD: this.workspace.blobSync, blobCRUD: this.workspace.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => this.workspace.createDoc({ id }), create: (id: string) => this.workspace.createDoc(id).getStore({ id }),
get: (id: string) => this.workspace.getDoc(id), get: (id: string) =>
this.workspace.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => this.workspace.removeDoc(id), delete: (id: string) => this.workspace.removeDoc(id),
}, },
middlewares, middlewares,

View File

@@ -4,8 +4,7 @@ import type { Awareness } from 'y-protocols/awareness.js';
import type * as Y from 'yjs'; import type * as Y from 'yjs';
import type { IdGenerator } from '../utils/id-generator.js'; import type { IdGenerator } from '../utils/id-generator.js';
import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js'; import type { Doc } from './doc.js';
import type { Store } from './store/store.js';
import type { WorkspaceMeta } from './workspace-meta.js'; import type { WorkspaceMeta } from './workspace-meta.js';
export interface Workspace { export interface Workspace {
@@ -25,8 +24,8 @@ export interface Workspace {
docRemoved: Subject<string>; docRemoved: Subject<string>;
}; };
createDoc(options?: CreateBlocksOptions): Store; createDoc(docId?: string): Doc;
getDoc(docId: string, options?: GetBlocksOptions): Store | null; getDoc(docId: string): Doc | null;
removeDoc(docId: string): void; removeDoc(docId: string): void;
dispose(): void; dispose(): void;

View File

@@ -15,13 +15,7 @@ import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs'; import * as Y from 'yjs';
import type { ExtensionType } from '../extension/extension.js'; import type { ExtensionType } from '../extension/extension.js';
import type { import type { Doc, Workspace, WorkspaceMeta } from '../model/index.js';
CreateBlocksOptions,
GetBlocksOptions,
Store,
Workspace,
WorkspaceMeta,
} from '../model/index.js';
import { type IdGenerator, nanoid } from '../utils/id-generator.js'; import { type IdGenerator, nanoid } from '../utils/id-generator.js';
import { AwarenessStore } from '../yjs/index.js'; import { AwarenessStore } from '../yjs/index.js';
import { TestDoc } from './test-doc.js'; import { TestDoc } from './test-doc.js';
@@ -155,14 +149,9 @@ export class TestWorkspace implements Workspace {
* If the `init` parameter is passed, a `surface`, `note`, and `paragraph` block * If the `init` parameter is passed, a `surface`, `note`, and `paragraph` block
* will be created in the doc simultaneously. * will be created in the doc simultaneously.
*/ */
createDoc(options: CreateBlocksOptions = {}) { createDoc(docId?: string): Doc {
const { const id = docId ?? this.idGenerator();
id: docId = this.idGenerator(), if (this._hasDoc(id)) {
query,
readonly,
extensions,
} = options;
if (this._hasDoc(docId)) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.DocCollectionError, ErrorCode.DocCollectionError,
'doc already exists' 'doc already exists'
@@ -170,18 +159,13 @@ export class TestWorkspace implements Workspace {
} }
this.meta.addDocMeta({ this.meta.addDocMeta({
id: docId, id,
title: '', title: '',
createDate: Date.now(), createDate: Date.now(),
tags: [], tags: [],
}); });
this.slots.docCreated.next(docId); this.slots.docCreated.next(id);
return this.getDoc(docId, { return this.getDoc(id) as Doc;
id: docId,
query,
readonly,
extensions,
}) as Store;
} }
dispose() { dispose() {
@@ -203,12 +187,9 @@ export class TestWorkspace implements Workspace {
return space ?? null; return space ?? null;
} }
getDoc( getDoc(docId: string): Doc | null {
docId: string,
options: GetBlocksOptions = { id: docId }
): Store | null {
const collection = this.getBlockCollection(docId); const collection = this.getBlockCollection(docId);
return collection?.getStore(options) ?? null; return collection;
} }
removeDoc(docId: string) { removeDoc(docId: string) {

View File

@@ -1,6 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
import type { Store } from '../model/store/store'; import type { Store } from '../model';
import type { DocMeta, DocsPropertiesMeta } from '../model/workspace-meta'; import type { DocMeta, DocsPropertiesMeta } from '../model/workspace-meta';
export type BlockSnapshot = { export type BlockSnapshot = {

View File

@@ -15,8 +15,8 @@ export async function importFromSnapshot(
schema: new Schema().register(AffineSchemas), schema: new Schema().register(AffineSchemas),
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [replaceIdMiddleware(collection.idGenerator)], middlewares: [replaceIdMiddleware(collection.idGenerator)],

View File

@@ -50,7 +50,7 @@ function createCollectionOptions() {
} }
function initCollection(collection: TestWorkspace) { function initCollection(collection: TestWorkspace) {
const doc = collection.createDoc({ id: 'doc:home' }); const doc = collection.createDoc('doc:home').getStore();
doc.load(() => { doc.load(() => {
const rootId = doc.addBlock('affine:page', { const rootId = doc.addBlock('affine:page', {

View File

@@ -6,16 +6,17 @@ export function createEmptyDoc() {
collection.storeExtensions = SpecProvider._.getSpec('store').value; collection.storeExtensions = SpecProvider._.getSpec('store').value;
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc(); const doc = collection.createDoc();
const store = doc.getStore();
return { return {
doc, doc,
init() { init() {
doc.load(); doc.load();
const rootId = doc.addBlock('affine:page', {}); const rootId = store.addBlock('affine:page', {});
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = store.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
return doc; return store;
}, },
}; };
} }

View File

@@ -8,7 +8,7 @@ export function getDocFromUrlParams(collection: Workspace, url: URL) {
const docId = decodeURIComponent(url.hash.slice(1)); const docId = decodeURIComponent(url.hash.slice(1));
if (docId) { if (docId) {
doc = collection.getDoc(docId); doc = collection.getDoc(docId)?.getStore() ?? null;
} }
if (!doc) { if (!doc) {
const blockCollection = collection.docs.values().next().value as Doc; const blockCollection = collection.docs.values().next().value as Doc;

View File

@@ -2,13 +2,14 @@ import { ZipTransformer } from '@blocksuite/affine/blocks/root';
import { AffineSchemas } from '@blocksuite/affine/schemas'; import { AffineSchemas } from '@blocksuite/affine/schemas';
import { Schema, Text, type Workspace } from '@blocksuite/affine/store'; import { Schema, Text, type Workspace } from '@blocksuite/affine/store';
export async function affineSnapshot(collection: Workspace, id: string) { export async function affineSnapshot(collection: Workspace, id: string) {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id);
doc.load(); doc.load();
const store = doc.getStore();
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text('Affine Snapshot Test'), title: new Text('Affine Snapshot Test'),
}); });
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
const path = '/apps/starter/data/snapshots/affine-default.zip'; const path = '/apps/starter/data/snapshots/affine-default.zip';
const response = await fetch(path); const response = await fetch(path);

View File

@@ -15,24 +15,25 @@ import { viewPresets } from '@blocksuite/data-view/view-presets';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const database: InitFn = (collection: Workspace, id: string) => { export const database: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id);
const store = doc.getStore();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text('BlockSuite Playground'), title: new Text('BlockSuite Playground'),
}); });
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block // Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = store.addBlock('affine:note', {}, rootId);
const pId = doc.addBlock('affine:paragraph', {}, noteId); const pId = store.addBlock('affine:paragraph', {}, noteId);
const model = doc.getModelById(pId); const model = store.getModelById(pId);
if (!model) { if (!model) {
throw new Error('model is not found'); throw new Error('model is not found');
} }
const addDatabase = (title: string, group = true) => { const addDatabase = (title: string, group = true) => {
const databaseId = doc.addBlock( const databaseId = store.addBlock(
'affine:database', 'affine:database',
{ {
columns: [], columns: [],
@@ -40,7 +41,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
}, },
noteId noteId
); );
const database = doc.getModelById(databaseId) as DatabaseBlockModel; const database = store.getModelById(databaseId) as DatabaseBlockModel;
const datasource = new DatabaseBlockDataSource(database); const datasource = new DatabaseBlockDataSource(database);
datasource.viewManager.viewAdd('table'); datasource.viewManager.viewAdd('table');
database.props.title = new Text(title); database.props.title = new Text(title);
@@ -74,7 +75,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
'h6', 'h6',
]; ];
paragraphTypes.forEach(type => { paragraphTypes.forEach(type => {
const id = doc.addBlock( const id = store.addBlock(
'affine:paragraph', 'affine:paragraph',
{ type: type, text: new Text(`Paragraph type ${type}`) }, { type: type, text: new Text(`Paragraph type ${type}`) },
databaseId databaseId
@@ -90,7 +91,7 @@ export const database: InitFn = (collection: Workspace, id: string) => {
const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle']; const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
listTypes.forEach(type => { listTypes.forEach(type => {
const id = doc.addBlock( const id = store.addBlock(
'affine:list', 'affine:list',
{ type: type, text: new Text(`List type ${type}`) }, { type: type, text: new Text(`List type ${type}`) },
databaseId databaseId
@@ -104,11 +105,11 @@ export const database: InitFn = (collection: Workspace, id: string) => {
} }
}); });
// Add a paragraph after database // Add a paragraph after database
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
datasource.viewManager.viewAdd(viewPresets.kanbanViewMeta.type); datasource.viewManager.viewAdd(viewPresets.kanbanViewMeta.type);
doc.resetHistory(); doc.resetHistory();

View File

@@ -3,30 +3,31 @@ import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const embed: InitFn = (collection: Workspace, id: string) => { export const embed: InitFn = (collection: Workspace, id: string) => {
const doc = collection.getDoc(id) ?? collection.createDoc({ id }); const doc = collection.getDoc(id) ?? collection.createDoc(id);
doc.doc.clear(); const store = doc.getStore();
doc.clear();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
const surfaceId = doc.addBlock('affine:surface', {}, rootId); const surfaceId = store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block // Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = store.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block // Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
doc.addBlock( store.addBlock(
'affine:embed-github', 'affine:embed-github',
{ {
url: 'https://github.com/toeverything/AFFiNE/pull/5453', url: 'https://github.com/toeverything/AFFiNE/pull/5453',
}, },
noteId noteId
); );
doc.addBlock( store.addBlock(
'affine:embed-github', 'affine:embed-github',
{ {
url: 'https://www.github.com/toeverything/blocksuite/pull/5927', url: 'https://www.github.com/toeverything/blocksuite/pull/5927',
@@ -35,7 +36,7 @@ export const embed: InitFn = (collection: Workspace, id: string) => {
}, },
surfaceId surfaceId
); );
doc.addBlock( store.addBlock(
'affine:embed-github', 'affine:embed-github',
{ {
url: 'https://github.com/Milkdown/milkdown/pull/1215', url: 'https://github.com/Milkdown/milkdown/pull/1215',
@@ -43,7 +44,7 @@ export const embed: InitFn = (collection: Workspace, id: string) => {
}, },
surfaceId surfaceId
); );
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
}); });
doc.resetHistory(); doc.resetHistory();

View File

@@ -3,21 +3,22 @@ import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const empty: InitFn = (collection: Workspace, id: string) => { export const empty: InitFn = (collection: Workspace, id: string) => {
const doc = collection.getDoc(id) ?? collection.createDoc({ id }); const doc = collection.getDoc(id) ?? collection.createDoc(id);
doc.doc.clear(); const store = doc.getStore();
doc.clear();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block // Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = store.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block // Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId); store.addBlock('affine:paragraph', {}, noteId);
}); });
doc.resetHistory(); doc.resetHistory();

View File

@@ -50,16 +50,17 @@ export const heavyWhiteboard: InitFn = (collection: Workspace, id: string) => {
const count = Number(params.get('count')) || SHAPES_COUNT; const count = Number(params.get('count')) || SHAPES_COUNT;
const enableShapes = !!params.get('shapes'); const enableShapes = !!params.get('shapes');
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id);
const store = doc.getStore();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
const surfaceBlocks = enableShapes ? createShapes(count) : {}; const surfaceBlocks = enableShapes ? createShapes(count) : {};
doc.addBlock( store.addBlock(
'affine:surface', 'affine:surface',
{ {
elements: new Boxed(native2Y(surfaceBlocks, { deep: false })) as Boxed< elements: new Boxed(native2Y(surfaceBlocks, { deep: false })) as Boxed<
@@ -74,7 +75,7 @@ export const heavyWhiteboard: InitFn = (collection: Workspace, id: string) => {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
const x = Math.random() * RANGE - RANGE / 2; const x = Math.random() * RANGE - RANGE / 2;
const y = Math.random() * RANGE - RANGE / 2; const y = Math.random() * RANGE - RANGE / 2;
const noteId = doc.addBlock( const noteId = store.addBlock(
'affine:note', 'affine:note',
{ {
xywh: `[${x}, ${y}, 100, 50]` as SerializedXYWH, xywh: `[${x}, ${y}, 100, 50]` as SerializedXYWH,
@@ -82,7 +83,7 @@ export const heavyWhiteboard: InitFn = (collection: Workspace, id: string) => {
rootId rootId
); );
// Add paragraph block inside note block // Add paragraph block inside note block
doc.addBlock( store.addBlock(
'affine:paragraph', 'affine:paragraph',
{ {
text: new Text('Note #' + i), text: new Text('Note #' + i),

View File

@@ -7,19 +7,20 @@ const params = new URLSearchParams(location.search);
export const heavy: InitFn = (collection: Workspace, docId: string) => { export const heavy: InitFn = (collection: Workspace, docId: string) => {
const count = Number(params.get('count')) || 1000; const count = Number(params.get('count')) || 1000;
const doc = collection.createDoc({ id: docId }); const doc = collection.createDoc(docId);
const store = doc.getStore();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = store.addBlock('affine:page', {
title: new Text(), title: new Text(),
}); });
doc.addBlock('affine:surface', {}, rootId); store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block // Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = store.addBlock('affine:note', {}, rootId);
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
// Add paragraph block inside note block // Add paragraph block inside note block
doc.addBlock( store.addBlock(
'affine:paragraph', 'affine:paragraph',
{ {
text: new Text('Hello, world! ' + i), text: new Text('Hello, world! ' + i),

View File

@@ -3,13 +3,15 @@ import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const linked: InitFn = (collection: Workspace, id: string) => { export const linked: InitFn = (collection: Workspace, id: string) => {
const docA = collection.getDoc(id) ?? collection.createDoc({ id }); const docA =
collection.getDoc(id)?.getStore({ id }) ??
collection.createDoc(id).getStore({ id });
const docBId = 'doc:linked-page'; const docBId = 'doc:linked-page';
const docB = collection.createDoc({ id: docBId }); const docB = collection.createDoc(docBId).getStore();
const docCId = 'doc:linked-edgeless'; const docCId = 'doc:linked-edgeless';
const docC = collection.createDoc({ id: docCId }); const docC = collection.createDoc(docCId).getStore();
docA.doc.clear(); docA.doc.clear();
docB.doc.clear(); docB.doc.clear();

View File

@@ -4,7 +4,7 @@ import { createTestEditor } from '../utils/extensions.js';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const multiEditor: InitFn = (collection: Workspace, id: string) => { export const multiEditor: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id).getStore({ id });
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = doc.addBlock('affine:page', {
@@ -39,7 +39,7 @@ export const multiEditorVertical: InitFn = (
collection: Workspace, collection: Workspace,
docId: string docId: string
) => { ) => {
const doc = collection.createDoc({ id: docId }); const doc = collection.createDoc(docId).getStore();
doc.load(() => { doc.load(() => {
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = doc.addBlock('affine:page', {

View File

@@ -4,8 +4,8 @@ import * as Y from 'yjs';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const pendingStructs: InitFn = (collection: Workspace, id: string) => { export const pendingStructs: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id).getStore({ id });
const tempDoc = collection.createDoc({ id: 'tempDoc' }); const tempDoc = collection.createDoc('tempDoc').getStore();
doc.load(); doc.load();
tempDoc.load(() => { tempDoc.load(() => {
const rootId = tempDoc.addBlock('affine:page', { const rootId = tempDoc.addBlock('affine:page', {

View File

@@ -6,7 +6,7 @@ import type { InitFn } from './utils.js';
const presetMarkdown = `Click the 🔁 button to switch between editors dynamically - they are fully compatible!`; const presetMarkdown = `Click the 🔁 button to switch between editors dynamically - they are fully compatible!`;
export const preset: InitFn = async (collection: Workspace, id: string) => { export const preset: InitFn = async (collection: Workspace, id: string) => {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id).getStore({ id });
doc.load(); doc.load();
// Add root block and surface block at root level // Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', { const rootId = doc.addBlock('affine:page', {

View File

@@ -12,13 +12,17 @@ This article has also been published [in PDF format](https://www.inkandswitch.co
We welcome your feedback: [@inkandswitch](https://twitter.com/inkandswitch) or hello@inkandswitch.com.`; We welcome your feedback: [@inkandswitch](https://twitter.com/inkandswitch) or hello@inkandswitch.com.`;
export const synced: InitFn = (collection: Workspace, id: string) => { export const synced: InitFn = (collection: Workspace, id: string) => {
const docMain = collection.getDoc(id) ?? collection.createDoc({ id }); const docMain =
collection.getDoc(id)?.getStore({ id }) ??
collection.createDoc(id).getStore({ id });
const docSyncedPageId = 'doc:synced-page'; const docSyncedPageId = 'doc:synced-page';
const docSyncedPage = collection.createDoc({ id: docSyncedPageId }); const docSyncedPage = collection.createDoc(docSyncedPageId).getStore();
const docSyncedEdgelessId = 'doc:synced-edgeless'; const docSyncedEdgelessId = 'doc:synced-edgeless';
const docSyncedEdgeless = collection.createDoc({ id: docSyncedEdgelessId }); const docSyncedEdgeless = collection
.createDoc(docSyncedEdgelessId)
.getStore();
docMain.doc.clear(); docMain.doc.clear();
docSyncedPage.doc.clear(); docSyncedPage.doc.clear();

View File

@@ -4,8 +4,8 @@ import * as Y from 'yjs';
import type { InitFn } from './utils.js'; import type { InitFn } from './utils.js';
export const versionMismatch: InitFn = (collection: Workspace, id: string) => { export const versionMismatch: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc({ id }); const doc = collection.createDoc(id).getStore({ id });
const tempDoc = collection.createDoc({ id: 'tempDoc' }); const tempDoc = collection.createDoc('tempDoc').getStore();
doc.load(); doc.load();
tempDoc.load(() => { tempDoc.load(() => {

View File

@@ -66,8 +66,8 @@ export function createStarterDocCollection() {
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
}); });

View File

@@ -52,7 +52,7 @@ export function createTestEditor(store: Store, workspace: Workspace) {
editor.std editor.std
.get(RefNodeSlotsProvider) .get(RefNodeSlotsProvider)
.docLinkClicked.subscribe(({ pageId: docId }) => { .docLinkClicked.subscribe(({ pageId: docId }) => {
const target = workspace.getDoc(docId); const target = workspace.getDoc(docId)?.getStore();
if (!target) { if (!target) {
throw new Error(`Failed to jump to doc ${docId}`); throw new Error(`Failed to jump to doc ${docId}`);
} }

View File

@@ -26,7 +26,7 @@ async function getStore(
): Promise<Store> { ): Promise<Store> {
if (!noInit) { if (!noInit) {
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'doc:home' }); const doc = collection.createDoc('doc:home').getStore();
window.doc = doc; window.doc = doc;
return doc; return doc;
} }

View File

@@ -477,7 +477,8 @@ const SAVE_AS_DOC = {
toast: 'New doc created', toast: 'New doc created',
handler: (host: EditorHost, content: string) => { handler: (host: EditorHost, content: string) => {
reportResponse('result:add-page'); reportResponse('result:add-page');
const newDoc = host.doc.workspace.createDoc(); const doc = host.doc.workspace.createDoc();
const newDoc = doc.getStore();
newDoc.load(); newDoc.load();
const rootId = newDoc.addBlock('affine:page'); const rootId = newDoc.addBlock('affine:page');
newDoc.addBlock('affine:surface', {}, rootId); newDoc.addBlock('affine:surface', {}, rootId);
@@ -533,7 +534,7 @@ const CREATE_AS_LINKED_DOC = {
} }
// Create a new doc and add the content to it // Create a new doc and add the content to it
const newDoc = host.doc.workspace.createDoc(); const newDoc = host.doc.workspace.createDoc().getStore();
newDoc.load(); newDoc.load();
const rootId = newDoc.addBlock('affine:page'); const rootId = newDoc.addBlock('affine:page');
newDoc.addBlock('affine:surface', {}, rootId); newDoc.addBlock('affine:surface', {}, rootId);

View File

@@ -219,7 +219,7 @@ export class AISlidesRenderer extends WithDisposable(LitElement) {
id: 'SLIDES_PREVIEW', id: 'SLIDES_PREVIEW',
}); });
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc(); const doc = collection.createDoc().getStore();
doc.load(() => { doc.load(() => {
const pageBlockId = doc.addBlock('affine:page', {}); const pageBlockId = doc.addBlock('affine:page', {});

View File

@@ -30,7 +30,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
`; `;
const collection = new TestWorkspace(); const collection = new TestWorkspace();
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc(); const doc = collection.createDoc().getStore();
const nodes = markdownToMindmap(markdown, doc, provider); const nodes = markdownToMindmap(markdown, doc, provider);
expect(nodes).toEqual({ expect(nodes).toEqual({
@@ -68,7 +68,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
`; `;
const collection = new TestWorkspace(); const collection = new TestWorkspace();
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc(); const doc = collection.createDoc().getStore();
const nodes = markdownToMindmap(markdown, doc, provider); const nodes = markdownToMindmap(markdown, doc, provider);
expect(nodes).toEqual({ expect(nodes).toEqual({
@@ -100,7 +100,7 @@ describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
const markdown = ''; const markdown = '';
const collection = new TestWorkspace(); const collection = new TestWorkspace();
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc(); const doc = collection.createDoc().getStore();
const nodes = markdownToMindmap(markdown, doc, provider); const nodes = markdownToMindmap(markdown, doc, provider);
expect(nodes).toEqual(null); expect(nodes).toEqual(null);

View File

@@ -103,7 +103,8 @@ export class MiniMindmapPreview extends WithDisposable(LitElement) {
id: 'MINI_MINDMAP_TEMPORARY', id: 'MINI_MINDMAP_TEMPORARY',
}); });
collection.meta.initialize(); collection.meta.initialize();
const doc = collection.createDoc({ id: 'doc:home' }).load(); const doc = collection.createDoc('doc:home').getStore();
doc.load();
const rootId = doc.addBlock('affine:page', {}); const rootId = doc.addBlock('affine:page', {});
const surfaceId = doc.addBlock('affine:surface', {}, rootId); const surfaceId = doc.addBlock('affine:surface', {}, rootId);
const surface = doc.getModelById(surfaceId) as SurfaceBlockModel; const surface = doc.getModelById(surfaceId) as SurfaceBlockModel;

View File

@@ -684,7 +684,7 @@ const embedLinkedDocToolbarConfig = {
ctx.hide(); ctx.hide();
const model = block.model; const model = block.model;
const doc = ctx.workspace.getDoc(model.props.pageId); const doc = ctx.workspace.getDoc(model.props.pageId)?.getStore();
const abortController = new AbortController(); const abortController = new AbortController();
abortController.signal.onabort = () => ctx.show(); abortController.signal.onabort = () => ctx.show();

View File

@@ -165,8 +165,8 @@ export async function markDownToDoc(
schema, schema,
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares, middlewares,

View File

@@ -174,11 +174,9 @@ export const useSnapshotPage = (
docCollection.id, docCollection.id,
workspacesService.getWorkspaceFlavourProvider(affineWorkspace.meta) workspacesService.getWorkspaceFlavourProvider(affineWorkspace.meta)
); );
let page = historyShellWorkspace.getDoc(pageId); let page = historyShellWorkspace.getDoc(pageId)?.getStore();
if (!page && snapshot) { if (!page && snapshot) {
page = historyShellWorkspace.createDoc({ page = historyShellWorkspace.createDoc(pageId).getStore();
id: pageId,
});
page.readonly = true; page.readonly = true;
const spaceDoc = page.spaceDoc; const spaceDoc = page.spaceDoc;
page.load(() => { page.load(() => {

View File

@@ -65,8 +65,8 @@ async function exportDoc(
schema: getAFFiNEWorkspaceSchema(), schema: getAFFiNEWorkspaceSchema(),
blobCRUD: doc.workspace.blobSync, blobCRUD: doc.workspace.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => doc.workspace.createDoc({ id }), create: (id: string) => doc.workspace.createDoc(id).getStore({ id }),
get: (id: string) => doc.workspace.getDoc(id), get: (id: string) => doc.workspace.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => doc.workspace.removeDoc(id), delete: (id: string) => doc.workspace.removeDoc(id),
}, },
middlewares: [ middlewares: [

View File

@@ -9,7 +9,7 @@ import { useCallback } from 'react';
export function useReferenceLinkHelper(docCollection: Workspace) { export function useReferenceLinkHelper(docCollection: Workspace) {
const addReferenceLink = useCallback( const addReferenceLink = useCallback(
(pageId: string, referenceId: string) => { (pageId: string, referenceId: string) => {
const page = docCollection?.getDoc(pageId); const page = docCollection?.getDoc(pageId)?.getStore();
if (!page) { if (!page) {
return; return;
} }

View File

@@ -5,7 +5,7 @@ export function useDocCollectionHelper(docCollection: Workspace) {
return useMemo( return useMemo(
() => ({ () => ({
createDoc: (pageId?: string): Store => { createDoc: (pageId?: string): Store => {
return docCollection.createDoc({ id: pageId }); return docCollection.createDoc(pageId).getStore();
}, },
}), }),
[docCollection] [docCollection]

View File

@@ -9,8 +9,8 @@ export function useDocCollectionPage(
docCollection: Workspace, docCollection: Workspace,
pageId: string | null pageId: string | null
): Store | null { ): Store | null {
const [page, setPage] = useState( const [page, setPage] = useState<Store | null>(
pageId ? docCollection.getDoc(pageId) : null pageId ? (docCollection.getDoc(pageId)?.getStore() ?? null) : null
); );
useEffect(() => { useEffect(() => {
@@ -18,7 +18,7 @@ export function useDocCollectionPage(
group.add( group.add(
docCollection.slots.docCreated.subscribe(id => { docCollection.slots.docCreated.subscribe(id => {
if (pageId === id) { if (pageId === id) {
setPage(docCollection.getDoc(id)); setPage(docCollection.getDoc(id)?.getStore() ?? null);
} }
}) })
); );

View File

@@ -33,12 +33,16 @@ beforeEach(async () => {
const frameId = page.addBlock('affine:note', {}, pageBlockId); const frameId = page.addBlock('affine:note', {}, pageBlockId);
page.addBlock('affine:paragraph', {}, frameId); page.addBlock('affine:paragraph', {}, frameId);
}; };
await initPage(docCollection.createDoc({ id: 'page0', extensions })); const store = docCollection.createDoc('page0').getStore({ extensions });
await initPage(store);
}); });
describe('useBlockSuitePagePreview', () => { describe('useBlockSuitePagePreview', () => {
test('basic', async () => { test('basic', async () => {
const page = docCollection.getDoc('page0') as Store; const page = docCollection.getDoc('page0')?.getStore();
if (!page) {
throw new Error('Page not found');
}
const id = page.addBlock( const id = page.addBlock(
'affine:paragraph', 'affine:paragraph',
{ {

View File

@@ -9,8 +9,8 @@ export function useDocCollectionPage(
docCollection: Workspace, docCollection: Workspace,
pageId: string | null pageId: string | null
): Store | null { ): Store | null {
const [page, setPage] = useState( const [page, setPage] = useState<Store | null>(
pageId ? docCollection.getDoc(pageId) : null pageId ? (docCollection.getDoc(pageId)?.getStore() ?? null) : null
); );
useEffect(() => { useEffect(() => {
@@ -18,7 +18,7 @@ export function useDocCollectionPage(
group.add( group.add(
docCollection.slots.docCreated.subscribe(id => { docCollection.slots.docCreated.subscribe(id => {
if (pageId === id) { if (pageId === id) {
setPage(docCollection.getDoc(id)); setPage(docCollection.getDoc(id)?.getStore() ?? null);
} }
}) })
); );
@@ -46,7 +46,9 @@ export function useDocCollectionPage(
useEffect(() => { useEffect(() => {
if (page?.id !== pageId) { if (page?.id !== pageId) {
setPage(pageId ? docCollection.getDoc(pageId) : null); setPage(
pageId ? (docCollection.getDoc(pageId)?.getStore() ?? null) : null
);
} }
}, [docCollection, page?.id, pageId]); }, [docCollection, page?.id, pageId]);

View File

@@ -87,8 +87,8 @@ async function initDoc(name: DocName) {
schema: getAFFiNEWorkspaceSchema(), schema: getAFFiNEWorkspaceSchema(),
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [], middlewares: [],

View File

@@ -96,7 +96,7 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
}, },
getDoc: (docId: string) => { getDoc: (docId: string) => {
const doc = workspaceService.workspace.docCollection.getDoc(docId); const doc = workspaceService.workspace.docCollection.getDoc(docId);
return doc; return doc?.getStore() ?? null;
}, },
getReferenceDocs: (docIds: string[]) => { getReferenceDocs: (docIds: string[]) => {
const docs$ = docsSearchService.watchRefsFrom(docIds); const docs$ = docsSearchService.watchRefsFrom(docIds);

View File

@@ -155,7 +155,7 @@ export class DndService extends Service {
getBlocksuiteDndAPI(sourceDocId?: string) { getBlocksuiteDndAPI(sourceDocId?: string) {
const collection = this.workspaceService.workspace.docCollection; const collection = this.workspaceService.workspace.docCollection;
sourceDocId ??= collection.docs.keys().next().value; sourceDocId ??= collection.docs.keys().next().value;
const doc = sourceDocId ? collection.getDoc(sourceDocId) : null; const doc = sourceDocId ? collection.getDoc(sourceDocId)?.getStore() : null;
if (!doc) { if (!doc) {
return null; return null;

View File

@@ -206,8 +206,8 @@ export class DocsService extends Service {
schema: getAFFiNEWorkspaceSchema(), schema: getAFFiNEWorkspaceSchema(),
blobCRUD: collection.blobSync, blobCRUD: collection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => collection.createDoc({ id }), create: (id: string) => collection.createDoc(id).getStore({ id }),
get: (id: string) => collection.getDoc(id), get: (id: string) => collection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => collection.removeDoc(id), delete: (id: string) => collection.removeDoc(id),
}, },
middlewares: [replaceIdMiddleware(collection.idGenerator)], middlewares: [replaceIdMiddleware(collection.idGenerator)],

View File

@@ -21,7 +21,11 @@ export class DocsStore extends Store {
} }
getBlockSuiteDoc(id: string) { getBlockSuiteDoc(id: string) {
return this.workspaceService.workspace.docCollection.getDoc(id); return (
this.workspaceService.workspace.docCollection
.getDoc(id)
?.getStore({ id }) ?? null
);
} }
getBlocksuiteCollection() { getBlocksuiteCollection() {
@@ -29,7 +33,8 @@ export class DocsStore extends Store {
} }
createBlockSuiteDoc() { createBlockSuiteDoc() {
return this.workspaceService.workspace.docCollection.createDoc(); const doc = this.workspaceService.workspace.docCollection.createDoc();
return doc.getStore({ id: doc.id });
} }
watchDocIds() { watchDocIds() {

View File

@@ -192,8 +192,10 @@ function generateMarkdownPreviewBuilder(
schema: getAFFiNEWorkspaceSchema(), schema: getAFFiNEWorkspaceSchema(),
blobCRUD: markdownPreviewDocCollection.blobSync, blobCRUD: markdownPreviewDocCollection.blobSync,
docCRUD: { docCRUD: {
create: (id: string) => markdownPreviewDocCollection.createDoc({ id }), create: (id: string) =>
get: (id: string) => markdownPreviewDocCollection.getDoc(id), markdownPreviewDocCollection.createDoc(id).getStore({ id }),
get: (id: string) =>
markdownPreviewDocCollection.getDoc(id)?.getStore({ id }) ?? null,
delete: (id: string) => markdownPreviewDocCollection.removeDoc(id), delete: (id: string) => markdownPreviewDocCollection.removeDoc(id),
}, },
middlewares: [docLinkBaseURLMiddleware, titleMiddleware], middlewares: [docLinkBaseURLMiddleware, titleMiddleware],

View File

@@ -67,7 +67,7 @@ export class IntegrationWriter extends Entity {
} else { } else {
const collection = workspace.docCollection; const collection = workspace.docCollection;
const doc = collection.getDoc(docId); const doc = collection.getDoc(docId)?.getStore();
if (!doc) throw new Error('Doc not found'); if (!doc) throw new Error('Doc not found');
doc.workspace.meta.setDocMeta(docId, { doc.workspace.meta.setDocMeta(docId, {

View File

@@ -69,7 +69,7 @@ function useImageBlob(
['workspace', 'image', docId, blockId], ['workspace', 'image', docId, blockId],
{ {
fetcher: async ([_, __, pageId, blockId]) => { fetcher: async ([_, __, pageId, blockId]) => {
const page = docCollection.getDoc(pageId); const page = docCollection.getDoc(pageId)?.getStore();
const block = page?.getBlock(blockId); const block = page?.getBlock(blockId);
if (!block) { if (!block) {
return null; return null;

View File

@@ -4,12 +4,9 @@ import {
} from '@blocksuite/affine/global/exceptions'; } from '@blocksuite/affine/global/exceptions';
import { NoopLogger } from '@blocksuite/affine/global/utils'; import { NoopLogger } from '@blocksuite/affine/global/utils';
import { import {
type CreateBlocksOptions,
type Doc, type Doc,
type GetBlocksOptions,
type IdGenerator, type IdGenerator,
nanoid, nanoid,
type Store,
type Workspace, type Workspace,
type WorkspaceMeta, type WorkspaceMeta,
} from '@blocksuite/affine/store'; } from '@blocksuite/affine/store';
@@ -113,9 +110,9 @@ export class WorkspaceImpl implements Workspace {
* If the `init` parameter is passed, a `surface`, `note`, and `paragraph` block * If the `init` parameter is passed, a `surface`, `note`, and `paragraph` block
* will be created in the doc simultaneously. * will be created in the doc simultaneously.
*/ */
createDoc(options: CreateBlocksOptions = {}) { createDoc(docId?: string): Doc {
const { id: docId = this.idGenerator(), query, readonly } = options; const id = docId ?? this.idGenerator();
if (this._hasDoc(docId)) { if (this._hasDoc(id)) {
throw new BlockSuiteError( throw new BlockSuiteError(
ErrorCode.DocCollectionError, ErrorCode.DocCollectionError,
'doc already exists' 'doc already exists'
@@ -123,17 +120,13 @@ export class WorkspaceImpl implements Workspace {
} }
this.meta.addDocMeta({ this.meta.addDocMeta({
id: docId, id,
title: '', title: '',
createDate: Date.now(), createDate: Date.now(),
tags: [], tags: [],
}); });
this.slots.docCreated.next(docId); this.slots.docCreated.next(id);
return this.getDoc(docId, { return this.getDoc(id) as Doc;
id: docId,
query,
readonly,
}) as Store;
} }
private _getDoc(docId: string): Doc | null { private _getDoc(docId: string): Doc | null {
@@ -141,12 +134,9 @@ export class WorkspaceImpl implements Workspace {
return space ?? null; return space ?? null;
} }
getDoc( getDoc(docId: string): Doc | null {
docId: string, const doc = this._getDoc(docId);
options: GetBlocksOptions = { id: docId } return doc;
): Store | null {
const collection = this._getDoc(docId);
return collection?.getStore(options) ?? null;
} }
removeDoc(docId: string) { removeDoc(docId: string) {

View File

@@ -159,7 +159,7 @@ test.describe('Embed synced doc', () => {
const noteId = doc.addBlock('affine:note', {}, rootId); const noteId = doc.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:paragraph', {}, noteId); doc.addBlock('affine:paragraph', {}, noteId);
const doc2 = collection.createDoc({ id: 'doc2' }); const doc2 = collection.createDoc('doc2').getStore();
doc2.load(); doc2.load();
const rootId2 = doc2.addBlock('affine:page', { const rootId2 = doc2.addBlock('affine:page', {
title: new window.$blocksuite.store.Text('Doc 2'), title: new window.$blocksuite.store.Text('Doc 2'),
@@ -174,7 +174,7 @@ test.describe('Embed synced doc', () => {
noteId2 noteId2
); );
const doc3 = collection.createDoc({ id: 'doc3' }); const doc3 = collection.createDoc('doc3').getStore();
doc3.load(); doc3.load();
const rootId3 = doc3.addBlock('affine:page', { const rootId3 = doc3.addBlock('affine:page', {
title: new window.$blocksuite.store.Text('Doc 3'), title: new window.$blocksuite.store.Text('Doc 3'),
@@ -260,7 +260,7 @@ test.describe('Embed synced doc', () => {
// go back to previous doc // go back to previous doc
await page.evaluate(() => { await page.evaluate(() => {
const { collection, editor } = window; const { collection, editor } = window;
editor.doc = collection.getDoc('doc:home')!; editor.doc = collection.getDoc('doc:home')!.getStore();
}); });
const databaseFirstCell = page.locator( const databaseFirstCell = page.locator(

View File

@@ -92,7 +92,7 @@ export async function switchToPage(page: Page, docId?: string) {
docId = docMetas[0].id; docId = docMetas[0].id;
} }
const doc = collection.getDoc(docId); const doc = collection.getDoc(docId)?.getStore();
if (!doc) return; if (!doc) return;
editor.doc = doc; editor.doc = doc;
}, docId); }, docId);

View File

@@ -66,7 +66,7 @@ export enum ConnectorMode {
export async function getNoteRect(page: Page, noteId: string) { export async function getNoteRect(page: Page, noteId: string) {
const xywh: string | null = await page.evaluate( const xywh: string | null = await page.evaluate(
([noteId]) => { ([noteId]) => {
const doc = window.collection.getDoc('doc:home'); const doc = window.collection.getDoc('doc:home')?.getStore();
const block = doc?.getModelById(noteId); const block = doc?.getModelById(noteId);
if (block?.flavour === 'affine:note') { if (block?.flavour === 'affine:note') {
return (block as NoteBlockModel).xywh; return (block as NoteBlockModel).xywh;
@@ -84,7 +84,7 @@ export async function getNoteRect(page: Page, noteId: string) {
export async function getNoteProps(page: Page, noteId: string) { export async function getNoteProps(page: Page, noteId: string) {
const props = await page.evaluate( const props = await page.evaluate(
([id]) => { ([id]) => {
const doc = window.collection.getDoc('doc:home'); const doc = window.collection.getDoc('doc:home')?.getStore();
const block = doc?.getModelById(id); const block = doc?.getModelById(id);
if (block?.flavour === 'affine:note') { if (block?.flavour === 'affine:note') {
return (block as NoteBlockModel).keys.reduce( return (block as NoteBlockModel).keys.reduce(