chore: merge blocksuite source code (#9213)

This commit is contained in:
Mirone
2024-12-20 15:38:06 +08:00
committed by GitHub
parent 2c9ef916f4
commit 30200ff86d
2031 changed files with 238888 additions and 229 deletions

View File

@@ -0,0 +1,21 @@
import { ZipTransformer } from '@blocksuite/blocks';
import { type DocCollection, Text } from '@blocksuite/store';
export async function affineSnapshot(collection: DocCollection, id: string) {
const doc = collection.createDoc({ id });
doc.load();
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text('Affine Snapshot Test'),
});
doc.addBlock('affine:surface', {}, rootId);
const path = '/apps/starter/data/snapshots/affine-default.zip';
const response = await fetch(path);
const file = await response.blob();
await ZipTransformer.importDocs(collection, file);
}
affineSnapshot.id = 'affine-snapshot';
affineSnapshot.displayName = 'Affine Snapshot Test';
affineSnapshot.description = 'Affine Snapshot Test';

View File

@@ -0,0 +1,160 @@
import {
databaseBlockColumns,
type DatabaseBlockModel,
type ListType,
type ParagraphType,
type ViewBasicDataType,
} from '@blocksuite/blocks';
import { viewPresets } from '@blocksuite/data-view/view-presets';
import { assertExists } from '@blocksuite/global/utils';
import { type DocCollection, Text } from '@blocksuite/store';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { propertyPresets } from '../../../../affine/data-view/src/property-presets';
import type { InitFn } from './utils.js';
export const database: InitFn = (collection: DocCollection, id: string) => {
const doc = collection.createDoc({ id });
doc.awarenessStore.setFlag('enable_database_number_formatting', true);
doc.awarenessStore.setFlag('enable_database_attachment_note', true);
doc.awarenessStore.setFlag('enable_database_full_width', true);
doc.awarenessStore.setFlag('enable_block_query', true);
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text('BlockSuite Playground'),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
const pId = doc.addBlock('affine:paragraph', {}, noteId);
const model = doc.getBlockById(pId);
assertExists(model);
const addDatabase = (title: string, group = true) => {
const databaseId = doc.addBlock(
'affine:database',
{
columns: [],
cells: {},
},
noteId
);
new Promise(resolve => requestAnimationFrame(resolve))
.then(() => {
const service = window.host.std.getService('affine:database');
if (!service) return;
service.initDatabaseBlock(
doc,
model,
databaseId,
viewPresets.tableViewMeta.type,
true
);
const database = doc.getBlockById(databaseId) as DatabaseBlockModel;
database.title = new Text(title);
const richTextId = service.addColumn(
database,
'end',
databaseBlockColumns.richTextColumnConfig.create(
databaseBlockColumns.richTextColumnConfig.config.name
)
);
Object.values([
propertyPresets.multiSelectPropertyConfig,
propertyPresets.datePropertyConfig,
propertyPresets.numberPropertyConfig,
databaseBlockColumns.linkColumnConfig,
propertyPresets.checkboxPropertyConfig,
propertyPresets.progressPropertyConfig,
]).forEach(column => {
service.addColumn(
database,
'end',
column.create(column.config.name)
);
});
service.updateView(database, database.views[0].id, () => {
return {
groupBy: group
? {
columnId: database.columns[1].id,
type: 'groupBy',
name: 'select',
}
: undefined,
} as Partial<ViewBasicDataType>;
});
const paragraphTypes: ParagraphType[] = [
'text',
'quote',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
];
paragraphTypes.forEach(type => {
const id = doc.addBlock(
'affine:paragraph',
{ type: type, text: new Text(`Paragraph type ${type}`) },
databaseId
);
service.updateCell(database, id, {
columnId: richTextId,
value: new Text(`Paragraph type ${type}`),
});
});
const listTypes: ListType[] = [
'numbered',
'bulleted',
'todo',
'toggle',
];
listTypes.forEach(type => {
const id = doc.addBlock(
'affine:list',
{ type: type, text: new Text(`List type ${type}`) },
databaseId
);
service.updateCell(database, id, {
columnId: richTextId,
value: new Text(`List type ${type}`),
});
});
// Add a paragraph after database
doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock('affine:paragraph', {}, noteId);
service.databaseViewAddView(
database,
viewPresets.kanbanViewMeta.type
);
doc.resetHistory();
})
.catch(console.error);
};
// Add database block inside note block
addDatabase('Database 1', false);
addDatabase('Database 2');
addDatabase('Database 3');
addDatabase('Database 4');
addDatabase('Database 5');
addDatabase('Database 6');
addDatabase('Database 7');
addDatabase('Database 8');
addDatabase('Database 9');
addDatabase('Database 10');
});
};
database.id = 'database';
database.displayName = 'Database Example';
database.description = 'Database block basic example';

View File

@@ -0,0 +1,54 @@
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const embed: InitFn = (collection: DocCollection, id: string) => {
const doc = collection.getDoc(id) ?? collection.createDoc({ id });
doc.clear();
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
const surfaceId = doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId);
doc.addBlock(
'affine:embed-github',
{
url: 'https://github.com/toeverything/AFFiNE/pull/5453',
},
noteId
);
doc.addBlock(
'affine:embed-github',
{
url: 'https://www.github.com/toeverything/blocksuite/pull/5927',
style: 'vertical',
xywh: '[0, 400, 364, 390]',
},
surfaceId
);
doc.addBlock(
'affine:embed-github',
{
url: 'https://github.com/Milkdown/milkdown/pull/1215',
xywh: '[500, 400, 752, 116]',
},
surfaceId
);
doc.addBlock('affine:paragraph', {}, noteId);
});
doc.resetHistory();
};
embed.id = 'embed';
embed.displayName = 'Example for embed blocks';
embed.description = 'Example for embed blocks';

View File

@@ -0,0 +1,28 @@
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const empty: InitFn = (collection: DocCollection, id: string) => {
const doc = collection.getDoc(id) ?? collection.createDoc({ id });
doc.clear();
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId);
});
doc.resetHistory();
};
empty.id = 'empty';
empty.displayName = 'Empty Editor';
empty.description = 'Start from empty editor';

View File

@@ -0,0 +1,101 @@
import { DEFAULT_ROUGHNESS } from '@blocksuite/affine-model';
import type { SerializedXYWH } from '@blocksuite/global/utils';
import {
Boxed,
type DocCollection,
nanoid,
native2Y,
Text,
type Y,
} from '@blocksuite/store';
import type { InitFn } from './utils.js';
const SHAPE_TYPES = ['rect', 'triangle', 'ellipse', 'diamond'];
const params = new URLSearchParams(location.search);
function createShapes(count: number): Record<string, unknown> {
const surfaceBlocks: Record<string, unknown> = {};
for (let i = 0; i < count; i++) {
const x = Math.random() * count * 2;
const y = Math.random() * count * 2;
const id = nanoid();
surfaceBlocks[id] = native2Y(
{
id,
index: 'a0',
type: 'shape',
xywh: `[${x},${y},100,100]`,
seed: Math.floor(Math.random() * 2 ** 31),
shapeType: SHAPE_TYPES[Math.floor(Math.random() * 40) % 4],
radius: 0,
filled: false,
fillColor: '--affine-palette-shape-yellow',
strokeWidth: 4,
strokeColor: '--affine-palette-line-yellow',
strokeStyle: 'solid',
roughness: DEFAULT_ROUGHNESS,
},
{ deep: false }
);
}
return surfaceBlocks;
}
const SHAPES_COUNT = 100;
const RANGE = 2000;
export const heavyWhiteboard: InitFn = (
collection: DocCollection,
id: string
) => {
const count = Number(params.get('count')) || SHAPES_COUNT;
const enableShapes = !!params.get('shapes');
const doc = collection.createDoc({ id });
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
const surfaceBlocks = enableShapes ? createShapes(count) : {};
doc.addBlock(
'affine:surface',
{
elements: new Boxed(native2Y(surfaceBlocks, { deep: false })) as Boxed<
Y.Map<Y.Map<unknown>>
>,
},
rootId
);
let i = 0;
// Add note block inside root block
for (i = 0; i < count; i++) {
const x = Math.random() * RANGE - RANGE / 2;
const y = Math.random() * RANGE - RANGE / 2;
const noteId = doc.addBlock(
'affine:note',
{
xywh: `[${x}, ${y}, 100, 50]` as SerializedXYWH,
},
rootId
);
// Add paragraph block inside note block
doc.addBlock(
'affine:paragraph',
{
text: new Text('Note #' + i),
},
noteId
);
}
});
};
heavyWhiteboard.id = 'heavy-whiteboard';
heavyWhiteboard.displayName = 'Heavy Whiteboard';
heavyWhiteboard.description = 'Heavy Whiteboard on 200 elements by default';

View File

@@ -0,0 +1,35 @@
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
const params = new URLSearchParams(location.search);
export const heavy: InitFn = (collection: DocCollection, docId: string) => {
const count = Number(params.get('count')) || 1000;
const doc = collection.createDoc({ id: docId });
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
for (let i = 0; i < count; i++) {
// Add paragraph block inside note block
doc.addBlock(
'affine:paragraph',
{
text: new Text('Hello, world! ' + i),
},
noteId
);
}
});
};
heavy.id = 'heavy';
heavy.displayName = 'Heavy Example';
heavy.description = 'Heavy example on thousands of paragraph blocks';

View File

@@ -0,0 +1,19 @@
/**
* Manually create initial page structure.
* In collaboration mode or on page refresh with local persistence,
* the page structure will be automatically loaded from provider.
* In these cases, these functions should not be called.
*/
export * from './affine-snapshot.js';
export * from './database.js';
export * from './embed.js';
export * from './empty.js';
export * from './heavy.js';
export * from './heavy-whiteboard.js';
export * from './linked.js';
export * from './multiple-editor.js';
export * from './pending-structs.js';
export * from './preset.js';
export * from './synced.js';
export type { InitFn } from './utils.js';
export * from './version-mismatch.js';

View File

@@ -0,0 +1,80 @@
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const linked: InitFn = (collection: DocCollection, id: string) => {
const docA = collection.getDoc(id) ?? collection.createDoc({ id });
const docBId = 'doc:linked-page';
const docB = collection.createDoc({ id: docBId });
const docCId = 'doc:linked-edgeless';
const docC = collection.createDoc({ id: docCId });
docA.clear();
docB.clear();
docC.clear();
docB.load(() => {
const rootId = docB.addBlock('affine:page', {
title: new Text(''),
});
docB.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = docB.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
docB.addBlock('affine:paragraph', {}, noteId);
});
docC.load(() => {
const rootId = docC.addBlock('affine:page', {
title: new Text(''),
});
docC.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = docC.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
docC.addBlock('affine:paragraph', {}, noteId);
});
docA.load();
// Add root block and surface block at root level
const rootId = docA.addBlock('affine:page', {
title: new Text('Doc A'),
});
docA.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = docA.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
docA.addBlock('affine:paragraph', {}, noteId);
docA.addBlock('affine:embed-linked-doc', { pageId: docBId }, noteId);
docA.addBlock(
'affine:embed-linked-doc',
{ pageId: 'doc:deleted-example' },
noteId
);
docA.addBlock('affine:embed-linked-doc', { pageId: docCId }, noteId);
docA.addBlock(
'affine:embed-linked-doc',
{ pageId: 'doc:deleted-example-edgeless' },
noteId
);
docA.resetHistory();
docB.resetHistory();
docC.resetHistory();
};
linked.id = 'linked';
linked.displayName = 'Linked Doc Editor';
linked.description = 'A demo with linked docs';

View File

@@ -0,0 +1,95 @@
import { RefNodeSlotsProvider } from '@blocksuite/affine-components/rich-text';
import { AffineEditorContainer } from '@blocksuite/presets';
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const multiEditor: InitFn = (collection: DocCollection, id: string) => {
const doc = collection.createDoc({ id });
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId);
});
doc.resetHistory();
const app = document.getElementById('app');
if (app) {
const editor = new AffineEditorContainer();
editor.doc = doc;
editor.std
.get(RefNodeSlotsProvider)
.docLinkClicked.on(({ pageId: docId }) => {
const target = collection.getDoc(docId);
if (!target) {
throw new Error(`Failed to jump to doc ${docId}`);
}
target.load();
editor.doc = target;
});
editor.style.borderRight = '1px solid var(--affine-border-color)';
app.append(editor);
app.style.display = 'flex';
}
};
multiEditor.id = 'multiple-editor';
multiEditor.displayName = 'Multiple Editor Example';
multiEditor.description = 'Multiple Editor basic example';
export const multiEditorVertical: InitFn = (
collection: DocCollection,
docId: string
) => {
const doc = collection.createDoc({ id: docId });
doc.load(() => {
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text(),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
doc.addBlock('affine:paragraph', {}, noteId);
});
doc.resetHistory();
const app = document.getElementById('app');
if (app) {
const editor = new AffineEditorContainer();
editor.doc = doc;
editor.std
.get(RefNodeSlotsProvider)
.docLinkClicked.on(({ pageId: docId }) => {
const target = collection.getDoc(docId);
if (!target) {
throw new Error(`Failed to jump to doc ${docId}`);
}
target.load();
editor.doc = target;
});
editor.style.borderBottom = '1px solid var(--affine-border-color)';
app.append(editor);
app.style.display = 'flex';
app.style.flexDirection = 'column';
}
};
multiEditorVertical.id = 'multiple-editor-vertical';
multiEditorVertical.displayName = 'Vertical Multiple Editor Example';
multiEditorVertical.description = 'Multiple Editor vertical layout example';

View File

@@ -0,0 +1,41 @@
import { DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const pendingStructs: InitFn = (
collection: DocCollection,
id: string
) => {
const doc = collection.createDoc({ id });
const tempDoc = collection.createDoc({ id: 'tempDoc' });
doc.load();
tempDoc.load(() => {
const rootId = tempDoc.addBlock('affine:page', {
title: new Text('Pending Structs'),
});
const vec = DocCollection.Y.encodeStateVector(tempDoc.spaceDoc);
// To avoid pending structs, uncomment the following line
// const update = DocCollection.Y.encodeStateAsUpdate(tempDoc.spaceDoc);
tempDoc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = tempDoc.addBlock('affine:note', {}, rootId);
tempDoc.addBlock(
'affine:paragraph',
{
text: new Text('This is a paragraph block'),
},
noteId
);
const diff = DocCollection.Y.encodeStateAsUpdate(tempDoc.spaceDoc, vec);
// To avoid pending structs, uncomment the following line
// DocCollection.Y.applyUpdate(doc.spaceDoc, update);
DocCollection.Y.applyUpdate(doc.spaceDoc, diff);
});
};
pendingStructs.id = 'pending-structs';
pendingStructs.displayName = 'Pending Structs';
pendingStructs.description = 'Doc with pending structs';

View File

@@ -0,0 +1,36 @@
import { MarkdownTransformer } from '@blocksuite/blocks';
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils.js';
const presetMarkdown = `Click the 🔁 button to switch between editors dynamically - they are fully compatible!`;
export const preset: InitFn = async (collection: DocCollection, id: string) => {
const doc = collection.createDoc({ id });
doc.load();
// Add root block and surface block at root level
const rootId = doc.addBlock('affine:page', {
title: new Text('BlockSuite Playground'),
});
doc.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = doc.addBlock(
'affine:note',
{ xywh: '[0, 100, 800, 640]' },
rootId
);
// Import preset markdown content inside note block
await MarkdownTransformer.importMarkdownToBlock({
doc,
blockId: noteId,
markdown: presetMarkdown,
});
doc.resetHistory();
};
preset.id = 'preset';
preset.displayName = 'BlockSuite Starter';
preset.description = 'Start from friendly introduction';

View File

@@ -0,0 +1,167 @@
import { MarkdownTransformer } from '@blocksuite/blocks';
import { type DocCollection, Text } from '@blocksuite/store';
import type { InitFn } from './utils';
const syncedDocMarkdown = `We share some of our findings from developing local-first software prototypes at [Ink & Switch](https://www.inkandswitch.com/) over the course of several years. These experiments test the viability of CRDTs in practice, and explore the user interface challenges for this new data model. Lastly, we suggest some next steps for moving towards local-first software: for researchers, for app developers, and a startup opportunity for entrepreneurs.
This article has also been published [in PDF format](https://www.inkandswitch.com/local-first/static/local-first.pdf) in the proceedings of the [Onward! 2019 conference](https://2019.splashcon.org/track/splash-2019-Onward-Essays). Please cite it as:
> Martin Kleppmann, Adam Wiggins, Peter van Hardenberg, and Mark McGranaghan. Local-first software: you own your data, in spite of the cloud. 2019 ACM SIGPLAN International Symposium on New Ideas, New Paradigms, and Reflections on Programming and Software (Onward!), October 2019, pages 154-178. [doi:10.1145/3359591.3359737](https://doi.org/10.1145/3359591.3359737)
We welcome your feedback: [@inkandswitch](https://twitter.com/inkandswitch) or hello@inkandswitch.com.`;
export const synced: InitFn = (collection: DocCollection, id: string) => {
const docMain = collection.getDoc(id) ?? collection.createDoc({ id });
const docSyncedPageId = 'doc:synced-page';
const docSyncedPage = collection.createDoc({ id: docSyncedPageId });
const docSyncedEdgelessId = 'doc:synced-edgeless';
const docSyncedEdgeless = collection.createDoc({ id: docSyncedEdgelessId });
docMain.clear();
docSyncedPage.clear();
docSyncedEdgeless.clear();
docSyncedPage.load(() => {
// Add root block and surface block at root level
const rootId = docSyncedPage.addBlock('affine:page', {
title: new Text('Synced - Page View'),
});
docSyncedPage.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = docSyncedPage.addBlock('affine:note', {}, rootId);
// Add markdown to note block
MarkdownTransformer.importMarkdownToBlock({
doc: docSyncedPage,
blockId: noteId,
markdown: syncedDocMarkdown,
}).catch(console.error);
});
docSyncedEdgeless.load(() => {
// Add root block and surface block at root level
const rootId = docSyncedEdgeless.addBlock('affine:page', {
title: new Text('Synced - Edgeless View'),
});
docSyncedEdgeless.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = docSyncedEdgeless.addBlock('affine:note', {}, rootId);
// Add markdown to note block
MarkdownTransformer.importMarkdownToBlock({
doc: docSyncedEdgeless,
blockId: noteId,
markdown: syncedDocMarkdown,
}).catch(console.error);
});
docMain.load(() => {
// Add root block and surface block at root level
const rootId = docMain.addBlock('affine:page', {
title: new Text('Home doc, having synced blocks'),
});
const surfaceId = docMain.addBlock('affine:surface', {}, rootId);
const noteId = docMain.addBlock('affine:note', {}, rootId);
// Add markdown to note block
MarkdownTransformer.importMarkdownToBlock({
doc: docMain,
blockId: noteId,
markdown: syncedDocMarkdown,
})
.then(() => {
// Add synced block - self
docMain.addBlock(
'affine:paragraph',
{
text: new Text('Cyclic / Matryoshka synced block 👇'),
type: 'h4',
},
noteId
);
// Add synced block - self
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: id,
},
noteId
);
// Add synced block - page view
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: docSyncedPageId,
},
noteId
);
// Add synced block - edgeless view
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: docSyncedEdgelessId,
},
noteId
);
// Add synced block - page view
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: docSyncedPageId,
xywh: '[-1000, 0, 752, 455]',
},
surfaceId
);
// Add synced block - edgeless view
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: docSyncedEdgelessId,
xywh: '[-1000, 500, 752, 455]',
},
surfaceId
);
// Add synced block - self
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: id,
xywh: '[-1000, 1000, 752, 455]',
},
surfaceId
);
// Add synced block - self
docMain.addBlock(
'affine:embed-synced-doc',
{
pageId: 'doc:deleted-page',
},
noteId
);
})
.catch(console.error);
});
docSyncedEdgeless.resetHistory();
docSyncedPage.resetHistory();
docMain.resetHistory();
};
synced.id = 'synced';
synced.displayName = 'Synced block demo';
synced.description = 'A simple demo for synced block';

View File

@@ -0,0 +1,8 @@
import type { DocCollection } from '@blocksuite/store';
export interface InitFn {
(collection: DocCollection, docId: string): Promise<void> | void;
id: string;
displayName: string;
description: string;
}

View File

@@ -0,0 +1,39 @@
import type { Y } from '@blocksuite/store';
import { DocCollection } from '@blocksuite/store';
import type { InitFn } from './utils.js';
export const versionMismatch: InitFn = (
collection: DocCollection,
id: string
) => {
const doc = collection.createDoc({ id });
const tempDoc = collection.createDoc({ id: 'tempDoc' });
doc.load();
tempDoc.load(() => {
const rootId = tempDoc.addBlock('affine:page', {});
tempDoc.addBlock('affine:surface', {}, rootId);
const noteId = tempDoc.addBlock(
'affine:note',
{ xywh: '[0, 100, 800, 640]' },
rootId
);
const paragraphId = tempDoc.addBlock('affine:paragraph', {}, noteId);
const blocks = tempDoc.spaceDoc.get('blocks') as Y.Map<unknown>;
const paragraph = blocks.get(paragraphId) as Y.Map<unknown>;
paragraph.set('sys:version', (paragraph.get('sys:version') as number) + 1);
const update = DocCollection.Y.encodeStateAsUpdate(tempDoc.spaceDoc);
DocCollection.Y.applyUpdate(doc.spaceDoc, update);
doc.addBlock('affine:paragraph', {}, noteId);
});
collection.removeDoc('tempDoc');
doc.resetHistory();
};
versionMismatch.id = 'version-mismatch';
versionMismatch.displayName = 'Version Mismatch';
versionMismatch.description = 'Error boundary when version mismatch in data';