diff --git a/blocksuite/affine/shared/src/adapters/html-adapter/block-adapter.ts b/blocksuite/affine/shared/src/adapters/html/block-adapter.ts similarity index 100% rename from blocksuite/affine/shared/src/adapters/html-adapter/block-adapter.ts rename to blocksuite/affine/shared/src/adapters/html/block-adapter.ts diff --git a/blocksuite/affine/shared/src/adapters/html-adapter/delta-converter.ts b/blocksuite/affine/shared/src/adapters/html/delta-converter.ts similarity index 100% rename from blocksuite/affine/shared/src/adapters/html-adapter/delta-converter.ts rename to blocksuite/affine/shared/src/adapters/html/delta-converter.ts diff --git a/blocksuite/affine/shared/src/adapters/html-adapter/index.ts b/blocksuite/affine/shared/src/adapters/html/index.ts similarity index 100% rename from blocksuite/affine/shared/src/adapters/html-adapter/index.ts rename to blocksuite/affine/shared/src/adapters/html/index.ts diff --git a/blocksuite/affine/shared/src/adapters/html-adapter/type.ts b/blocksuite/affine/shared/src/adapters/html/type.ts similarity index 100% rename from blocksuite/affine/shared/src/adapters/html-adapter/type.ts rename to blocksuite/affine/shared/src/adapters/html/type.ts diff --git a/blocksuite/affine/shared/src/adapters/index.ts b/blocksuite/affine/shared/src/adapters/index.ts index fb261cd31a..ae19f8fcfe 100644 --- a/blocksuite/affine/shared/src/adapters/index.ts +++ b/blocksuite/affine/shared/src/adapters/index.ts @@ -3,12 +3,14 @@ export { type BlockHtmlAdapterMatcher, BlockHtmlAdapterMatcherIdentifier, type Html, + HtmlASTToDeltaExtension, type HtmlASTToDeltaMatcher, HtmlASTToDeltaMatcherIdentifier, HtmlDeltaConverter, + InlineDeltaToHtmlAdapterExtension, type InlineDeltaToHtmlAdapterMatcher, InlineDeltaToHtmlAdapterMatcherIdentifier, -} from './html-adapter/index.js'; +} from './html/index.js'; export { BlockMarkdownAdapterExtension, type BlockMarkdownAdapterMatcher, diff --git a/blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts b/blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts index 2de1f80c00..bba8219d92 100644 --- a/blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts +++ b/blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts @@ -2,6 +2,7 @@ import { DEFAULT_NOTE_BACKGROUND_COLOR, NoteDisplayMode, } from '@blocksuite/affine-model'; +import { Container } from '@blocksuite/global/di'; import type { BlockSnapshot, DocSnapshot, @@ -10,11 +11,24 @@ import type { import { AssetsManager, MemoryBlobCRUD } from '@blocksuite/store'; import { describe, expect, test } from 'vitest'; -import { HtmlAdapter } from '../../_common/adapters/html-adapter/html.js'; +import { defaultBlockHtmlAdapterMatchers } from '../../_common/adapters/html/block-matcher.js'; +import { htmlInlineToDeltaMatchers } from '../../_common/adapters/html/delta-converter/html-inline.js'; +import { inlineDeltaToHtmlAdapterMatchers } from '../../_common/adapters/html/delta-converter/inline-delta.js'; +import { HtmlAdapter } from '../../_common/adapters/html/html.js'; import { nanoidReplacement } from '../../_common/test-utils/test-utils.js'; import { embedSyncedDocMiddleware } from '../../_common/transformers/middlewares.js'; import { createJob } from '../utils/create-job.js'; +const container = new Container(); +[ + ...htmlInlineToDeltaMatchers, + ...defaultBlockHtmlAdapterMatchers, + ...inlineDeltaToHtmlAdapterMatchers, +].forEach(ext => { + ext.setup(container); +}); +const provider = container.provider(); + describe('snapshot to html', () => { const template = (html: string, title?: string) => { let htmlTemplate = ` @@ -126,7 +140,7 @@ describe('snapshot to html', () => { `
import this
` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -192,7 +206,7 @@ describe('snapshot to html', () => { `
import this
` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -258,7 +272,7 @@ describe('snapshot to html', () => { `
import this
` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -444,7 +458,7 @@ describe('snapshot to html', () => { `

aaa

bbb

ccc

ddd

eee

fff

ggg

hhh

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -589,7 +603,7 @@ describe('snapshot to html', () => { `` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -694,7 +708,7 @@ describe('snapshot to html', () => { `` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -768,7 +782,7 @@ describe('snapshot to html', () => { `

aaa bbb ccc

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -842,7 +856,7 @@ describe('snapshot to html', () => { `

aaa bbb ccc

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -917,7 +931,7 @@ describe('snapshot to html', () => { `

aaabbbccc

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -992,7 +1006,7 @@ describe('snapshot to html', () => { `

aaabbbccc

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -1069,7 +1083,7 @@ describe('snapshot to html', () => { `
YXXTjRmLlNyiOUnHb8nAIvUP6V7PAXhwW9F5_tc2LGs=.blob

` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const blobManager = new MemoryBlobCRUD(); await blobManager.set( 'YXXTjRmLlNyiOUnHb8nAIvUP6V7PAXhwW9F5_tc2LGs=', @@ -1171,7 +1185,7 @@ describe('snapshot to html', () => { `
${testCase.title}
` ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -1430,7 +1444,7 @@ describe('snapshot to html', () => { const html = template( '
TitleStatusDateNumberProgressMultiSelectRichTextLinkCheckbox
Task 1TODO2023-12-15165test1,test2test2https://google.comtrue
Task 2In Progress2023-12-20test1
' ); - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapshot, }); @@ -1510,7 +1524,7 @@ describe('snapshot to html', () => { const html = template( '
Test Doc
' ); - const htmlAdapter = new HtmlAdapter(createJob([middleware])); + const htmlAdapter = new HtmlAdapter(createJob([middleware]), provider); const target = await htmlAdapter.fromBlockSnapshot({ snapshot: blockSnapShot, }); @@ -1896,7 +1910,7 @@ describe('snapshot to html', () => { await job.snapshotToDoc(syncedDocSnapshot); await job.snapshotToDoc(docSnapShot); - const mdAdapter = new HtmlAdapter(job); + const mdAdapter = new HtmlAdapter(job, provider); const target = await mdAdapter.fromDocSnapshot({ snapshot: docSnapShot, }); @@ -1981,7 +1995,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2091,7 +2105,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2157,7 +2171,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2202,7 +2216,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2238,7 +2252,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2350,7 +2364,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2394,7 +2408,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2438,7 +2452,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2491,7 +2505,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2544,7 +2558,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); @@ -2585,7 +2599,7 @@ describe('html to snapshot', () => { ], }; - const htmlAdapter = new HtmlAdapter(createJob()); + const htmlAdapter = new HtmlAdapter(createJob(), provider); const rawBlockSnapshot = await htmlAdapter.toBlockSnapshot({ file: html, }); diff --git a/blocksuite/blocks/src/_common/adapters/extension.ts b/blocksuite/blocks/src/_common/adapters/extension.ts index 228113d748..7135e10d22 100644 --- a/blocksuite/blocks/src/_common/adapters/extension.ts +++ b/blocksuite/blocks/src/_common/adapters/extension.ts @@ -1,7 +1,9 @@ import type { ExtensionType } from '@blocksuite/block-std'; import { AttachmentAdapterFactoryExtension } from './attachment.js'; -import { HtmlAdapterFactoryExtension } from './html-adapter/html.js'; +import { htmlInlineToDeltaMatchers } from './html/delta-converter/html-inline.js'; +import { inlineDeltaToHtmlAdapterMatchers } from './html/delta-converter/inline-delta.js'; +import { HtmlAdapterFactoryExtension } from './html/html.js'; import { ImageAdapterFactoryExtension } from './image.js'; import { MarkdownAdapterFactoryExtension } from './markdown/markdown.js'; import { MixTextAdapterFactoryExtension } from './mix-text.js'; @@ -10,6 +12,8 @@ import { NotionTextAdapterFactoryExtension } from './notion-text.js'; import { PlainTextAdapterFactoryExtension } from './plain-text/plain-text.js'; export const AdapterFactoryExtensions: ExtensionType[] = [ + ...htmlInlineToDeltaMatchers, + ...inlineDeltaToHtmlAdapterMatchers, AttachmentAdapterFactoryExtension, ImageAdapterFactoryExtension, MarkdownAdapterFactoryExtension, diff --git a/blocksuite/blocks/src/_common/adapters/html-adapter/block-matcher.ts b/blocksuite/blocks/src/_common/adapters/html-adapter/block-matcher.ts deleted file mode 100644 index 1fa564266b..0000000000 --- a/blocksuite/blocks/src/_common/adapters/html-adapter/block-matcher.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - embedFigmaBlockHtmlAdapterMatcher, - embedGithubBlockHtmlAdapterMatcher, - embedLinkedDocBlockHtmlAdapterMatcher, - embedLoomBlockHtmlAdapterMatcher, - embedSyncedDocBlockHtmlAdapterMatcher, - embedYoutubeBlockHtmlAdapterMatcher, -} from '@blocksuite/affine-block-embed'; -import { listBlockHtmlAdapterMatcher } from '@blocksuite/affine-block-list'; -import { paragraphBlockHtmlAdapterMatcher } from '@blocksuite/affine-block-paragraph'; - -import { bookmarkBlockHtmlAdapterMatcher } from '../../../bookmark-block/adapters/html.js'; -import { codeBlockHtmlAdapterMatcher } from '../../../code-block/adapters/html.js'; -import { databaseBlockHtmlAdapterMatcher } from '../../../database-block/adapters/html.js'; -import { dividerBlockHtmlAdapterMatcher } from '../../../divider-block/adapters/html.js'; -import { imageBlockHtmlAdapterMatcher } from '../../../image-block/adapters/html.js'; -import { rootBlockHtmlAdapterMatcher } from '../../../root-block/adapters/html.js'; - -export const defaultBlockHtmlAdapterMatchers = [ - listBlockHtmlAdapterMatcher, - paragraphBlockHtmlAdapterMatcher, - codeBlockHtmlAdapterMatcher, - dividerBlockHtmlAdapterMatcher, - imageBlockHtmlAdapterMatcher, - rootBlockHtmlAdapterMatcher, - embedYoutubeBlockHtmlAdapterMatcher, - embedFigmaBlockHtmlAdapterMatcher, - embedLoomBlockHtmlAdapterMatcher, - embedGithubBlockHtmlAdapterMatcher, - bookmarkBlockHtmlAdapterMatcher, - databaseBlockHtmlAdapterMatcher, - embedLinkedDocBlockHtmlAdapterMatcher, - embedSyncedDocBlockHtmlAdapterMatcher, -]; diff --git a/blocksuite/blocks/src/_common/adapters/html/block-matcher.ts b/blocksuite/blocks/src/_common/adapters/html/block-matcher.ts new file mode 100644 index 0000000000..6b13cd8694 --- /dev/null +++ b/blocksuite/blocks/src/_common/adapters/html/block-matcher.ts @@ -0,0 +1,34 @@ +import { + EmbedFigmaBlockHtmlAdapterExtension, + EmbedGithubBlockHtmlAdapterExtension, + EmbedLinkedDocHtmlAdapterExtension, + EmbedLoomBlockHtmlAdapterExtension, + EmbedSyncedDocBlockHtmlAdapterExtension, + EmbedYoutubeBlockHtmlAdapterExtension, +} from '@blocksuite/affine-block-embed'; +import { ListBlockHtmlAdapterExtension } from '@blocksuite/affine-block-list'; +import { ParagraphBlockHtmlAdapterExtension } from '@blocksuite/affine-block-paragraph'; + +import { BookmarkBlockHtmlAdapterExtension } from '../../../bookmark-block/adapters/html.js'; +import { CodeBlockHtmlAdapterExtension } from '../../../code-block/adapters/html.js'; +import { DatabaseBlockHtmlAdapterExtension } from '../../../database-block/adapters/html.js'; +import { DividerBlockHtmlAdapterExtension } from '../../../divider-block/adapters/html.js'; +import { ImageBlockHtmlAdapterExtension } from '../../../image-block/adapters/html.js'; +import { RootBlockHtmlAdapterExtension } from '../../../root-block/adapters/html.js'; + +export const defaultBlockHtmlAdapterMatchers = [ + ListBlockHtmlAdapterExtension, + ParagraphBlockHtmlAdapterExtension, + CodeBlockHtmlAdapterExtension, + DividerBlockHtmlAdapterExtension, + ImageBlockHtmlAdapterExtension, + RootBlockHtmlAdapterExtension, + EmbedYoutubeBlockHtmlAdapterExtension, + EmbedFigmaBlockHtmlAdapterExtension, + EmbedLoomBlockHtmlAdapterExtension, + EmbedGithubBlockHtmlAdapterExtension, + BookmarkBlockHtmlAdapterExtension, + DatabaseBlockHtmlAdapterExtension, + EmbedLinkedDocHtmlAdapterExtension, + EmbedSyncedDocBlockHtmlAdapterExtension, +]; diff --git a/blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/html-inline.ts b/blocksuite/blocks/src/_common/adapters/html/delta-converter/html-inline.ts similarity index 86% rename from blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/html-inline.ts rename to blocksuite/blocks/src/_common/adapters/html/delta-converter/html-inline.ts index 7a0d910322..1a2704a9a6 100644 --- a/blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/html-inline.ts +++ b/blocksuite/blocks/src/_common/adapters/html/delta-converter/html-inline.ts @@ -1,6 +1,6 @@ -import type { - HtmlAST, - HtmlASTToDeltaMatcher, +import { + type HtmlAST, + HtmlASTToDeltaExtension, } from '@blocksuite/affine-shared/adapters'; import { collapseWhiteSpace } from 'collapse-white-space'; import type { Element } from 'hast'; @@ -14,7 +14,7 @@ const listElementTags = new Set(['ol', 'ul']); const strongElementTags = new Set(['strong', 'b']); const italicElementTags = new Set(['i', 'em']); -export const htmlTextToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlTextToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'text', match: ast => ast.type === 'text', toDelta: (ast, context) => { @@ -33,9 +33,9 @@ export const htmlTextToDeltaMatcher: HtmlASTToDeltaMatcher = { : collapseWhiteSpace(ast.value); return value ? [{ insert: value }] : []; }, -}; +}); -export const htmlTextLikeElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlTextLikeElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'text-like-element', match: ast => isElement(ast) && textLikeElementTags.has(ast.tagName), toDelta: (ast, context) => { @@ -46,17 +46,17 @@ export const htmlTextLikeElementToDeltaMatcher: HtmlASTToDeltaMatcher = { context.toDelta(child, { trim: false }) ); }, -}; +}); -export const htmlListToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlListToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'list-element', match: ast => isElement(ast) && listElementTags.has(ast.tagName), toDelta: () => { return []; }, -}; +}); -export const htmlStrongElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlStrongElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'strong-element', match: ast => isElement(ast) && strongElementTags.has(ast.tagName), toDelta: (ast, context) => { @@ -70,9 +70,9 @@ export const htmlStrongElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlItalicElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlItalicElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'italic-element', match: ast => isElement(ast) && italicElementTags.has(ast.tagName), toDelta: (ast, context) => { @@ -86,8 +86,9 @@ export const htmlItalicElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; -export const htmlCodeElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +}); + +export const htmlCodeElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'code-element', match: ast => isElement(ast) && ast.tagName === 'code', toDelta: (ast, context) => { @@ -101,9 +102,9 @@ export const htmlCodeElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlDelElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlDelElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'del-element', match: ast => isElement(ast) && ast.tagName === 'del', toDelta: (ast, context) => { @@ -117,9 +118,9 @@ export const htmlDelElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlUnderlineElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlUnderlineElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'underline-element', match: ast => isElement(ast) && ast.tagName === 'u', toDelta: (ast, context) => { @@ -133,9 +134,9 @@ export const htmlUnderlineElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlLinkElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlLinkElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'link-element', match: ast => isElement(ast) && ast.tagName === 'a', toDelta: (ast, context) => { @@ -194,9 +195,9 @@ export const htmlLinkElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlMarkElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlMarkElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'mark-element', match: ast => isElement(ast) && ast.tagName === 'mark', toDelta: (ast, context) => { @@ -210,17 +211,17 @@ export const htmlMarkElementToDeltaMatcher: HtmlASTToDeltaMatcher = { }) ); }, -}; +}); -export const htmlBrElementToDeltaMatcher: HtmlASTToDeltaMatcher = { +export const htmlBrElementToDeltaMatcher = HtmlASTToDeltaExtension({ name: 'br-element', match: ast => isElement(ast) && ast.tagName === 'br', toDelta: () => { return [{ insert: '\n' }]; }, -}; +}); -export const htmlInlineToDeltaMatchers: HtmlASTToDeltaMatcher[] = [ +export const htmlInlineToDeltaMatchers = [ htmlTextToDeltaMatcher, htmlTextLikeElementToDeltaMatcher, htmlStrongElementToDeltaMatcher, diff --git a/blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/inline-delta.ts b/blocksuite/blocks/src/_common/adapters/html/delta-converter/inline-delta.ts similarity index 68% rename from blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/inline-delta.ts rename to blocksuite/blocks/src/_common/adapters/html/delta-converter/inline-delta.ts index 577e530211..d8b0501e97 100644 --- a/blocksuite/blocks/src/_common/adapters/html-adapter/delta-converter/inline-delta.ts +++ b/blocksuite/blocks/src/_common/adapters/html/delta-converter/inline-delta.ts @@ -1,10 +1,8 @@ import { generateDocUrl } from '@blocksuite/affine-block-embed'; -import type { - InlineDeltaToHtmlAdapterMatcher, - InlineHtmlAST, -} from '@blocksuite/affine-shared/adapters'; +import type { InlineHtmlAST } from '@blocksuite/affine-shared/adapters'; +import { InlineDeltaToHtmlAdapterExtension } from '@blocksuite/affine-shared/adapters'; -export const boldDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = { +export const boldDeltaToHtmlAdapterMatcher = InlineDeltaToHtmlAdapterExtension({ name: 'bold', match: delta => !!delta.attributes?.bold, toAST: (_, context) => { @@ -15,10 +13,10 @@ export const boldDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = { children: [context.current], }; }, -}; +}); -export const italicDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = - { +export const italicDeltaToHtmlAdapterMatcher = + InlineDeltaToHtmlAdapterExtension({ name: 'italic', match: delta => !!delta.attributes?.italic, toAST: (_, context) => { @@ -29,10 +27,10 @@ export const italicDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = children: [context.current], }; }, - }; + }); -export const strikeDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = - { +export const strikeDeltaToHtmlAdapterMatcher = + InlineDeltaToHtmlAdapterExtension({ name: 'strike', match: delta => !!delta.attributes?.strike, toAST: (_, context) => { @@ -43,10 +41,10 @@ export const strikeDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = children: [context.current], }; }, - }; + }); -export const inlineCodeDeltaToMarkdownAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = - { +export const inlineCodeDeltaToMarkdownAdapterMatcher = + InlineDeltaToHtmlAdapterExtension({ name: 'inlineCode', match: delta => !!delta.attributes?.code, toAST: (_, context) => { @@ -57,10 +55,10 @@ export const inlineCodeDeltaToMarkdownAdapterMatcher: InlineDeltaToHtmlAdapterMa children: [context.current], }; }, - }; + }); -export const underlineDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = - { +export const underlineDeltaToHtmlAdapterMatcher = + InlineDeltaToHtmlAdapterExtension({ name: 'underline', match: delta => !!delta.attributes?.underline, toAST: (_, context) => { @@ -71,10 +69,10 @@ export const underlineDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher children: [context.current], }; }, - }; + }); -export const referenceDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = - { +export const referenceDeltaToHtmlAdapterMatcher = + InlineDeltaToHtmlAdapterExtension({ name: 'reference', match: delta => !!delta.attributes?.reference, toAST: (delta, context) => { @@ -108,9 +106,9 @@ export const referenceDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher return hast; }, - }; + }); -export const linkDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = { +export const linkDeltaToHtmlAdapterMatcher = InlineDeltaToHtmlAdapterExtension({ name: 'link', match: delta => !!delta.attributes?.link, toAST: (delta, _) => { @@ -131,15 +129,14 @@ export const linkDeltaToHtmlAdapterMatcher: InlineDeltaToHtmlAdapterMatcher = { children: [hast], }; }, -}; +}); -export const inlineDeltaToHtmlAdapterMatchers: InlineDeltaToHtmlAdapterMatcher[] = - [ - boldDeltaToHtmlAdapterMatcher, - italicDeltaToHtmlAdapterMatcher, - strikeDeltaToHtmlAdapterMatcher, - underlineDeltaToHtmlAdapterMatcher, - inlineCodeDeltaToMarkdownAdapterMatcher, - referenceDeltaToHtmlAdapterMatcher, - linkDeltaToHtmlAdapterMatcher, - ]; +export const inlineDeltaToHtmlAdapterMatchers = [ + boldDeltaToHtmlAdapterMatcher, + italicDeltaToHtmlAdapterMatcher, + strikeDeltaToHtmlAdapterMatcher, + underlineDeltaToHtmlAdapterMatcher, + inlineCodeDeltaToMarkdownAdapterMatcher, + referenceDeltaToHtmlAdapterMatcher, + linkDeltaToHtmlAdapterMatcher, +]; diff --git a/blocksuite/blocks/src/_common/adapters/html-adapter/html.ts b/blocksuite/blocks/src/_common/adapters/html/html.ts similarity index 93% rename from blocksuite/blocks/src/_common/adapters/html-adapter/html.ts rename to blocksuite/blocks/src/_common/adapters/html/html.ts index 6c77f8054d..faf053f968 100644 --- a/blocksuite/blocks/src/_common/adapters/html-adapter/html.ts +++ b/blocksuite/blocks/src/_common/adapters/html/html.ts @@ -8,9 +8,12 @@ import { BlockHtmlAdapterMatcherIdentifier, HastUtils, type HtmlAST, + HtmlASTToDeltaMatcherIdentifier, HtmlDeltaConverter, + InlineDeltaToHtmlAdapterMatcherIdentifier, } from '@blocksuite/affine-shared/adapters'; import type { ExtensionType } from '@blocksuite/block-std'; +import type { ServiceProvider } from '@blocksuite/global/di'; import { type AssetsManager, ASTWalker, @@ -36,9 +39,6 @@ import rehypeStringify from 'rehype-stringify'; import { unified } from 'unified'; import { AdapterFactoryIdentifier } from '../type.js'; -import { defaultBlockHtmlAdapterMatchers } from './block-matcher.js'; -import { htmlInlineToDeltaMatchers } from './delta-converter/html-inline.js'; -import { inlineDeltaToHtmlAdapterMatchers } from './delta-converter/inline-delta.js'; export type Html = string; @@ -170,11 +170,20 @@ export class HtmlAdapter extends BaseAdapter { deltaConverter: HtmlDeltaConverter; - constructor( - job: Job, - readonly blockMatchers: BlockHtmlAdapterMatcher[] = defaultBlockHtmlAdapterMatchers - ) { + readonly blockMatchers: BlockHtmlAdapterMatcher[]; + + constructor(job: Job, provider: ServiceProvider) { super(job); + const blockMatchers = Array.from( + provider.getAll(BlockHtmlAdapterMatcherIdentifier).values() + ); + const inlineDeltaToHtmlAdapterMatchers = Array.from( + provider.getAll(InlineDeltaToHtmlAdapterMatcherIdentifier).values() + ); + const htmlInlineToDeltaMatchers = Array.from( + provider.getAll(HtmlASTToDeltaMatcherIdentifier).values() + ); + this.blockMatchers = blockMatchers; this.deltaConverter = new HtmlDeltaConverter( job.adapterConfigs, inlineDeltaToHtmlAdapterMatchers, @@ -373,13 +382,7 @@ export const HtmlAdapterFactoryIdentifier = AdapterFactoryIdentifier('Html'); export const HtmlAdapterFactoryExtension: ExtensionType = { setup: di => { di.addImpl(HtmlAdapterFactoryIdentifier, provider => ({ - get: (job: Job) => - new HtmlAdapter( - job, - Array.from( - provider.getAll(BlockHtmlAdapterMatcherIdentifier).values() - ) - ), + get: job => new HtmlAdapter(job, provider), })); }, }; diff --git a/blocksuite/blocks/src/_common/adapters/html-adapter/index.ts b/blocksuite/blocks/src/_common/adapters/html/index.ts similarity index 100% rename from blocksuite/blocks/src/_common/adapters/html-adapter/index.ts rename to blocksuite/blocks/src/_common/adapters/html/index.ts diff --git a/blocksuite/blocks/src/_common/adapters/index.ts b/blocksuite/blocks/src/_common/adapters/index.ts index d6e8c7b240..6340949f3d 100644 --- a/blocksuite/blocks/src/_common/adapters/index.ts +++ b/blocksuite/blocks/src/_common/adapters/index.ts @@ -1,6 +1,6 @@ export * from './attachment.js'; export * from './extension.js'; -export * from './html-adapter/html.js'; +export * from './html/html.js'; export * from './image.js'; export * from './markdown/index.js'; export * from './mix-text.js'; diff --git a/blocksuite/blocks/src/_common/transformers/html.ts b/blocksuite/blocks/src/_common/transformers/html.ts index e09d1c6116..c3d4be102b 100644 --- a/blocksuite/blocks/src/_common/transformers/html.ts +++ b/blocksuite/blocks/src/_common/transformers/html.ts @@ -1,8 +1,12 @@ +import { Container } from '@blocksuite/global/di'; import { sha } from '@blocksuite/global/utils'; import type { Doc, DocCollection } from '@blocksuite/store'; import { extMimeMap, Job } from '@blocksuite/store'; -import { HtmlAdapter } from '../adapters/html-adapter/html.js'; +import { defaultBlockHtmlAdapterMatchers } from '../adapters/html/block-matcher.js'; +import { htmlInlineToDeltaMatchers } from '../adapters/html/delta-converter/html-inline.js'; +import { inlineDeltaToHtmlAdapterMatchers } from '../adapters/html/delta-converter/inline-delta.js'; +import { HtmlAdapter } from '../adapters/html/html.js'; import { defaultImageProxyMiddleware, docLinkBaseURLMiddleware, @@ -22,6 +26,17 @@ type ImportHTMLZipOptions = { imported: Blob; }; +const container = new Container(); +[ + ...htmlInlineToDeltaMatchers, + ...defaultBlockHtmlAdapterMatchers, + ...inlineDeltaToHtmlAdapterMatchers, +].forEach(ext => { + ext.setup(container); +}); + +const provider = container.provider(); + /** * Exports a doc to HTML format. * @@ -34,7 +49,7 @@ async function exportDoc(doc: Doc) { middlewares: [docLinkBaseURLMiddleware, titleMiddleware], }); const snapshot = job.docToSnapshot(doc); - const adapter = new HtmlAdapter(job); + const adapter = new HtmlAdapter(job, provider); if (!snapshot) { return; } @@ -83,7 +98,7 @@ async function importHTMLToDoc({ docLinkBaseURLMiddleware, ], }); - const htmlAdapter = new HtmlAdapter(job); + const htmlAdapter = new HtmlAdapter(job, provider); const page = await htmlAdapter.toDoc({ file: html, assets: job.assetsManager, @@ -147,7 +162,7 @@ async function importHTMLZip({ collection, imported }: ImportHTMLZipOptions) { for (const [key, value] of pendingPathBlobIdMap.entries()) { pathBlobIdMap.set(key, value); } - const htmlAdapter = new HtmlAdapter(job); + const htmlAdapter = new HtmlAdapter(job, provider); const html = await blob.text(); const doc = await htmlAdapter.toDoc({ file: html, diff --git a/blocksuite/framework/block-std/src/clipboard/index.ts b/blocksuite/framework/block-std/src/clipboard/index.ts index a1d8295e9b..fcb8888efb 100644 --- a/blocksuite/framework/block-std/src/clipboard/index.ts +++ b/blocksuite/framework/block-std/src/clipboard/index.ts @@ -1,3 +1,4 @@ +import type { ServiceProvider } from '@blocksuite/global/di'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import type { BaseAdapter, @@ -15,7 +16,9 @@ import { unified } from 'unified'; import { LifeCycleWatcher } from '../extension/index.js'; -type AdapterConstructor = new (job: Job) => T; +type AdapterConstructor = + | { new (job: Job): T } + | (new (job: Job, provider: ServiceProvider) => T); type AdapterMap = Map< string, @@ -131,7 +134,7 @@ export class Clipboard extends LifeCycleWatcher { } if (item) { const job = this._getJob(); - const adapterInstance = new adapter(job); + const adapterInstance = new adapter(job, this.std.provider); const payload = { file: item, assets: job.assetsManager, @@ -274,7 +277,7 @@ export class Clipboard extends LifeCycleWatcher { return; } const { adapter } = adapterItem; - const adapterInstance = new adapter(job); + const adapterInstance = new adapter(job, this.std.provider); const result = await adapterInstance.fromSlice(slice); if (!result) { return;