diff --git a/blocksuite/affine/block-surface/src/adapters/extension.ts b/blocksuite/affine/block-surface/src/adapters/extension.ts index 9c0fd0dec0..51bfbdae91 100644 --- a/blocksuite/affine/block-surface/src/adapters/extension.ts +++ b/blocksuite/affine/block-surface/src/adapters/extension.ts @@ -1,3 +1,4 @@ +import { elementToMarkdownAdapterMatchers } from './markdown/element-adapter/elements/index.js'; import { EdgelessSurfaceBlockMarkdownAdapterExtension, SurfaceBlockMarkdownAdapterExtension, @@ -10,12 +11,14 @@ import { export const SurfaceBlockAdapterExtensions = [ ...elementToPlainTextAdapterMatchers, + ...elementToMarkdownAdapterMatchers, SurfaceBlockPlainTextAdapterExtension, SurfaceBlockMarkdownAdapterExtension, ]; export const EdgelessSurfaceBlockAdapterExtensions = [ ...elementToPlainTextAdapterMatchers, + ...elementToMarkdownAdapterMatchers, EdgelessSurfaceBlockPlainTextAdapterExtension, EdgelessSurfaceBlockMarkdownAdapterExtension, ]; diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/brush.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/brush.ts index 842ab54765..25214c82af 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/brush.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/brush.ts @@ -1,19 +1,18 @@ -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const brushToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { - name: 'brush', - match: elementModel => elementModel.type === 'brush', - toAST: () => { - const content = `Brush Stroke`; - return { - type: 'paragraph', - children: [ - { - type: 'text', - value: content, - }, - ], - }; - }, - }; +export const brushToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({ + name: 'brush', + match: elementModel => elementModel.type === 'brush', + toAST: () => { + const content = `Brush Stroke`; + return { + type: 'paragraph', + children: [ + { + type: 'text', + value: content, + }, + ], + }; + }, +}); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/connector.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/connector.ts index 9094e2e703..f83cd5d19f 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/connector.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/connector.ts @@ -1,8 +1,8 @@ import { getConnectorText } from '../../../utils/text.js'; -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const connectorToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { +export const connectorToMarkdownAdapterMatcher = + ElementToMarkdownAdapterExtension({ name: 'connector', match: elementModel => elementModel.type === 'connector', toAST: elementModel => { @@ -22,4 +22,4 @@ export const connectorToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMat ], }; }, - }; + }); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/group.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/group.ts index 3be7180cfa..3329dac672 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/group.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/group.ts @@ -1,25 +1,24 @@ import { getGroupTitle } from '../../../utils/text.js'; -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const groupToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { - name: 'group', - match: elementModel => elementModel.type === 'group', - toAST: elementModel => { - const title = getGroupTitle(elementModel); - if (!title) { - return null; - } +export const groupToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({ + name: 'group', + match: elementModel => elementModel.type === 'group', + toAST: elementModel => { + const title = getGroupTitle(elementModel); + if (!title) { + return null; + } - const content = `Group, with title "${title}"`; - return { - type: 'paragraph', - children: [ - { - type: 'text', - value: content, - }, - ], - }; - }, - }; + const content = `Group, with title "${title}"`; + return { + type: 'paragraph', + children: [ + { + type: 'text', + value: content, + }, + ], + }; + }, +}); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/mindmap.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/mindmap.ts index 1b28eec9b3..e853e64d05 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/mindmap.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/mindmap.ts @@ -1,10 +1,10 @@ import type { MindMapTreeNode } from '../../../types/mindmap.js'; import { buildMindMapTree } from '../../../utils/mindmap.js'; import { getShapeText } from '../../../utils/text.js'; -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const mindmapToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { +export const mindmapToMarkdownAdapterMatcher = + ElementToMarkdownAdapterExtension({ name: 'mindmap', match: elementModel => elementModel.type === 'mindmap', toAST: (elementModel, context) => { @@ -64,4 +64,4 @@ export const mindmapToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatch return null; }, - }; + }); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/shape.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/shape.ts index 9f7ae6e35e..be917f4d6d 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/shape.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/shape.ts @@ -1,46 +1,45 @@ import type { MindMapTreeNode } from '../../../types/mindmap.js'; import { getShapeText, getShapeType } from '../../../utils/text.js'; -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const shapeToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { - name: 'shape', - match: elementModel => elementModel.type === 'shape', - toAST: (elementModel, context) => { - let content = ''; - const { walkerContext } = context; - const mindMapNodeMaps = walkerContext.getGlobalContext( - 'surface:mindMap:nodeMapArray' - ) as Array>; - if (mindMapNodeMaps && mindMapNodeMaps.length > 0) { - // Check if the elementModel is a mindMap node - // If it is, we should return { content: '' } directly - // And get the content when we handle the whole mindMap - const isMindMapNode = mindMapNodeMaps.some(nodeMap => - nodeMap.has(elementModel.id as string) - ); - if (isMindMapNode) { - return null; - } - } - - // If it is not, we should return the text and shapeType - const text = getShapeText(elementModel); - const type = getShapeType(elementModel); - if (!text && !type) { +export const shapeToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({ + name: 'shape', + match: elementModel => elementModel.type === 'shape', + toAST: (elementModel, context) => { + let content = ''; + const { walkerContext } = context; + const mindMapNodeMaps = walkerContext.getGlobalContext( + 'surface:mindMap:nodeMapArray' + ) as Array>; + if (mindMapNodeMaps && mindMapNodeMaps.length > 0) { + // Check if the elementModel is a mindMap node + // If it is, we should return { content: '' } directly + // And get the content when we handle the whole mindMap + const isMindMapNode = mindMapNodeMaps.some(nodeMap => + nodeMap.has(elementModel.id as string) + ); + if (isMindMapNode) { return null; } + } - const shapeType = type.charAt(0).toUpperCase() + type.slice(1); - content = `${shapeType}, with text label "${text}"`; - return { - type: 'paragraph', - children: [ - { - type: 'text', - value: content, - }, - ], - }; - }, - }; + // If it is not, we should return the text and shapeType + const text = getShapeText(elementModel); + const type = getShapeType(elementModel); + if (!text && !type) { + return null; + } + + const shapeType = type.charAt(0).toUpperCase() + type.slice(1); + content = `${shapeType}, with text label "${text}"`; + return { + type: 'paragraph', + children: [ + { + type: 'text', + value: content, + }, + ], + }; + }, +}); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/text.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/text.ts index 4528cc8cf4..3c4159b56e 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/text.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/elements/text.ts @@ -1,24 +1,23 @@ import { getTextElementText } from '../../../utils/text.js'; -import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; +import { ElementToMarkdownAdapterExtension } from '../type.js'; -export const textToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = - { - name: 'text', - match: elementModel => elementModel.type === 'text', - toAST: elementModel => { - const content = getTextElementText(elementModel); - if (!content) { - return null; - } +export const textToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({ + name: 'text', + match: elementModel => elementModel.type === 'text', + toAST: elementModel => { + const content = getTextElementText(elementModel); + if (!content) { + return null; + } - return { - type: 'paragraph', - children: [ - { - type: 'text', - value: content, - }, - ], - }; - }, - }; + return { + type: 'paragraph', + children: [ + { + type: 'text', + value: content, + }, + ], + }; + }, +}); diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/index.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/index.ts index 7c7211bdf7..969564ede4 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/index.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/index.ts @@ -4,15 +4,14 @@ import { ElementModelAdapter, type ElementModelAdapterContext, } from '../../type.js'; -import { elementToMarkdownAdapterMatchers } from './elements/index.js'; -import type { ElementModelToMarkdownAdapterMatcher } from './type.js'; +import type { ElementToMarkdownAdapterMatcher } from './type.js'; export class MarkdownElementModelAdapter extends ElementModelAdapter< MarkdownAST, MarkdownAST > { constructor( - readonly elementModelMatchers: ElementModelToMarkdownAdapterMatcher[] = elementToMarkdownAdapterMatchers + readonly elementModelMatchers: ElementToMarkdownAdapterMatcher[] ) { super(); } diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/type.ts b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/type.ts index ee51e534ad..8152f3455b 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/type.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/element-adapter/type.ts @@ -1,6 +1,29 @@ +import type { ExtensionType } from '@blocksuite/affine/store'; import type { MarkdownAST } from '@blocksuite/affine-shared/adapters'; +import { + createIdentifier, + type ServiceIdentifier, +} from '@blocksuite/global/di'; import type { ElementModelMatcher } from '../../type.js'; -export type ElementModelToMarkdownAdapterMatcher = - ElementModelMatcher; +export type ElementToMarkdownAdapterMatcher = ElementModelMatcher; + +export const ElementToMarkdownAdapterMatcherIdentifier = + createIdentifier( + 'elementToMarkdownAdapterMatcher' + ); + +export function ElementToMarkdownAdapterExtension( + matcher: ElementToMarkdownAdapterMatcher +): ExtensionType & { + identifier: ServiceIdentifier; +} { + const identifier = ElementToMarkdownAdapterMatcherIdentifier(matcher.name); + return { + setup: di => { + di.addImpl(identifier, () => matcher); + }, + identifier, + }; +} diff --git a/blocksuite/affine/block-surface/src/adapters/markdown/markdown.ts b/blocksuite/affine/block-surface/src/adapters/markdown/markdown.ts index d0d3675341..9916145152 100644 --- a/blocksuite/affine/block-surface/src/adapters/markdown/markdown.ts +++ b/blocksuite/affine/block-surface/src/adapters/markdown/markdown.ts @@ -5,6 +5,7 @@ import { import { getMindMapNodeMap } from '../utils/mindmap.js'; import { MarkdownElementModelAdapter } from './element-adapter/index.js'; +import { ElementToMarkdownAdapterMatcherIdentifier } from './element-adapter/type.js'; export const surfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = { flavour: 'affine:surface', @@ -29,8 +30,18 @@ export const edgelessSurfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMat toBlockSnapshot: {}, fromBlockSnapshot: { enter: (o, context) => { - const { walkerContext } = context; - const markdownElementModelAdapter = new MarkdownElementModelAdapter(); + const { walkerContext, provider } = context; + if (!provider) { + context.walkerContext.skipAllChildren(); + return; + } + + const elementModelMatchers = Array.from( + provider.getAll(ElementToMarkdownAdapterMatcherIdentifier).values() + ); + const markdownElementModelAdapter = new MarkdownElementModelAdapter( + elementModelMatchers + ); if ('elements' in o.node.props) { const elements = o.node.props.elements as Record< string, diff --git a/blocksuite/affine/shared/src/adapters/markdown/markdown.ts b/blocksuite/affine/shared/src/adapters/markdown/markdown.ts index 1baf7af3b1..5e76d375fc 100644 --- a/blocksuite/affine/shared/src/adapters/markdown/markdown.ts +++ b/blocksuite/affine/shared/src/adapters/markdown/markdown.ts @@ -72,6 +72,7 @@ export class MarkdownAdapter extends BaseAdapter { configs: this.configs, job: this.job, deltaConverter: this.deltaConverter, + provider: this.provider, textBuffer: { content: '' }, assets, }; @@ -92,6 +93,7 @@ export class MarkdownAdapter extends BaseAdapter { configs: this.configs, job: this.job, deltaConverter: this.deltaConverter, + provider: this.provider, textBuffer: { content: '' }, assets, }; @@ -126,6 +128,7 @@ export class MarkdownAdapter extends BaseAdapter { configs: this.configs, job: this.job, deltaConverter: this.deltaConverter, + provider: this.provider, textBuffer: { content: '' }, assets, updateAssetIds: (assetsId: string) => { @@ -149,6 +152,7 @@ export class MarkdownAdapter extends BaseAdapter { configs: this.configs, job: this.job, deltaConverter: this.deltaConverter, + provider: this.provider, textBuffer: { content: '' }, assets, }; @@ -166,7 +170,10 @@ export class MarkdownAdapter extends BaseAdapter { readonly blockMatchers: BlockMarkdownAdapterMatcher[]; - constructor(job: Job, provider: ServiceProvider) { + constructor( + job: Job, + readonly provider: ServiceProvider + ) { super(job); const blockMatchers = Array.from( provider.getAll(BlockMarkdownAdapterMatcherIdentifier).values()