snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-16 04:01:20 +05:00
commit 702cc698a7
9990 changed files with 1073618 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,136 @@
import {
DatabaseBlockDataSource,
databaseBlockProperties,
} from '@blocksuite/affine/blocks/database';
import {
type DatabaseBlockModel,
type ListType,
type ParagraphType,
} from '@blocksuite/affine/model';
import { Text, type Workspace } from '@blocksuite/affine/store';
import { groupTraitKey } from '@blocksuite/data-view';
import { propertyPresets } from '@blocksuite/data-view/property-presets';
import { viewPresets } from '@blocksuite/data-view/view-presets';
import type { InitFn } from './utils.js';
export const database: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc(id);
const store = doc.getStore();
doc.load(() => {
// Add root block and surface block at root level
const rootId = store.addBlock('affine:page', {
title: new Text('BlockSuite Playground'),
});
store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = store.addBlock('affine:note', {}, rootId);
const pId = store.addBlock('affine:paragraph', {}, noteId);
const model = store.getModelById(pId);
if (!model) {
throw new Error('model is not found');
}
const addDatabase = (title: string, group = true) => {
const databaseId = store.addBlock(
'affine:database',
{
columns: [],
cells: {},
},
noteId
);
const database = store.getModelById(databaseId) as DatabaseBlockModel;
const datasource = new DatabaseBlockDataSource(database);
datasource.viewManager.viewAdd('table');
database.props.title = new Text(title);
const richTextId = datasource.propertyAdd('end', {
type: databaseBlockProperties.richTextColumnConfig.type,
name: 'Rich Text',
});
Object.values([
propertyPresets.multiSelectPropertyConfig,
propertyPresets.datePropertyConfig,
propertyPresets.numberPropertyConfig,
databaseBlockProperties.linkColumnConfig,
propertyPresets.checkboxPropertyConfig,
propertyPresets.progressPropertyConfig,
]).forEach(column => {
datasource.propertyAdd('end', {
type: column.type,
name: column.config.name,
});
});
if (group) {
const groupTrait =
datasource.viewManager.currentView$.value?.traitGet(groupTraitKey);
groupTrait?.changeGroup(database.props.columns[1].id);
}
const paragraphTypes: ParagraphType[] = [
'text',
'quote',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
];
paragraphTypes.forEach(type => {
const id = store.addBlock(
'affine:paragraph',
{ type: type, text: new Text(`Paragraph type ${type}`) },
databaseId
);
if (richTextId) {
datasource.cellValueChange(
id,
richTextId,
new Text(`Paragraph type ${type}`)
);
}
});
const listTypes: ListType[] = ['numbered', 'bulleted', 'todo', 'toggle'];
listTypes.forEach(type => {
const id = store.addBlock(
'affine:list',
{ type: type, text: new Text(`List type ${type}`) },
databaseId
);
if (richTextId) {
datasource.cellValueChange(
id,
richTextId,
new Text(`List type ${type}`)
);
}
});
// Add a paragraph after database
store.addBlock('affine:paragraph', {}, noteId);
store.addBlock('affine:paragraph', {}, noteId);
store.addBlock('affine:paragraph', {}, noteId);
store.addBlock('affine:paragraph', {}, noteId);
store.addBlock('affine:paragraph', {}, noteId);
datasource.viewManager.viewAdd(viewPresets.kanbanViewMeta.type);
store.resetHistory();
};
// 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,55 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js';
export const embed: InitFn = (collection: Workspace, id: string) => {
const doc = collection.getDoc(id) ?? collection.createDoc(id);
const store = doc.getStore();
doc.clear();
doc.load(() => {
// Add root block and surface block at root level
const rootId = store.addBlock('affine:page', {
title: new Text(),
});
const surfaceId = store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = store.addBlock('affine:note', {}, rootId);
// Add paragraph block inside note block
store.addBlock('affine:paragraph', {}, noteId);
store.addBlock(
'affine:embed-github',
{
url: 'https://github.com/toeverything/AFFiNE/pull/5453',
},
noteId
);
store.addBlock(
'affine:embed-github',
{
url: 'https://www.github.com/toeverything/blocksuite/pull/5927',
style: 'vertical',
xywh: '[0, 400, 364, 390]',
},
surfaceId
);
store.addBlock(
'affine:embed-github',
{
url: 'https://github.com/Milkdown/milkdown/pull/1215',
xywh: '[500, 400, 752, 116]',
},
surfaceId
);
store.addBlock('affine:paragraph', {}, noteId);
});
store.resetHistory();
};
embed.id = 'embed';
embed.displayName = 'Example for embed blocks';
embed.description = 'Example for embed blocks';

View File

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

View File

@@ -0,0 +1,99 @@
import type { SerializedXYWH } from '@blocksuite/affine/global/gfx';
import {
Boxed,
nanoid,
native2Y,
Text,
type Workspace,
} from '@blocksuite/affine/store';
import { DEFAULT_ROUGHNESS } from '@blocksuite/affine-model';
import type * as Y from 'yjs';
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: Workspace, id: string) => {
const count = Number(params.get('count')) || SHAPES_COUNT;
const enableShapes = !!params.get('shapes');
const doc = collection.createDoc(id);
const store = doc.getStore();
doc.load(() => {
// Add root block and surface block at root level
const rootId = store.addBlock('affine:page', {
title: new Text(),
});
const surfaceBlocks = enableShapes ? createShapes(count) : {};
store.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 = store.addBlock(
'affine:note',
{
xywh: `[${x}, ${y}, 100, 50]` as SerializedXYWH,
},
rootId
);
// Add paragraph block inside note block
store.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,36 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js';
const params = new URLSearchParams(location.search);
export const heavy: InitFn = (collection: Workspace, docId: string) => {
const count = Number(params.get('count')) || 1000;
const doc = collection.createDoc(docId);
const store = doc.getStore();
doc.load(() => {
// Add root block and surface block at root level
const rootId = store.addBlock('affine:page', {
title: new Text(),
});
store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
const noteId = store.addBlock('affine:note', {}, rootId);
for (let i = 0; i < count; i++) {
// Add paragraph block inside note block
store.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,82 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import type { InitFn } from './utils.js';
export const linked: InitFn = (collection: Workspace, id: string) => {
const docA =
collection.getDoc(id)?.getStore({ id }) ??
collection.createDoc(id).getStore({ id });
const docBId = 'doc:linked-page';
const docB = collection.createDoc(docBId).getStore();
const docCId = 'doc:linked-edgeless';
const docC = collection.createDoc(docCId).getStore();
docA.doc.clear();
docB.doc.clear();
docC.doc.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,72 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import { createTestEditor } from '../utils/extensions.js';
import type { InitFn } from './utils.js';
export const multiEditor: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc(id).getStore({ 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 = createTestEditor(doc, collection);
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: Workspace,
docId: string
) => {
const doc = collection.createDoc(docId).getStore();
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 = createTestEditor(doc, collection);
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,39 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import * as Y from 'yjs';
import type { InitFn } from './utils.js';
export const pendingStructs: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc(id).getStore({ id });
const tempDoc = collection.createDoc('tempDoc').getStore();
doc.load();
tempDoc.load(() => {
const rootId = tempDoc.addBlock('affine:page', {
title: new Text('Pending Structs'),
});
const vec = Y.encodeStateVector(tempDoc.spaceDoc);
// To avoid pending structs, uncomment the following line
// const update = 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 = Y.encodeStateAsUpdate(tempDoc.spaceDoc, vec);
// To avoid pending structs, uncomment the following line
// Y.applyUpdate(doc.spaceDoc, update);
Y.applyUpdate(doc.spaceDoc, diff);
});
};
pendingStructs.id = 'pending-structs';
pendingStructs.displayName = 'Pending Structs';
pendingStructs.description = 'Doc with pending structs';

View File

@@ -0,0 +1,48 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
import { getTestStoreManager } from '@blocksuite/integration-test/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: Workspace, id: string) => {
let doc = collection.getDoc(id);
const hasDoc = !!doc;
if (!doc) {
doc = collection.createDoc(id);
}
const store = doc.getStore({ id });
store.load();
// Run only once on all clients.
let noteId: string;
if (!hasDoc) {
// Add root block and surface block at root level
const rootId = store.addBlock('affine:page', {
title: new Text('BlockSuite Playground'),
});
store.addBlock('affine:surface', {}, rootId);
// Add note block inside root block
noteId = store.addBlock(
'affine:note',
{ xywh: '[0, 100, 800, 640]' },
rootId
);
// Import preset markdown content inside note block
await MarkdownTransformer.importMarkdownToBlock({
doc: store,
blockId: noteId,
markdown: presetMarkdown,
extensions: getTestStoreManager().get('store'),
});
}
store.resetHistory();
};
preset.id = 'preset';
preset.displayName = 'BlockSuite Starter';
preset.description = 'Start from friendly introduction';

View File

@@ -0,0 +1,175 @@
import { Text, type Workspace } from '@blocksuite/affine/store';
import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc';
import { getTestStoreManager } from '@blocksuite/integration-test/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: Workspace, id: string) => {
const docMain =
collection.getDoc(id)?.getStore({ id }) ??
collection.createDoc(id).getStore({ id });
const docSyncedPageId = 'doc:synced-page';
const docSyncedPage = collection.createDoc(docSyncedPageId).getStore();
const docSyncedEdgelessId = 'doc:synced-edgeless';
const docSyncedEdgeless = collection
.createDoc(docSyncedEdgelessId)
.getStore();
docMain.doc.clear();
docSyncedPage.doc.clear();
docSyncedEdgeless.doc.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,
extensions: getTestStoreManager().get('store'),
}).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,
extensions: getTestStoreManager().get('store'),
}).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,
extensions: getTestStoreManager().get('store'),
})
.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 { Workspace } from '@blocksuite/affine/store';
export interface InitFn {
(collection: Workspace, docId: string): Promise<void> | void;
id: string;
displayName: string;
description: string;
}

View File

@@ -0,0 +1,36 @@
import type { Workspace } from '@blocksuite/affine/store';
import * as Y from 'yjs';
import type { InitFn } from './utils.js';
export const versionMismatch: InitFn = (collection: Workspace, id: string) => {
const doc = collection.createDoc(id).getStore({ id });
const tempDoc = collection.createDoc('tempDoc').getStore();
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 = Y.encodeStateAsUpdate(tempDoc.spaceDoc);
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';