mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-18 02:26:21 +08:00
refactor(editor): job should not rely on doc collection directly (#9488)
This commit is contained in:
@@ -27,7 +27,7 @@ export const embedSyncedDocBlockHtmlAdapterMatcher: BlockHtmlAdapterMatcher = {
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
const syncedDoc = job.docCRUD.get(syncedDocId);
|
||||
walkerContext.setGlobalContext('hast:html-root-doc', false);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export const embedSyncedDocBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatc
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
const syncedDoc = job.docCRUD.get(syncedDocId);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
if (counter === 1) {
|
||||
|
||||
@@ -31,7 +31,7 @@ export const embedSyncedDocBlockPlainTextAdapterMatcher: BlockPlainTextAdapterMa
|
||||
|
||||
if (type === 'content') {
|
||||
const syncedDocId = o.node.props.pageId as string;
|
||||
const syncedDoc = job.collection.getDoc(syncedDocId);
|
||||
const syncedDoc = job.docCRUD.get(syncedDocId);
|
||||
if (!syncedDoc) return;
|
||||
|
||||
if (counter === 1) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
StdIdentifier,
|
||||
} from '@blocksuite/block-std';
|
||||
import { type Container, createIdentifier } from '@blocksuite/global/di';
|
||||
import { Job, Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||
import { Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||
|
||||
export const DndApiExtensionIdentifier = createIdentifier<DNDAPIExtension>(
|
||||
'AffineDndApiIdentifier'
|
||||
@@ -40,7 +40,7 @@ export class DNDAPIExtension extends Extension {
|
||||
const { docId, flavour = 'affine:embed-linked-doc', blockId } = options;
|
||||
|
||||
const slice = Slice.fromModels(this.std.doc, []);
|
||||
const job = new Job({ collection: this.std.collection });
|
||||
const job = this.std.getJob();
|
||||
const snapshot = job.sliceToSnapshot(slice);
|
||||
if (!snapshot) {
|
||||
console.error('Failed to convert slice to snapshot');
|
||||
|
||||
@@ -3,14 +3,14 @@ import type { JobMiddleware } from '@blocksuite/store';
|
||||
|
||||
export const newIdCrossDoc =
|
||||
(std: BlockStdScope): JobMiddleware =>
|
||||
({ slots, collection }) => {
|
||||
({ slots }) => {
|
||||
let samePage = false;
|
||||
slots.beforeImport.on(payload => {
|
||||
if (payload.type === 'slice') {
|
||||
samePage = payload.snapshot.pageId === std.doc.id;
|
||||
}
|
||||
if (payload.type === 'block' && !samePage) {
|
||||
payload.snapshot.id = collection.idGenerator();
|
||||
payload.snapshot.id = std.collection.idGenerator();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { JobMiddleware } from '@blocksuite/store';
|
||||
|
||||
export const surfaceRefToEmbed =
|
||||
(std: BlockStdScope): JobMiddleware =>
|
||||
({ slots, collection }) => {
|
||||
({ slots }) => {
|
||||
let pageId: string | null = null;
|
||||
slots.beforeImport.on(payload => {
|
||||
if (payload.type === 'slice') {
|
||||
@@ -18,7 +18,7 @@ export const surfaceRefToEmbed =
|
||||
!std.doc.hasBlock(payload.snapshot.id)
|
||||
) {
|
||||
const id = payload.snapshot.id;
|
||||
payload.snapshot.id = collection.idGenerator();
|
||||
payload.snapshot.id = std.collection.idGenerator();
|
||||
payload.snapshot.flavour = 'affine:embed-linked-doc';
|
||||
payload.snapshot.props = {
|
||||
blockId: id,
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
} from '@blocksuite/block-std';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
|
||||
import { Bound, Point } from '@blocksuite/global/utils';
|
||||
import { Job, Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||
import { Slice, type SliceSnapshot } from '@blocksuite/store';
|
||||
|
||||
import { DropIndicator } from '../components/drop-indicator.js';
|
||||
import { AFFINE_DRAG_HANDLE_WIDGET } from '../consts.js';
|
||||
@@ -544,14 +544,11 @@ export class DragEventWatcher {
|
||||
|
||||
private _getJob() {
|
||||
const std = this._std;
|
||||
return new Job({
|
||||
collection: std.collection,
|
||||
middlewares: [
|
||||
newIdCrossDoc(std),
|
||||
reorderList(std),
|
||||
surfaceRefToEmbed(std),
|
||||
],
|
||||
});
|
||||
return std.getJob([
|
||||
newIdCrossDoc(std),
|
||||
reorderList(std),
|
||||
surfaceRefToEmbed(std),
|
||||
]);
|
||||
}
|
||||
|
||||
private _serializeData(slice: Slice, state: DndEventState) {
|
||||
|
||||
@@ -27,5 +27,14 @@ export function createJob(middlewares?: JobMiddleware[]) {
|
||||
const schema = new Schema().register(AffineSchemas);
|
||||
const docCollection = new DocCollection({ schema });
|
||||
docCollection.meta.initialize();
|
||||
return new Job({ collection: docCollection, middlewares: testMiddlewares });
|
||||
return new Job({
|
||||
schema,
|
||||
blobCRUD: docCollection.blobSync,
|
||||
middlewares: testMiddlewares,
|
||||
docCRUD: {
|
||||
create: (id: string) => docCollection.createDoc({ id }),
|
||||
get: (id: string) => docCollection.getDoc(id),
|
||||
delete: (id: string) => docCollection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,8 +45,17 @@ const provider = container.provider();
|
||||
*/
|
||||
async function exportDoc(doc: Doc) {
|
||||
const job = new Job({
|
||||
collection: doc.collection,
|
||||
middlewares: [docLinkBaseURLMiddleware, titleMiddleware],
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.collection.createDoc({ id }),
|
||||
get: (id: string) => doc.collection.getDoc(id),
|
||||
delete: (id: string) => doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(doc.collection.id),
|
||||
titleMiddleware(doc.collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const snapshot = job.docToSnapshot(doc);
|
||||
const adapter = new HtmlAdapter(job, provider);
|
||||
@@ -91,11 +100,17 @@ async function importHTMLToDoc({
|
||||
fileName,
|
||||
}: ImportHTMLToDocOptions) {
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
fileNameMiddleware(fileName),
|
||||
docLinkBaseURLMiddleware,
|
||||
docLinkBaseURLMiddleware(collection.id),
|
||||
],
|
||||
});
|
||||
const htmlAdapter = new HtmlAdapter(job, provider);
|
||||
@@ -147,11 +162,17 @@ async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) {
|
||||
htmlBlobs.map(async ([fileName, blob]) => {
|
||||
const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, '');
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
fileNameMiddleware(fileNameWithoutExt),
|
||||
docLinkBaseURLMiddleware,
|
||||
docLinkBaseURLMiddleware(collection.id),
|
||||
],
|
||||
});
|
||||
const assets = job.assets;
|
||||
|
||||
@@ -51,8 +51,17 @@ type ImportMarkdownZipOptions = {
|
||||
*/
|
||||
async function exportDoc(doc: Doc) {
|
||||
const job = new Job({
|
||||
collection: doc.collection,
|
||||
middlewares: [docLinkBaseURLMiddleware, titleMiddleware],
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.collection.createDoc({ id }),
|
||||
get: (id: string) => doc.collection.getDoc(id),
|
||||
delete: (id: string) => doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(doc.collection.id),
|
||||
titleMiddleware(doc.collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const snapshot = job.docToSnapshot(doc);
|
||||
|
||||
@@ -101,8 +110,17 @@ async function importMarkdownToBlock({
|
||||
blockId,
|
||||
}: ImportMarkdownToBlockOptions) {
|
||||
const job = new Job({
|
||||
collection: doc.collection,
|
||||
middlewares: [defaultImageProxyMiddleware, docLinkBaseURLMiddleware],
|
||||
schema: doc.schema,
|
||||
blobCRUD: doc.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.collection.createDoc({ id }),
|
||||
get: (id: string) => doc.collection.getDoc(id),
|
||||
delete: (id: string) => doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
docLinkBaseURLMiddleware(doc.collection.id),
|
||||
],
|
||||
});
|
||||
const adapter = new MarkdownAdapter(job, provider);
|
||||
const snapshot = await adapter.toSliceSnapshot({
|
||||
@@ -137,11 +155,17 @@ async function importMarkdownToDoc({
|
||||
fileName,
|
||||
}: ImportMarkdownToDocOptions) {
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
fileNameMiddleware(fileName),
|
||||
docLinkBaseURLMiddleware,
|
||||
docLinkBaseURLMiddleware(collection.id),
|
||||
],
|
||||
});
|
||||
const mdAdapter = new MarkdownAdapter(job, provider);
|
||||
@@ -195,11 +219,17 @@ async function importMarkdownZip({
|
||||
markdownBlobs.map(async ([fileName, blob]) => {
|
||||
const fileNameWithoutExt = fileName.replace(/\.[^/.]+$/, '');
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
defaultImageProxyMiddleware,
|
||||
fileNameMiddleware(fileNameWithoutExt),
|
||||
docLinkBaseURLMiddleware,
|
||||
docLinkBaseURLMiddleware(collection.id),
|
||||
],
|
||||
});
|
||||
const assets = job.assets;
|
||||
|
||||
@@ -8,193 +8,197 @@ import type {
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DEFAULT_IMAGE_PROXY_ENDPOINT } from '@blocksuite/affine-shared/consts';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { DeltaOperation, JobMiddleware } from '@blocksuite/store';
|
||||
import type { DeltaOperation, DocMeta, JobMiddleware } from '@blocksuite/store';
|
||||
|
||||
export const replaceIdMiddleware: JobMiddleware = ({ slots, collection }) => {
|
||||
const idMap = new Map<string, string>();
|
||||
slots.afterImport.on(payload => {
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
payload.snapshot.flavour === 'affine:database'
|
||||
) {
|
||||
const model = payload.model as DatabaseBlockModel;
|
||||
Object.keys(model.cells).forEach(cellId => {
|
||||
if (idMap.has(cellId)) {
|
||||
model.cells[idMap.get(cellId)!] = model.cells[cellId];
|
||||
delete model.cells[cellId];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// replace LinkedPage pageId with new id in paragraph blocks
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
['affine:list', 'affine:paragraph'].includes(payload.snapshot.flavour)
|
||||
) {
|
||||
const model = payload.model as ParagraphBlockModel | ListBlockModel;
|
||||
let prev = 0;
|
||||
const delta: DeltaOperation[] = [];
|
||||
for (const d of model.text.toDelta()) {
|
||||
if (d.attributes?.reference?.pageId) {
|
||||
const newId = idMap.get(d.attributes.reference.pageId);
|
||||
if (!newId) {
|
||||
prev += d.insert?.length ?? 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev > 0) {
|
||||
delta.push({ retain: prev });
|
||||
}
|
||||
|
||||
delta.push({
|
||||
retain: d.insert?.length ?? 0,
|
||||
attributes: {
|
||||
reference: {
|
||||
...d.attributes.reference,
|
||||
pageId: newId,
|
||||
},
|
||||
},
|
||||
});
|
||||
prev = 0;
|
||||
} else {
|
||||
prev += d.insert?.length ?? 0;
|
||||
}
|
||||
}
|
||||
if (delta.length > 0) {
|
||||
model.text.applyDelta(delta);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
payload.snapshot.flavour === 'affine:surface-ref'
|
||||
) {
|
||||
const model = payload.model as SurfaceRefBlockModel;
|
||||
const original = model.reference;
|
||||
// If there exists a replacement, replace the reference with the new id.
|
||||
// Otherwise,
|
||||
// 1. If the reference is an affine:frame not in doc, generate a new id.
|
||||
// 2. If the reference is graph, keep the original id.
|
||||
if (idMap.has(original)) {
|
||||
model.reference = idMap.get(original)!;
|
||||
} else if (
|
||||
model.refFlavour === 'affine:frame' &&
|
||||
!model.doc.hasBlock(original)
|
||||
export const replaceIdMiddleware =
|
||||
(idGenerator: () => string): JobMiddleware =>
|
||||
({ slots, docCRUD }) => {
|
||||
const idMap = new Map<string, string>();
|
||||
slots.afterImport.on(payload => {
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
payload.snapshot.flavour === 'affine:database'
|
||||
) {
|
||||
const newId = collection.idGenerator();
|
||||
idMap.set(original, newId);
|
||||
model.reference = newId;
|
||||
const model = payload.model as DatabaseBlockModel;
|
||||
Object.keys(model.cells).forEach(cellId => {
|
||||
if (idMap.has(cellId)) {
|
||||
model.cells[idMap.get(cellId)!] = model.cells[cellId];
|
||||
delete model.cells[cellId];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@fundon): process linked block/element
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
['affine:embed-linked-doc', 'affine:embed-synced-doc'].includes(
|
||||
payload.snapshot.flavour
|
||||
)
|
||||
) {
|
||||
const model = payload.model as EmbedLinkedDocModel | EmbedSyncedDocModel;
|
||||
const original = model.pageId;
|
||||
// If the pageId is not in the doc, generate a new id.
|
||||
// If we already have a replacement, use it.
|
||||
if (!collection.getDoc(original)) {
|
||||
if (idMap.has(original)) {
|
||||
model.pageId = idMap.get(original)!;
|
||||
} else {
|
||||
const newId = collection.idGenerator();
|
||||
idMap.set(original, newId);
|
||||
model.pageId = newId;
|
||||
// replace LinkedPage pageId with new id in paragraph blocks
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
['affine:list', 'affine:paragraph'].includes(payload.snapshot.flavour)
|
||||
) {
|
||||
const model = payload.model as ParagraphBlockModel | ListBlockModel;
|
||||
let prev = 0;
|
||||
const delta: DeltaOperation[] = [];
|
||||
for (const d of model.text.toDelta()) {
|
||||
if (d.attributes?.reference?.pageId) {
|
||||
const newId = idMap.get(d.attributes.reference.pageId);
|
||||
if (!newId) {
|
||||
prev += d.insert?.length ?? 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev > 0) {
|
||||
delta.push({ retain: prev });
|
||||
}
|
||||
|
||||
delta.push({
|
||||
retain: d.insert?.length ?? 0,
|
||||
attributes: {
|
||||
reference: {
|
||||
...d.attributes.reference,
|
||||
pageId: newId,
|
||||
},
|
||||
},
|
||||
});
|
||||
prev = 0;
|
||||
} else {
|
||||
prev += d.insert?.length ?? 0;
|
||||
}
|
||||
}
|
||||
if (delta.length > 0) {
|
||||
model.text.applyDelta(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
slots.beforeImport.on(payload => {
|
||||
if (payload.type === 'page') {
|
||||
if (idMap.has(payload.snapshot.meta.id)) {
|
||||
payload.snapshot.meta.id = idMap.get(payload.snapshot.meta.id)!;
|
||||
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
payload.snapshot.flavour === 'affine:surface-ref'
|
||||
) {
|
||||
const model = payload.model as SurfaceRefBlockModel;
|
||||
const original = model.reference;
|
||||
// If there exists a replacement, replace the reference with the new id.
|
||||
// Otherwise,
|
||||
// 1. If the reference is an affine:frame not in doc, generate a new id.
|
||||
// 2. If the reference is graph, keep the original id.
|
||||
if (idMap.has(original)) {
|
||||
model.reference = idMap.get(original)!;
|
||||
} else if (
|
||||
model.refFlavour === 'affine:frame' &&
|
||||
!model.doc.hasBlock(original)
|
||||
) {
|
||||
const newId = idGenerator();
|
||||
idMap.set(original, newId);
|
||||
model.reference = newId;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@fundon): process linked block/element
|
||||
if (
|
||||
payload.type === 'block' &&
|
||||
['affine:embed-linked-doc', 'affine:embed-synced-doc'].includes(
|
||||
payload.snapshot.flavour
|
||||
)
|
||||
) {
|
||||
const model = payload.model as
|
||||
| EmbedLinkedDocModel
|
||||
| EmbedSyncedDocModel;
|
||||
const original = model.pageId;
|
||||
// If the pageId is not in the doc, generate a new id.
|
||||
// If we already have a replacement, use it.
|
||||
if (!docCRUD.get(original)) {
|
||||
if (idMap.has(original)) {
|
||||
model.pageId = idMap.get(original)!;
|
||||
} else {
|
||||
const newId = idGenerator();
|
||||
idMap.set(original, newId);
|
||||
model.pageId = newId;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
slots.beforeImport.on(payload => {
|
||||
if (payload.type === 'page') {
|
||||
if (idMap.has(payload.snapshot.meta.id)) {
|
||||
payload.snapshot.meta.id = idMap.get(payload.snapshot.meta.id)!;
|
||||
return;
|
||||
}
|
||||
const newId = idGenerator();
|
||||
idMap.set(payload.snapshot.meta.id, newId);
|
||||
payload.snapshot.meta.id = newId;
|
||||
return;
|
||||
}
|
||||
const newId = collection.idGenerator();
|
||||
idMap.set(payload.snapshot.meta.id, newId);
|
||||
payload.snapshot.meta.id = newId;
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.type === 'block') {
|
||||
const { snapshot } = payload;
|
||||
if (snapshot.flavour === 'affine:page') {
|
||||
const index = snapshot.children.findIndex(
|
||||
c => c.flavour === 'affine:surface'
|
||||
);
|
||||
if (index !== -1) {
|
||||
const [surface] = snapshot.children.splice(index, 1);
|
||||
snapshot.children.push(surface);
|
||||
if (payload.type === 'block') {
|
||||
const { snapshot } = payload;
|
||||
if (snapshot.flavour === 'affine:page') {
|
||||
const index = snapshot.children.findIndex(
|
||||
c => c.flavour === 'affine:surface'
|
||||
);
|
||||
if (index !== -1) {
|
||||
const [surface] = snapshot.children.splice(index, 1);
|
||||
snapshot.children.push(surface);
|
||||
}
|
||||
}
|
||||
|
||||
const original = snapshot.id;
|
||||
let newId: string;
|
||||
if (idMap.has(original)) {
|
||||
newId = idMap.get(original)!;
|
||||
} else {
|
||||
newId = idGenerator();
|
||||
idMap.set(original, newId);
|
||||
}
|
||||
snapshot.id = newId;
|
||||
|
||||
if (snapshot.flavour === 'affine:surface') {
|
||||
// Generate new IDs for images and frames in advance.
|
||||
snapshot.children.forEach(child => {
|
||||
const original = child.id;
|
||||
if (idMap.has(original)) {
|
||||
newId = idMap.get(original)!;
|
||||
} else {
|
||||
newId = idGenerator();
|
||||
idMap.set(original, newId);
|
||||
}
|
||||
});
|
||||
|
||||
Object.entries(
|
||||
snapshot.props.elements as Record<string, Record<string, unknown>>
|
||||
).forEach(([_, value]) => {
|
||||
switch (value.type) {
|
||||
case 'connector': {
|
||||
let connection = value.source as Record<string, string>;
|
||||
if (idMap.has(connection.id)) {
|
||||
const newId = idMap.get(connection.id);
|
||||
assertExists(newId, 'reference id must exist');
|
||||
connection.id = newId;
|
||||
}
|
||||
connection = value.target as Record<string, string>;
|
||||
if (idMap.has(connection.id)) {
|
||||
const newId = idMap.get(connection.id);
|
||||
assertExists(newId, 'reference id must exist');
|
||||
connection.id = newId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'group': {
|
||||
const json = (value.children as Record<string, unknown>)
|
||||
.json as Record<string, unknown>;
|
||||
Object.entries(json).forEach(([key, value]) => {
|
||||
if (idMap.has(key)) {
|
||||
delete json[key];
|
||||
const newKey = idMap.get(key);
|
||||
assertExists(newKey, 'reference id must exist');
|
||||
json[newKey] = value;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const original = snapshot.id;
|
||||
let newId: string;
|
||||
if (idMap.has(original)) {
|
||||
newId = idMap.get(original)!;
|
||||
} else {
|
||||
newId = collection.idGenerator();
|
||||
idMap.set(original, newId);
|
||||
}
|
||||
snapshot.id = newId;
|
||||
|
||||
if (snapshot.flavour === 'affine:surface') {
|
||||
// Generate new IDs for images and frames in advance.
|
||||
snapshot.children.forEach(child => {
|
||||
const original = child.id;
|
||||
if (idMap.has(original)) {
|
||||
newId = idMap.get(original)!;
|
||||
} else {
|
||||
newId = collection.idGenerator();
|
||||
idMap.set(original, newId);
|
||||
}
|
||||
});
|
||||
|
||||
Object.entries(
|
||||
snapshot.props.elements as Record<string, Record<string, unknown>>
|
||||
).forEach(([_, value]) => {
|
||||
switch (value.type) {
|
||||
case 'connector': {
|
||||
let connection = value.source as Record<string, string>;
|
||||
if (idMap.has(connection.id)) {
|
||||
const newId = idMap.get(connection.id);
|
||||
assertExists(newId, 'reference id must exist');
|
||||
connection.id = newId;
|
||||
}
|
||||
connection = value.target as Record<string, string>;
|
||||
if (idMap.has(connection.id)) {
|
||||
const newId = idMap.get(connection.id);
|
||||
assertExists(newId, 'reference id must exist');
|
||||
connection.id = newId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'group': {
|
||||
const json = (value.children as Record<string, unknown>)
|
||||
.json as Record<string, unknown>;
|
||||
Object.entries(json).forEach(([key, value]) => {
|
||||
if (idMap.has(key)) {
|
||||
delete json[key];
|
||||
const newKey = idMap.get(key);
|
||||
assertExists(newKey, 'reference id must exist');
|
||||
json[newKey] = value;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const customImageProxyMiddleware = (
|
||||
imageProxyURL: string
|
||||
@@ -204,46 +208,52 @@ export const customImageProxyMiddleware = (
|
||||
};
|
||||
};
|
||||
|
||||
const customDocLinkBaseUrlMiddleware = (baseUrl: string): JobMiddleware => {
|
||||
return ({ adapterConfigs, collection }) => {
|
||||
const customDocLinkBaseUrlMiddleware = (
|
||||
baseUrl: string,
|
||||
collectionId: string
|
||||
): JobMiddleware => {
|
||||
return ({ adapterConfigs }) => {
|
||||
const docLinkBaseUrl = baseUrl
|
||||
? `${baseUrl}/workspace/${collection.id}`
|
||||
? `${baseUrl}/workspace/${collectionId}`
|
||||
: '';
|
||||
adapterConfigs.set('docLinkBaseUrl', docLinkBaseUrl);
|
||||
};
|
||||
};
|
||||
|
||||
export const titleMiddleware: JobMiddleware = ({
|
||||
slots,
|
||||
collection,
|
||||
adapterConfigs,
|
||||
}) => {
|
||||
slots.beforeExport.on(() => {
|
||||
for (const meta of collection.meta.docMetas) {
|
||||
adapterConfigs.set('title:' + meta.id, meta.title);
|
||||
}
|
||||
});
|
||||
};
|
||||
export const titleMiddleware =
|
||||
(metas: DocMeta[]): JobMiddleware =>
|
||||
({ slots, adapterConfigs }) => {
|
||||
slots.beforeExport.on(() => {
|
||||
for (const meta of metas) {
|
||||
adapterConfigs.set('title:' + meta.id, meta.title);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const docLinkBaseURLMiddlewareBuilder = (baseUrl: string) => {
|
||||
let middleware = customDocLinkBaseUrlMiddleware(baseUrl);
|
||||
export const docLinkBaseURLMiddlewareBuilder = (
|
||||
baseUrl: string,
|
||||
collectionId: string
|
||||
) => {
|
||||
let middleware = customDocLinkBaseUrlMiddleware(baseUrl, collectionId);
|
||||
return {
|
||||
get: () => middleware,
|
||||
set: (url: string) => {
|
||||
middleware = customDocLinkBaseUrlMiddleware(url);
|
||||
middleware = customDocLinkBaseUrlMiddleware(url, collectionId);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const defaultDocLinkBaseURLMiddlewareBuilder = docLinkBaseURLMiddlewareBuilder(
|
||||
typeof window !== 'undefined' ? window.location.origin : '.'
|
||||
);
|
||||
const defaultDocLinkBaseURLMiddlewareBuilder = (collectionId: string) =>
|
||||
docLinkBaseURLMiddlewareBuilder(
|
||||
typeof window !== 'undefined' ? window.location.origin : '.',
|
||||
collectionId
|
||||
);
|
||||
|
||||
export const docLinkBaseURLMiddleware =
|
||||
defaultDocLinkBaseURLMiddlewareBuilder.get();
|
||||
export const docLinkBaseURLMiddleware = (collectionId: string) =>
|
||||
defaultDocLinkBaseURLMiddlewareBuilder(collectionId).get();
|
||||
|
||||
export const setDocLinkBaseURLMiddleware =
|
||||
defaultDocLinkBaseURLMiddlewareBuilder.set;
|
||||
export const setDocLinkBaseURLMiddleware = (collectionId: string) =>
|
||||
defaultDocLinkBaseURLMiddlewareBuilder(collectionId).set;
|
||||
|
||||
const imageProxyMiddlewareBuilder = () => {
|
||||
let middleware = customImageProxyMiddleware(DEFAULT_IMAGE_PROXY_ENDPOINT);
|
||||
|
||||
@@ -119,7 +119,13 @@ async function importNotionZip({
|
||||
}
|
||||
const pagePromises = Array.from(pagePaths).map(async path => {
|
||||
const job = new Job({
|
||||
collection: collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [defaultImageProxyMiddleware],
|
||||
});
|
||||
const htmlAdapter = new NotionHtmlAdapter(job, provider);
|
||||
|
||||
@@ -7,12 +7,21 @@ import { replaceIdMiddleware, titleMiddleware } from './middlewares.js';
|
||||
|
||||
async function exportDocs(collection: DocCollection, docs: Doc[]) {
|
||||
const zip = new Zip();
|
||||
const job = new Job({ collection });
|
||||
const job = new Job({
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
replaceIdMiddleware(collection.idGenerator),
|
||||
titleMiddleware(collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const snapshots = await Promise.all(docs.map(job.docToSnapshot));
|
||||
|
||||
const collectionInfo = job.collectionInfoToSnapshot();
|
||||
await zip.file('info.json', JSON.stringify(collectionInfo, null, 2));
|
||||
|
||||
await Promise.all(
|
||||
snapshots
|
||||
.filter((snapshot): snapshot is DocSnapshot => !!snapshot)
|
||||
@@ -69,8 +78,17 @@ async function importDocs(collection: DocCollection, imported: Blob) {
|
||||
}
|
||||
|
||||
const job = new Job({
|
||||
collection,
|
||||
middlewares: [replaceIdMiddleware, titleMiddleware],
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
replaceIdMiddleware(collection.idGenerator),
|
||||
titleMiddleware(collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const assetsMap = job.assets;
|
||||
|
||||
|
||||
@@ -59,8 +59,12 @@ export class PageClipboard {
|
||||
const paste = pasteMiddleware(this._std);
|
||||
this._std.clipboard.use(copy);
|
||||
this._std.clipboard.use(paste);
|
||||
this._std.clipboard.use(replaceIdMiddleware);
|
||||
this._std.clipboard.use(titleMiddleware);
|
||||
this._std.clipboard.use(
|
||||
replaceIdMiddleware(this._std.doc.collection.idGenerator)
|
||||
);
|
||||
this._std.clipboard.use(
|
||||
titleMiddleware(this._std.doc.collection.meta.docMetas)
|
||||
);
|
||||
this._std.clipboard.use(defaultImageProxyMiddleware);
|
||||
|
||||
this._disposables.add({
|
||||
@@ -80,8 +84,12 @@ export class PageClipboard {
|
||||
this._std.clipboard.unregisterAdapter('*/*');
|
||||
this._std.clipboard.unuse(copy);
|
||||
this._std.clipboard.unuse(paste);
|
||||
this._std.clipboard.unuse(replaceIdMiddleware);
|
||||
this._std.clipboard.unuse(titleMiddleware);
|
||||
this._std.clipboard.unuse(
|
||||
replaceIdMiddleware(this._std.doc.collection.idGenerator)
|
||||
);
|
||||
this._std.clipboard.unuse(
|
||||
titleMiddleware(this._std.doc.collection.meta.docMetas)
|
||||
);
|
||||
this._std.clipboard.unuse(defaultImageProxyMiddleware);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -369,7 +369,15 @@ export class EdgelessClipboardController extends PageClipboard {
|
||||
if (mayBeSurfaceDataJson !== undefined) {
|
||||
const elementsRawData = JSON.parse(mayBeSurfaceDataJson);
|
||||
const { snapshot, blobs } = elementsRawData;
|
||||
const job = new Job({ collection: this.std.collection });
|
||||
const job = new Job({
|
||||
schema: this.std.collection.schema,
|
||||
blobCRUD: this.std.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.std.collection.createDoc({ id }),
|
||||
get: (id: string) => this.std.collection.getDoc(id),
|
||||
delete: (id: string) => this.std.collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const map = job.assetsManager.getAssets();
|
||||
decodeClipboardBlobs(blobs, map);
|
||||
for (const blobId of map.keys()) {
|
||||
@@ -1366,7 +1374,13 @@ export async function prepareClipboardData(
|
||||
std: BlockStdScope
|
||||
) {
|
||||
const job = new Job({
|
||||
collection: std.collection,
|
||||
schema: std.collection.schema,
|
||||
blobCRUD: std.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => std.collection.createDoc({ id }),
|
||||
get: (id: string) => std.collection.getDoc(id),
|
||||
delete: (id: string) => std.collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const selected = await Promise.all(
|
||||
selectedAll.map(async selected => {
|
||||
|
||||
@@ -87,7 +87,16 @@ export class TemplateJob {
|
||||
type: TemplateType;
|
||||
|
||||
constructor({ model, type, middlewares }: TemplateJobConfig) {
|
||||
this.job = new Job({ collection: model.doc.collection, middlewares: [] });
|
||||
this.job = new Job({
|
||||
schema: model.doc.collection.schema,
|
||||
blobCRUD: model.doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => model.doc.collection.createDoc({ id }),
|
||||
get: (id: string) => model.doc.collection.getDoc(id),
|
||||
delete: (id: string) => model.doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [],
|
||||
});
|
||||
this.model = model;
|
||||
this.type = TEMPLATE_TYPES.includes(type as TemplateType)
|
||||
? (type as TemplateType)
|
||||
|
||||
@@ -40,7 +40,13 @@ export function getSortedCloneElements(elements: GfxModel[]) {
|
||||
export function prepareCloneData(elements: GfxModel[], std: BlockStdScope) {
|
||||
elements = sortEdgelessElements(elements);
|
||||
const job = new Job({
|
||||
collection: std.collection,
|
||||
schema: std.collection.schema,
|
||||
blobCRUD: std.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => std.collection.createDoc({ id }),
|
||||
get: (id: string) => std.collection.getDoc(id),
|
||||
delete: (id: string) => std.collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const res = elements.map(element => {
|
||||
const data = serializeElement(element, elements, job);
|
||||
|
||||
@@ -247,7 +247,15 @@ export const markdownToMindmap = (
|
||||
provider: ServiceProvider
|
||||
) => {
|
||||
let result: Node | null = null;
|
||||
const job = new Job({ collection: doc.collection });
|
||||
const job = new Job({
|
||||
schema: doc.collection.schema,
|
||||
blobCRUD: doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.collection.createDoc({ id }),
|
||||
get: (id: string) => doc.collection.getDoc(id),
|
||||
delete: (id: string) => doc.collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
const markdown = new MarkdownAdapter(job, provider);
|
||||
const ast: Root = markdown['_markdownToAst'](answer);
|
||||
const traverse = (
|
||||
|
||||
@@ -4,10 +4,10 @@ import type {
|
||||
BaseAdapter,
|
||||
BlockSnapshot,
|
||||
Doc,
|
||||
Job,
|
||||
JobMiddleware,
|
||||
Slice,
|
||||
} from '@blocksuite/store';
|
||||
import { Job } from '@blocksuite/store';
|
||||
import DOMPurify from 'dompurify';
|
||||
import type { RootContentMap } from 'hast';
|
||||
import * as lz from 'lz-string';
|
||||
@@ -286,10 +286,7 @@ export class Clipboard extends LifeCycleWatcher {
|
||||
}
|
||||
|
||||
private _getJob() {
|
||||
return new Job({
|
||||
middlewares: this._jobMiddlewares,
|
||||
collection: this.std.collection,
|
||||
});
|
||||
return this.std.getJob(this._jobMiddlewares);
|
||||
}
|
||||
|
||||
readFromClipboard(clipboardData: DataTransfer) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ServiceProvider } from '@blocksuite/global/di';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import type { Doc } from '@blocksuite/store';
|
||||
import { type Doc, Job, type JobMiddleware } from '@blocksuite/store';
|
||||
|
||||
import { Clipboard } from '../clipboard/index.js';
|
||||
import { CommandManager } from '../command/index.js';
|
||||
@@ -168,6 +168,19 @@ export class BlockStdScope {
|
||||
return this.getOptional(BlockViewIdentifier(flavour));
|
||||
}
|
||||
|
||||
getJob(middlewares: JobMiddleware[] = []) {
|
||||
return new Job({
|
||||
schema: this.collection.schema,
|
||||
blobCRUD: this.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.collection.createDoc({ id }),
|
||||
get: (id: string) => this.collection.getDoc(id),
|
||||
delete: (id: string) => this.collection.removeDoc(id),
|
||||
},
|
||||
middlewares,
|
||||
});
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._lifeCycleWatchers.forEach(watcher => {
|
||||
watcher.mounted.call(watcher);
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
|
||||
interface BlobCRUD {
|
||||
get: (key: string) => Promise<Blob | null> | Blob | null;
|
||||
set: (key: string, value: Blob) => Promise<string> | string;
|
||||
delete: (key: string) => Promise<void> | void;
|
||||
list: () => Promise<string[]> | string[];
|
||||
}
|
||||
import type { BlobCRUD } from './type.js';
|
||||
|
||||
type AssetsManagerConfig = {
|
||||
blob: BlobCRUD;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||
import { nextTick, Slot } from '@blocksuite/global/utils';
|
||||
|
||||
import type { BlockModel, BlockSchemaType } from '../schema/index.js';
|
||||
import type { Doc, DocCollection, DocMeta } from '../store/index.js';
|
||||
import type { BlockModel, BlockSchemaType, Schema } from '../schema/index.js';
|
||||
import type { Doc } from '../store/index.js';
|
||||
import { AssetsManager } from './assets.js';
|
||||
import { BaseBlockTransformer } from './base.js';
|
||||
import type { DraftModel } from './draft.js';
|
||||
@@ -15,20 +15,22 @@ import type {
|
||||
} from './middleware.js';
|
||||
import { Slice } from './slice.js';
|
||||
import type {
|
||||
BlobCRUD,
|
||||
BlockSnapshot,
|
||||
CollectionInfoSnapshot,
|
||||
DocCRUD,
|
||||
DocSnapshot,
|
||||
SliceSnapshot,
|
||||
} from './type.js';
|
||||
import {
|
||||
BlockSnapshotSchema,
|
||||
CollectionInfoSnapshotSchema,
|
||||
DocSnapshotSchema,
|
||||
SliceSnapshotSchema,
|
||||
} from './type.js';
|
||||
|
||||
export type JobConfig = {
|
||||
collection: DocCollection;
|
||||
schema: Schema;
|
||||
blobCRUD: BlobCRUD;
|
||||
docCRUD: DocCRUD;
|
||||
middlewares?: JobMiddleware[];
|
||||
};
|
||||
|
||||
@@ -52,7 +54,9 @@ export class Job {
|
||||
|
||||
private readonly _assetsManager: AssetsManager;
|
||||
|
||||
private readonly _collection: DocCollection;
|
||||
private readonly _schema: Schema;
|
||||
|
||||
private readonly _docCRUD: DocCRUD;
|
||||
|
||||
private readonly _slots: JobSlots = {
|
||||
beforeImport: new Slot<BeforeImportPayload>(),
|
||||
@@ -74,31 +78,6 @@ export class Job {
|
||||
}
|
||||
};
|
||||
|
||||
collectionInfoToSnapshot = (): CollectionInfoSnapshot | undefined => {
|
||||
try {
|
||||
this._slots.beforeExport.emit({
|
||||
type: 'info',
|
||||
});
|
||||
const collectionMeta = this._getCollectionMeta();
|
||||
const snapshot: CollectionInfoSnapshot = {
|
||||
type: 'info',
|
||||
id: this._collection.id,
|
||||
...collectionMeta,
|
||||
};
|
||||
this._slots.afterExport.emit({
|
||||
type: 'info',
|
||||
snapshot,
|
||||
});
|
||||
CollectionInfoSnapshotSchema.parse(snapshot);
|
||||
|
||||
return snapshot;
|
||||
} catch (error) {
|
||||
console.error(`Error when transforming collection info to snapshot:`);
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
docToSnapshot = (doc: Doc): DocSnapshot | undefined => {
|
||||
try {
|
||||
this._slots.beforeExport.emit({
|
||||
@@ -199,7 +178,7 @@ export class Job {
|
||||
});
|
||||
DocSnapshotSchema.parse(snapshot);
|
||||
const { meta, blocks } = snapshot;
|
||||
const doc = this._collection.createDoc({ id: meta.id });
|
||||
const doc = this.docCRUD.create(meta.id);
|
||||
doc.load();
|
||||
await this.snapshotToBlock(blocks, doc);
|
||||
this._slots.afterImport.emit({
|
||||
@@ -328,19 +307,24 @@ export class Job {
|
||||
return this._assetsManager;
|
||||
}
|
||||
|
||||
get collection() {
|
||||
return this._collection;
|
||||
get schema() {
|
||||
return this._schema;
|
||||
}
|
||||
|
||||
constructor({ collection, middlewares = [] }: JobConfig) {
|
||||
this._collection = collection;
|
||||
this._assetsManager = new AssetsManager({ blob: collection.blobSync });
|
||||
get docCRUD() {
|
||||
return this._docCRUD;
|
||||
}
|
||||
|
||||
constructor({ blobCRUD, schema, docCRUD, middlewares = [] }: JobConfig) {
|
||||
this._assetsManager = new AssetsManager({ blob: blobCRUD });
|
||||
this._schema = schema;
|
||||
this._docCRUD = docCRUD;
|
||||
|
||||
middlewares.forEach(middleware => {
|
||||
middleware({
|
||||
slots: this._slots,
|
||||
docCRUD: this._docCRUD,
|
||||
assetsManager: this._assetsManager,
|
||||
collection: this._collection,
|
||||
adapterConfigs: this._adapterConfigs,
|
||||
});
|
||||
});
|
||||
@@ -465,20 +449,8 @@ export class Job {
|
||||
}
|
||||
}
|
||||
|
||||
private _getCollectionMeta() {
|
||||
const { meta } = this._collection;
|
||||
const { docs } = meta;
|
||||
if (!docs) {
|
||||
throw new BlockSuiteError(ErrorCode.TransformerError, 'Docs not found');
|
||||
}
|
||||
return {
|
||||
properties: {}, // for backward compatibility
|
||||
pages: JSON.parse(JSON.stringify(docs)) as DocMeta[],
|
||||
};
|
||||
}
|
||||
|
||||
private _getSchema(flavour: string) {
|
||||
const schema = this._collection.schema.flavourSchemaMap.get(flavour);
|
||||
const schema = this.schema.flavourSchemaMap.get(flavour);
|
||||
if (!schema) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.TransformerError,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { Slot } from '@blocksuite/global/utils';
|
||||
|
||||
import type { Doc, DocCollection } from '../store/index.js';
|
||||
import type { Doc } from '../store/index.js';
|
||||
import type { AssetsManager } from './assets.js';
|
||||
import type { DraftModel } from './draft.js';
|
||||
import type { Slice } from './slice.js';
|
||||
import type {
|
||||
BlockSnapshot,
|
||||
CollectionInfoSnapshot,
|
||||
DocCRUD,
|
||||
DocSnapshot,
|
||||
SliceSnapshot,
|
||||
} from './type.js';
|
||||
@@ -79,9 +80,9 @@ export type JobSlots = {
|
||||
};
|
||||
|
||||
type JobMiddlewareOptions = {
|
||||
collection: DocCollection;
|
||||
assetsManager: AssetsManager;
|
||||
slots: JobSlots;
|
||||
docCRUD: DocCRUD;
|
||||
adapterConfigs: Map<string, string>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Doc } from '../store/doc/doc.js';
|
||||
import type { DocMeta, DocsPropertiesMeta } from '../store/meta.js';
|
||||
|
||||
export type BlockSnapshot = {
|
||||
@@ -65,3 +66,16 @@ export const DocSnapshotSchema: z.ZodType<DocSnapshot> = z.object({
|
||||
meta: DocMetaSchema,
|
||||
blocks: BlockSnapshotSchema,
|
||||
});
|
||||
|
||||
export interface BlobCRUD {
|
||||
get: (key: string) => Promise<Blob | null> | Blob | null;
|
||||
set: (key: string, value: Blob) => Promise<string> | string;
|
||||
delete: (key: string) => Promise<void> | void;
|
||||
list: () => Promise<string[]> | string[];
|
||||
}
|
||||
|
||||
export interface DocCRUD {
|
||||
create: (id: string) => Doc;
|
||||
get: (id: string) => Doc | null;
|
||||
delete: (id: string) => void;
|
||||
}
|
||||
|
||||
@@ -102,10 +102,19 @@ export class AdaptersPanel extends WithDisposable(ShadowlessElement) {
|
||||
|
||||
private _createJob() {
|
||||
return new Job({
|
||||
collection: this.doc.collection,
|
||||
schema: this.doc.collection.schema,
|
||||
blobCRUD: this.doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.doc.collection.createDoc({ id }),
|
||||
get: (id: string) => this.doc.collection.getDoc(id),
|
||||
delete: (id: string) => this.doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddlewareBuilder('https://example.com').get(),
|
||||
titleMiddleware,
|
||||
docLinkBaseURLMiddlewareBuilder(
|
||||
'https://example.com',
|
||||
this.doc.collection.id
|
||||
).get(),
|
||||
titleMiddleware(this.doc.collection.meta.docMetas),
|
||||
embedSyncedDocMiddleware('content'),
|
||||
defaultImageProxyMiddleware,
|
||||
],
|
||||
|
||||
@@ -242,8 +242,17 @@ export class StarterDebugMenu extends ShadowlessElement {
|
||||
private async _exportFile(config: AdapterConfig) {
|
||||
const doc = this.editor.doc;
|
||||
const job = new Job({
|
||||
collection: this.editor.doc.collection,
|
||||
middlewares: [docLinkBaseURLMiddleware, titleMiddleware],
|
||||
schema: this.collection.schema,
|
||||
blobCRUD: this.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.collection.createDoc({ id }),
|
||||
get: (id: string) => this.collection.getDoc(id),
|
||||
delete: (id: string) => this.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(this.collection.id),
|
||||
titleMiddleware(this.collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
|
||||
const adapterFactory = this.editor.std.provider.get(config.identifier);
|
||||
@@ -432,7 +441,13 @@ export class StarterDebugMenu extends ShadowlessElement {
|
||||
});
|
||||
if (!file) return;
|
||||
const job = new Job({
|
||||
collection: this.collection,
|
||||
schema: this.collection.schema,
|
||||
blobCRUD: this.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.collection.createDoc({ id }),
|
||||
get: (id: string) => this.collection.getDoc(id),
|
||||
delete: (id: string) => this.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [defaultImageProxyMiddleware],
|
||||
});
|
||||
const htmlAdapter = new NotionHtmlAdapter(job, this.editor.std.provider);
|
||||
|
||||
@@ -84,7 +84,15 @@ export async function createDefaultDocCollection() {
|
||||
// debug info
|
||||
window.collection = collection;
|
||||
window.blockSchemas = AffineSchemas;
|
||||
window.job = new Job({ collection });
|
||||
window.job = new Job({
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
window.Y = DocCollection.Y;
|
||||
|
||||
return collection;
|
||||
|
||||
@@ -83,7 +83,15 @@ export function createStarterDocCollection() {
|
||||
// debug info
|
||||
window.collection = collection;
|
||||
window.blockSchemas = AffineSchemas;
|
||||
window.job = new Job({ collection: collection });
|
||||
window.job = new Job({
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
window.Y = DocCollection.Y;
|
||||
window.testUtils = new TestUtils();
|
||||
|
||||
|
||||
@@ -6,8 +6,14 @@ export async function importFromSnapshot(
|
||||
snapshot: DocSnapshot
|
||||
) {
|
||||
const job = new Job({
|
||||
collection,
|
||||
middlewares: [replaceIdMiddleware],
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [replaceIdMiddleware(collection.idGenerator)],
|
||||
});
|
||||
|
||||
return job.snapshotToDoc(snapshot);
|
||||
|
||||
@@ -174,8 +174,17 @@ const frameworkProvider = framework.provider();
|
||||
const blockSuiteDoc = doc.blockSuiteDoc;
|
||||
|
||||
const job = new Job({
|
||||
collection: blockSuiteDoc.collection,
|
||||
middlewares: [docLinkBaseURLMiddleware, titleMiddleware],
|
||||
schema: blockSuiteDoc.collection.schema,
|
||||
blobCRUD: blockSuiteDoc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => blockSuiteDoc.collection.createDoc({ id }),
|
||||
get: (id: string) => blockSuiteDoc.collection.getDoc(id),
|
||||
delete: (id: string) => blockSuiteDoc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware(blockSuiteDoc.collection.id),
|
||||
titleMiddleware(blockSuiteDoc.collection.meta.docMetas),
|
||||
],
|
||||
});
|
||||
const snapshot = job.docToSnapshot(blockSuiteDoc);
|
||||
|
||||
|
||||
@@ -78,8 +78,17 @@ export async function getContentFromSlice(
|
||||
type: 'markdown' | 'plain-text' = 'markdown'
|
||||
) {
|
||||
const job = new Job({
|
||||
collection: host.std.doc.collection,
|
||||
middlewares: [titleMiddleware, embedSyncedDocMiddleware('content')],
|
||||
schema: host.std.doc.collection.schema,
|
||||
blobCRUD: host.std.doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => host.std.doc.collection.createDoc({ id }),
|
||||
get: (id: string) => host.std.doc.collection.getDoc(id),
|
||||
delete: (id: string) => host.std.doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
titleMiddleware(host.std.doc.collection.meta.docMetas),
|
||||
embedSyncedDocMiddleware('content'),
|
||||
],
|
||||
});
|
||||
const snapshot = job.sliceToSnapshot(slice);
|
||||
if (!snapshot) {
|
||||
@@ -99,8 +108,14 @@ export async function getContentFromSlice(
|
||||
|
||||
export async function getPlainTextFromSlice(host: EditorHost, slice: Slice) {
|
||||
const job = new Job({
|
||||
collection: host.std.doc.collection,
|
||||
middlewares: [titleMiddleware],
|
||||
schema: host.std.doc.collection.schema,
|
||||
blobCRUD: host.std.doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => host.std.doc.collection.createDoc({ id }),
|
||||
get: (id: string) => host.std.doc.collection.getDoc(id),
|
||||
delete: (id: string) => host.std.doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [titleMiddleware(host.std.doc.collection.meta.docMetas)],
|
||||
});
|
||||
const snapshot = job.sliceToSnapshot(slice);
|
||||
if (!snapshot) {
|
||||
@@ -120,7 +135,13 @@ export const markdownToSnapshot = async (
|
||||
host: EditorHost
|
||||
) => {
|
||||
const job = new Job({
|
||||
collection: host.std.doc.collection,
|
||||
schema: host.std.doc.collection.schema,
|
||||
blobCRUD: host.std.doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => host.std.doc.collection.createDoc({ id }),
|
||||
get: (id: string) => host.std.doc.collection.getDoc(id),
|
||||
delete: (id: string) => host.std.doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [defaultImageProxyMiddleware, pasteMiddleware(host.std)],
|
||||
});
|
||||
const markdownAdapter = new MixTextAdapter(job, host.std.provider);
|
||||
@@ -204,7 +225,13 @@ export async function markDownToDoc(
|
||||
middlewares.push(...additionalMiddlewares);
|
||||
}
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares,
|
||||
});
|
||||
const mdAdapter = new MarkdownAdapter(job, provider);
|
||||
|
||||
@@ -55,10 +55,16 @@ interface AdapterConfig {
|
||||
|
||||
async function exportDoc(doc: Doc, std: BlockStdScope, config: AdapterConfig) {
|
||||
const job = new Job({
|
||||
collection: doc.collection,
|
||||
schema: doc.collection.schema,
|
||||
blobCRUD: doc.collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => doc.collection.createDoc({ id }),
|
||||
get: (id: string) => doc.collection.getDoc(id),
|
||||
delete: (id: string) => doc.collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [
|
||||
docLinkBaseURLMiddleware,
|
||||
titleMiddleware,
|
||||
docLinkBaseURLMiddleware(doc.collection.id),
|
||||
titleMiddleware(doc.collection.meta.docMetas),
|
||||
embedSyncedDocMiddleware('content'),
|
||||
],
|
||||
});
|
||||
|
||||
+7
-1
@@ -79,7 +79,13 @@ async function initDoc(name: DocName) {
|
||||
const snapshot = (await loaders[name]()) as DocSnapshot;
|
||||
const collection = await getCollection();
|
||||
const job = new Job({
|
||||
collection,
|
||||
schema: collection.schema,
|
||||
blobCRUD: collection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => collection.createDoc({ id }),
|
||||
get: (id: string) => collection.getDoc(id),
|
||||
delete: (id: string) => collection.removeDoc(id),
|
||||
},
|
||||
middlewares: [],
|
||||
});
|
||||
|
||||
|
||||
@@ -192,7 +192,13 @@ function generateMarkdownPreviewBuilder(
|
||||
const provider = container.provider();
|
||||
const markdownAdapter = new MarkdownAdapter(
|
||||
new Job({
|
||||
collection: markdownPreviewDocCollection,
|
||||
schema: markdownPreviewDocCollection.schema,
|
||||
blobCRUD: markdownPreviewDocCollection.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => markdownPreviewDocCollection.createDoc({ id }),
|
||||
get: (id: string) => markdownPreviewDocCollection.getDoc(id),
|
||||
delete: (id: string) => markdownPreviewDocCollection.removeDoc(id),
|
||||
},
|
||||
middlewares: [docLinkBaseURLMiddleware, titleMiddleware],
|
||||
}),
|
||||
provider
|
||||
|
||||
Reference in New Issue
Block a user