mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-06 08:50:50 +08:00
feat(editor): add provider for base adapter (#11169)
This commit is contained in:
@@ -7,7 +7,7 @@ import { createJob } from '../utils/create-job.js';
|
|||||||
import { getProvider } from '../utils/get-provider.js';
|
import { getProvider } from '../utils/get-provider.js';
|
||||||
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
|
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
|
||||||
|
|
||||||
getProvider();
|
const provider = getProvider();
|
||||||
|
|
||||||
describe('notion-text to snapshot', () => {
|
describe('notion-text to snapshot', () => {
|
||||||
test('basic', () => {
|
test('basic', () => {
|
||||||
@@ -98,7 +98,7 @@ describe('notion-text to snapshot', () => {
|
|||||||
pageId: '',
|
pageId: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ntAdapter = new NotionTextAdapter(createJob());
|
const ntAdapter = new NotionTextAdapter(createJob(), provider);
|
||||||
const target = ntAdapter.toSliceSnapshot({
|
const target = ntAdapter.toSliceSnapshot({
|
||||||
file: notionText,
|
file: notionText,
|
||||||
workspaceId: '',
|
workspaceId: '',
|
||||||
|
|||||||
@@ -165,5 +165,3 @@ export class PageClipboard extends ReadOnlyClipboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { pasteMiddleware };
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { defaultImageProxyMiddleware } from '@blocksuite/affine-block-image';
|
import { defaultImageProxyMiddleware } from '@blocksuite/affine-block-image';
|
||||||
import {
|
import {
|
||||||
AttachmentAdapter,
|
AttachmentAdapter,
|
||||||
|
ClipboardAdapter,
|
||||||
copyMiddleware,
|
copyMiddleware,
|
||||||
HtmlAdapter,
|
HtmlAdapter,
|
||||||
ImageAdapter,
|
ImageAdapter,
|
||||||
@@ -16,8 +17,6 @@ import {
|
|||||||
import type { BlockComponent, UIEventHandler } from '@blocksuite/block-std';
|
import type { BlockComponent, UIEventHandler } from '@blocksuite/block-std';
|
||||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
|
|
||||||
import { ClipboardAdapter } from './adapter.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReadOnlyClipboard is a class that provides a read-only clipboard for the root block.
|
* ReadOnlyClipboard is a class that provides a read-only clipboard for the root block.
|
||||||
* It is supported to copy models in the root block.
|
* It is supported to copy models in the root block.
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ import {
|
|||||||
FrameBlockModel,
|
FrameBlockModel,
|
||||||
MAX_IMAGE_WIDTH,
|
MAX_IMAGE_WIDTH,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
|
import {
|
||||||
|
ClipboardAdapter,
|
||||||
|
decodeClipboardBlobs,
|
||||||
|
encodeClipboardBlobs,
|
||||||
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
CANVAS_EXPORT_IGNORE_TAGS,
|
CANVAS_EXPORT_IGNORE_TAGS,
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
@@ -72,12 +77,7 @@ import {
|
|||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import * as Y from 'yjs';
|
import * as Y from 'yjs';
|
||||||
|
|
||||||
import { ClipboardAdapter } from '../../clipboard/adapter.js';
|
|
||||||
import { PageClipboard } from '../../clipboard/index.js';
|
import { PageClipboard } from '../../clipboard/index.js';
|
||||||
import {
|
|
||||||
decodeClipboardBlobs,
|
|
||||||
encodeClipboardBlobs,
|
|
||||||
} from '../../clipboard/utils.js';
|
|
||||||
import { edgelessElementsBoundFromRawData } from '../utils/bound-utils.js';
|
import { edgelessElementsBoundFromRawData } from '../utils/bound-utils.js';
|
||||||
import { createNewPresentationIndexes } from '../utils/clipboard-utils.js';
|
import { createNewPresentationIndexes } from '../utils/clipboard-utils.js';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -131,8 +131,8 @@ export const AttachmentAdapterFactoryIdentifier =
|
|||||||
|
|
||||||
export const AttachmentAdapterFactoryExtension: ExtensionType = {
|
export const AttachmentAdapterFactoryExtension: ExtensionType = {
|
||||||
setup: di => {
|
setup: di => {
|
||||||
di.addImpl(AttachmentAdapterFactoryIdentifier, () => ({
|
di.addImpl(AttachmentAdapterFactoryIdentifier, provider => ({
|
||||||
get: (job: Transformer) => new AttachmentAdapter(job),
|
get: (job: Transformer) => new AttachmentAdapter(job, provider),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-1
@@ -15,6 +15,7 @@ import type {
|
|||||||
} from '@blocksuite/store';
|
} from '@blocksuite/store';
|
||||||
import { BaseAdapter } from '@blocksuite/store';
|
import { BaseAdapter } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import { NotificationProvider } from '../../services/notification-service.js';
|
||||||
import { decodeClipboardBlobs, encodeClipboardBlobs } from './utils.js';
|
import { decodeClipboardBlobs, encodeClipboardBlobs } from './utils.js';
|
||||||
|
|
||||||
export type FileSnapshot = {
|
export type FileSnapshot = {
|
||||||
@@ -26,6 +27,13 @@ export type FileSnapshot = {
|
|||||||
export class ClipboardAdapter extends BaseAdapter<string> {
|
export class ClipboardAdapter extends BaseAdapter<string> {
|
||||||
static MIME = 'BLOCKSUITE/SNAPSHOT';
|
static MIME = 'BLOCKSUITE/SNAPSHOT';
|
||||||
|
|
||||||
|
private readonly _onError = (message: string) => {
|
||||||
|
const notification = this.provider.getOptional(NotificationProvider);
|
||||||
|
if (!notification) return;
|
||||||
|
|
||||||
|
notification.toast(message);
|
||||||
|
};
|
||||||
|
|
||||||
override fromBlockSnapshot(
|
override fromBlockSnapshot(
|
||||||
_payload: FromBlockSnapshotPayload
|
_payload: FromBlockSnapshotPayload
|
||||||
): Promise<FromBlockSnapshotResult<string>> {
|
): Promise<FromBlockSnapshotResult<string>> {
|
||||||
@@ -56,7 +64,10 @@ export class ClipboardAdapter extends BaseAdapter<string> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const map = assets.getAssets();
|
const map = assets.getAssets();
|
||||||
const blobs: Record<string, FileSnapshot> = await encodeClipboardBlobs(map);
|
const blobs: Record<string, FileSnapshot> = await encodeClipboardBlobs(
|
||||||
|
map,
|
||||||
|
this._onError
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
file: JSON.stringify({
|
file: JSON.stringify({
|
||||||
snapshot,
|
snapshot,
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './clipboard';
|
||||||
|
export * from './utils';
|
||||||
+7
-18
@@ -1,6 +1,4 @@
|
|||||||
import { toast } from '@blocksuite/affine-components/toast';
|
import type { FileSnapshot } from './clipboard.js';
|
||||||
|
|
||||||
import type { FileSnapshot } from './adapter.js';
|
|
||||||
|
|
||||||
const chars =
|
const chars =
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
@@ -67,30 +65,21 @@ export const decode = (base64: string): ArrayBuffer => {
|
|||||||
return arraybuffer;
|
return arraybuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function encodeClipboardBlobs(map: Map<string, Blob>) {
|
export async function encodeClipboardBlobs(
|
||||||
|
map: Map<string, Blob>,
|
||||||
|
onError?: (message: string) => void
|
||||||
|
) {
|
||||||
const blobs: Record<string, FileSnapshot> = {};
|
const blobs: Record<string, FileSnapshot> = {};
|
||||||
let sumSize = 0;
|
let sumSize = 0;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Array.from(map.entries()).map(async ([id, blob]) => {
|
Array.from(map.entries()).map(async ([id, blob]) => {
|
||||||
if (blob.size > 4 * 1024 * 1024) {
|
if (blob.size > 4 * 1024 * 1024) {
|
||||||
const host = document.querySelector('editor-host');
|
onError?.((blob as File).name ?? 'File' + ' is too large to be copied');
|
||||||
if (!host) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast(
|
|
||||||
host,
|
|
||||||
(blob as File).name ?? 'File' + ' is too large to be copied'
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sumSize += blob.size;
|
sumSize += blob.size;
|
||||||
if (sumSize > 6 * 1024 * 1024) {
|
if (sumSize > 6 * 1024 * 1024) {
|
||||||
const host = document.querySelector('editor-host');
|
onError?.(
|
||||||
if (!host) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast(
|
|
||||||
host,
|
|
||||||
(blob as File).name ??
|
(blob as File).name ??
|
||||||
'File' + ' cannot be copied due to the clipboard size limit'
|
'File' + ' cannot be copied due to the clipboard size limit'
|
||||||
);
|
);
|
||||||
@@ -174,11 +174,8 @@ export class HtmlAdapter extends BaseAdapter<Html> {
|
|||||||
|
|
||||||
readonly blockMatchers: BlockHtmlAdapterMatcher[];
|
readonly blockMatchers: BlockHtmlAdapterMatcher[];
|
||||||
|
|
||||||
constructor(
|
constructor(job: Transformer, provider: ServiceProvider) {
|
||||||
job: Transformer,
|
super(job, provider);
|
||||||
readonly provider: ServiceProvider
|
|
||||||
) {
|
|
||||||
super(job);
|
|
||||||
const blockMatchers = Array.from(
|
const blockMatchers = Array.from(
|
||||||
provider.getAll(BlockHtmlAdapterMatcherIdentifier).values()
|
provider.getAll(BlockHtmlAdapterMatcherIdentifier).values()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ export const ImageAdapterFactoryIdentifier = AdapterFactoryIdentifier('Image');
|
|||||||
|
|
||||||
export const ImageAdapterFactoryExtension: ExtensionType = {
|
export const ImageAdapterFactoryExtension: ExtensionType = {
|
||||||
setup: di => {
|
setup: di => {
|
||||||
di.addImpl(ImageAdapterFactoryIdentifier, () => ({
|
di.addImpl(ImageAdapterFactoryIdentifier, provider => ({
|
||||||
get: (job: Transformer) => new ImageAdapter(job),
|
get: (job: Transformer) => new ImageAdapter(job, provider),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './attachment';
|
export * from './attachment';
|
||||||
|
export * from './clipboard';
|
||||||
export {
|
export {
|
||||||
BlockHtmlAdapterExtension,
|
BlockHtmlAdapterExtension,
|
||||||
type BlockHtmlAdapterMatcher,
|
type BlockHtmlAdapterMatcher,
|
||||||
|
|||||||
@@ -170,11 +170,8 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
|
|
||||||
readonly blockMatchers: BlockMarkdownAdapterMatcher[];
|
readonly blockMatchers: BlockMarkdownAdapterMatcher[];
|
||||||
|
|
||||||
constructor(
|
constructor(job: Transformer, provider: ServiceProvider) {
|
||||||
job: Transformer,
|
super(job, provider);
|
||||||
readonly provider: ServiceProvider
|
|
||||||
) {
|
|
||||||
super(job);
|
|
||||||
const blockMatchers = Array.from(
|
const blockMatchers = Array.from(
|
||||||
provider.getAll(BlockMarkdownAdapterMatcherIdentifier).values()
|
provider.getAll(BlockMarkdownAdapterMatcherIdentifier).values()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class MixTextAdapter extends BaseAdapter<MixText> {
|
|||||||
private readonly _markdownAdapter: MarkdownAdapter;
|
private readonly _markdownAdapter: MarkdownAdapter;
|
||||||
|
|
||||||
constructor(job: Transformer, provider: ServiceProvider) {
|
constructor(job: Transformer, provider: ServiceProvider) {
|
||||||
super(job);
|
super(job, provider);
|
||||||
this._markdownAdapter = new MarkdownAdapter(job, provider);
|
this._markdownAdapter = new MarkdownAdapter(job, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export class NotionHtmlAdapter extends BaseAdapter<NotionHtml> {
|
|||||||
readonly blockMatchers: BlockNotionHtmlAdapterMatcher[];
|
readonly blockMatchers: BlockNotionHtmlAdapterMatcher[];
|
||||||
|
|
||||||
constructor(job: Transformer, provider: ServiceProvider) {
|
constructor(job: Transformer, provider: ServiceProvider) {
|
||||||
super(job);
|
super(job, provider);
|
||||||
const blockMatchers = Array.from(
|
const blockMatchers = Array.from(
|
||||||
provider.getAll(BlockNotionHtmlAdapterMatcherIdentifier).values()
|
provider.getAll(BlockNotionHtmlAdapterMatcherIdentifier).values()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ export const NotionTextAdapterFactoryIdentifier =
|
|||||||
|
|
||||||
export const NotionTextAdapterFactoryExtension: ExtensionType = {
|
export const NotionTextAdapterFactoryExtension: ExtensionType = {
|
||||||
setup: di => {
|
setup: di => {
|
||||||
di.addImpl(NotionTextAdapterFactoryIdentifier, () => ({
|
di.addImpl(NotionTextAdapterFactoryIdentifier, provider => ({
|
||||||
get: (job: Transformer) => new NotionTextAdapter(job),
|
get: (job: Transformer) => new NotionTextAdapter(job, provider),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,11 +49,8 @@ export class PlainTextAdapter extends BaseAdapter<PlainText> {
|
|||||||
|
|
||||||
readonly blockMatchers: BlockPlainTextAdapterMatcher[];
|
readonly blockMatchers: BlockPlainTextAdapterMatcher[];
|
||||||
|
|
||||||
constructor(
|
constructor(job: Transformer, provider: ServiceProvider) {
|
||||||
job: Transformer,
|
super(job, provider);
|
||||||
readonly provider: ServiceProvider
|
|
||||||
) {
|
|
||||||
super(job);
|
|
||||||
const blockMatchers = Array.from(
|
const blockMatchers = Array.from(
|
||||||
provider.getAll(BlockPlainTextAdapterMatcherIdentifier).values()
|
provider.getAll(BlockPlainTextAdapterMatcherIdentifier).values()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { ServiceProvider } from '@blocksuite/global/di';
|
||||||
import { BlockSuiteError } from '@blocksuite/global/exceptions';
|
import { BlockSuiteError } from '@blocksuite/global/exceptions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -73,7 +74,10 @@ export abstract class BaseAdapter<AdapterTarget = unknown> {
|
|||||||
return this.job.adapterConfigs;
|
return this.job.adapterConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(job: Transformer) {
|
constructor(
|
||||||
|
job: Transformer,
|
||||||
|
readonly provider: ServiceProvider
|
||||||
|
) {
|
||||||
this.job = job;
|
this.job = job;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import {
|
|||||||
TextSelection,
|
TextSelection,
|
||||||
} from '@blocksuite/affine/block-std';
|
} from '@blocksuite/affine/block-std';
|
||||||
import { defaultImageProxyMiddleware } from '@blocksuite/affine/blocks/image';
|
import { defaultImageProxyMiddleware } from '@blocksuite/affine/blocks/image';
|
||||||
import { pasteMiddleware } from '@blocksuite/affine/blocks/root';
|
|
||||||
import type { ServiceProvider } from '@blocksuite/affine/global/di';
|
import type { ServiceProvider } from '@blocksuite/affine/global/di';
|
||||||
import {
|
import {
|
||||||
embedSyncedDocMiddleware,
|
embedSyncedDocMiddleware,
|
||||||
MarkdownAdapter,
|
MarkdownAdapter,
|
||||||
MixTextAdapter,
|
MixTextAdapter,
|
||||||
|
pasteMiddleware,
|
||||||
PlainTextAdapter,
|
PlainTextAdapter,
|
||||||
titleMiddleware,
|
titleMiddleware,
|
||||||
} from '@blocksuite/affine/shared/adapters';
|
} from '@blocksuite/affine/shared/adapters';
|
||||||
|
|||||||
Reference in New Issue
Block a user