mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
21
blocksuite/playground/apps/starter/data/affine-snapshot.ts
Normal file
21
blocksuite/playground/apps/starter/data/affine-snapshot.ts
Normal 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';
|
||||
160
blocksuite/playground/apps/starter/data/database.ts
Normal file
160
blocksuite/playground/apps/starter/data/database.ts
Normal 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';
|
||||
54
blocksuite/playground/apps/starter/data/embed.ts
Normal file
54
blocksuite/playground/apps/starter/data/embed.ts
Normal 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';
|
||||
28
blocksuite/playground/apps/starter/data/empty.ts
Normal file
28
blocksuite/playground/apps/starter/data/empty.ts
Normal 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';
|
||||
101
blocksuite/playground/apps/starter/data/heavy-whiteboard.ts
Normal file
101
blocksuite/playground/apps/starter/data/heavy-whiteboard.ts
Normal 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';
|
||||
35
blocksuite/playground/apps/starter/data/heavy.ts
Normal file
35
blocksuite/playground/apps/starter/data/heavy.ts
Normal 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';
|
||||
19
blocksuite/playground/apps/starter/data/index.ts
Normal file
19
blocksuite/playground/apps/starter/data/index.ts
Normal 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';
|
||||
80
blocksuite/playground/apps/starter/data/linked.ts
Normal file
80
blocksuite/playground/apps/starter/data/linked.ts
Normal 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';
|
||||
95
blocksuite/playground/apps/starter/data/multiple-editor.ts
Normal file
95
blocksuite/playground/apps/starter/data/multiple-editor.ts
Normal 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';
|
||||
41
blocksuite/playground/apps/starter/data/pending-structs.ts
Normal file
41
blocksuite/playground/apps/starter/data/pending-structs.ts
Normal 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';
|
||||
36
blocksuite/playground/apps/starter/data/preset.ts
Normal file
36
blocksuite/playground/apps/starter/data/preset.ts
Normal 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';
|
||||
Binary file not shown.
167
blocksuite/playground/apps/starter/data/synced.ts
Normal file
167
blocksuite/playground/apps/starter/data/synced.ts
Normal 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';
|
||||
8
blocksuite/playground/apps/starter/data/utils.ts
Normal file
8
blocksuite/playground/apps/starter/data/utils.ts
Normal 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;
|
||||
}
|
||||
39
blocksuite/playground/apps/starter/data/version-mismatch.ts
Normal file
39
blocksuite/playground/apps/starter/data/version-mismatch.ts
Normal 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';
|
||||
Reference in New Issue
Block a user