-
-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(blocks): add markdown adapter for surface elements (#9017)
- Loading branch information
1 parent
09ffcf2
commit c64d99f
Showing
20 changed files
with
518 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,18 @@ | ||
import { | ||
EdgelessSurfaceBlockMarkdownAdapterExtension, | ||
SurfaceBlockMarkdownAdapterExtension, | ||
} from './markdown/markdown.js'; | ||
import { | ||
EdgelessSurfaceBlockPlainTextAdapterExtension, | ||
SurfaceBlockPlainTextAdapterExtension, | ||
} from './plain-text/plain-text.js'; | ||
|
||
export const SurfaceBlockAdapterExtensions = [ | ||
SurfaceBlockPlainTextAdapterExtension, | ||
SurfaceBlockMarkdownAdapterExtension, | ||
]; | ||
|
||
export const EdgelessSurfaceBlockAdapterExtensions = [ | ||
EdgelessSurfaceBlockPlainTextAdapterExtension, | ||
EdgelessSurfaceBlockMarkdownAdapterExtension, | ||
]; |
19 changes: 19 additions & 0 deletions
19
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/brush.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { ElementModelToMarkdownAdapterMatcher } 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, | ||
}, | ||
], | ||
}; | ||
}, | ||
}; |
26 changes: 26 additions & 0 deletions
26
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/connector.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; | ||
|
||
import { getConnectorText } from '../../../utils/text.js'; | ||
|
||
export const connectorToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = | ||
{ | ||
name: 'connector', | ||
match: elementModel => elementModel.type === 'connector', | ||
toAST: elementModel => { | ||
const text = getConnectorText(elementModel); | ||
if (!text) { | ||
return null; | ||
} | ||
|
||
const content = `Connector, with text label "${text}"`; | ||
return { | ||
type: 'paragraph', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: content, | ||
}, | ||
], | ||
}; | ||
}, | ||
}; |
26 changes: 26 additions & 0 deletions
26
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/group.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; | ||
|
||
import { getGroupTitle } from '../../../utils/text.js'; | ||
|
||
export const groupToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = | ||
{ | ||
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, | ||
}, | ||
], | ||
}; | ||
}, | ||
}; |
15 changes: 15 additions & 0 deletions
15
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { brushToMarkdownAdapterMatcher } from './brush.js'; | ||
import { connectorToMarkdownAdapterMatcher } from './connector.js'; | ||
import { groupToMarkdownAdapterMatcher } from './group.js'; | ||
import { mindmapToMarkdownAdapterMatcher } from './mindmap.js'; | ||
import { shapeToMarkdownAdapterMatcher } from './shape.js'; | ||
import { textToMarkdownAdapterMatcher } from './text.js'; | ||
|
||
export const elementToMarkdownAdapterMatchers = [ | ||
groupToMarkdownAdapterMatcher, | ||
shapeToMarkdownAdapterMatcher, | ||
connectorToMarkdownAdapterMatcher, | ||
brushToMarkdownAdapterMatcher, | ||
textToMarkdownAdapterMatcher, | ||
mindmapToMarkdownAdapterMatcher, | ||
]; |
68 changes: 68 additions & 0 deletions
68
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/mindmap.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import type { MindMapTreeNode } from '../../../types/mindmap.js'; | ||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; | ||
|
||
import { buildMindMapTree } from '../../../utils/mindmap.js'; | ||
import { getShapeText } from '../../../utils/text.js'; | ||
|
||
export const mindmapToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = | ||
{ | ||
name: 'mindmap', | ||
match: elementModel => elementModel.type === 'mindmap', | ||
toAST: (elementModel, context) => { | ||
if (elementModel.type !== 'mindmap') { | ||
return null; | ||
} | ||
|
||
const mindMapTree = buildMindMapTree(elementModel); | ||
if (!mindMapTree) { | ||
return null; | ||
} | ||
|
||
const { walkerContext, elements } = context; | ||
const traverseMindMapTree = (node: MindMapTreeNode) => { | ||
const shapeElement = elements[node.id as string]; | ||
const shapeText = getShapeText(shapeElement); | ||
walkerContext | ||
.openNode( | ||
{ | ||
type: 'listItem', | ||
spread: false, | ||
children: [], | ||
}, | ||
'children' | ||
) | ||
.openNode( | ||
{ | ||
type: 'paragraph', | ||
children: [ | ||
{ | ||
type: 'text', | ||
value: shapeText, | ||
}, | ||
], | ||
}, | ||
'children' | ||
) | ||
.closeNode(); | ||
node.children.forEach(child => { | ||
traverseMindMapTree(child); | ||
walkerContext.closeNode(); | ||
}); | ||
}; | ||
|
||
// First create a list node for the mind map tree | ||
walkerContext.openNode( | ||
{ | ||
type: 'list', | ||
ordered: false, | ||
spread: false, | ||
children: [], | ||
}, | ||
'children' | ||
); | ||
traverseMindMapTree(mindMapTree); | ||
walkerContext.closeNode().closeNode(); | ||
|
||
return null; | ||
}, | ||
}; |
47 changes: 47 additions & 0 deletions
47
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/shape.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { MindMapTreeNode } from '../../../types/mindmap.js'; | ||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; | ||
|
||
import { getShapeText, getShapeType } from '../../../utils/text.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<Map<string, MindMapTreeNode>>; | ||
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) { | ||
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, | ||
}, | ||
], | ||
}; | ||
}, | ||
}; |
25 changes: 25 additions & 0 deletions
25
packages/affine/block-surface/src/adapters/markdown/element-adapter/elements/text.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js'; | ||
|
||
import { getTextElementText } from '../../../utils/text.js'; | ||
|
||
export const textToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher = | ||
{ | ||
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, | ||
}, | ||
], | ||
}; | ||
}, | ||
}; |
33 changes: 33 additions & 0 deletions
33
packages/affine/block-surface/src/adapters/markdown/element-adapter/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import type { MarkdownAST } from '@blocksuite/affine-shared/adapters'; | ||
|
||
import type { ElementModelToMarkdownAdapterMatcher } from './type.js'; | ||
|
||
import { | ||
ElementModelAdapter, | ||
type ElementModelAdapterContext, | ||
} from '../../type.js'; | ||
import { elementToMarkdownAdapterMatchers } from './elements/index.js'; | ||
|
||
export class MarkdownElementModelAdapter extends ElementModelAdapter< | ||
MarkdownAST, | ||
MarkdownAST | ||
> { | ||
constructor( | ||
readonly elementModelMatchers: ElementModelToMarkdownAdapterMatcher[] = elementToMarkdownAdapterMatchers | ||
) { | ||
super(); | ||
} | ||
|
||
fromElementModel( | ||
element: Record<string, unknown>, | ||
context: ElementModelAdapterContext<MarkdownAST> | ||
) { | ||
const markdownAST: MarkdownAST | null = null; | ||
for (const matcher of this.elementModelMatchers) { | ||
if (matcher.match(element)) { | ||
return matcher.toAST(element, context); | ||
} | ||
} | ||
return markdownAST; | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
packages/affine/block-surface/src/adapters/markdown/element-adapter/type.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { MarkdownAST } from '@blocksuite/affine-shared/adapters'; | ||
|
||
import type { ElementModelMatcher } from '../../type.js'; | ||
|
||
export type ElementModelToMarkdownAdapterMatcher = | ||
ElementModelMatcher<MarkdownAST>; |
66 changes: 66 additions & 0 deletions
66
packages/affine/block-surface/src/adapters/markdown/markdown.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { | ||
BlockMarkdownAdapterExtension, | ||
type BlockMarkdownAdapterMatcher, | ||
} from '@blocksuite/affine-shared/adapters'; | ||
|
||
import { SurfaceBlockSchema } from '../../surface-model.js'; | ||
import { getMindMapNodeMap } from '../utils/mindmap.js'; | ||
import { MarkdownElementModelAdapter } from './element-adapter/index.js'; | ||
|
||
export const surfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = { | ||
flavour: SurfaceBlockSchema.model.flavour, | ||
toMatch: () => false, | ||
fromMatch: o => o.node.flavour === SurfaceBlockSchema.model.flavour, | ||
toBlockSnapshot: {}, | ||
fromBlockSnapshot: { | ||
enter: (_, context) => { | ||
context.walkerContext.skipAllChildren(); | ||
}, | ||
}, | ||
}; | ||
|
||
export const SurfaceBlockMarkdownAdapterExtension = | ||
BlockMarkdownAdapterExtension(surfaceBlockMarkdownAdapterMatcher); | ||
|
||
export const edgelessSurfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = | ||
{ | ||
flavour: SurfaceBlockSchema.model.flavour, | ||
toMatch: () => false, | ||
fromMatch: o => o.node.flavour === SurfaceBlockSchema.model.flavour, | ||
toBlockSnapshot: {}, | ||
fromBlockSnapshot: { | ||
enter: (o, context) => { | ||
const { walkerContext } = context; | ||
const markdownElementModelAdapter = new MarkdownElementModelAdapter(); | ||
if ('elements' in o.node.props) { | ||
const elements = o.node.props.elements as Record< | ||
string, | ||
Record<string, unknown> | ||
>; | ||
// Get all the node maps of mindMap elements | ||
const mindMapArray = Object.entries(elements) | ||
.filter(([_, element]) => element.type === 'mindmap') | ||
.map(([_, element]) => getMindMapNodeMap(element)); | ||
walkerContext.setGlobalContext( | ||
'surface:mindMap:nodeMapArray', | ||
mindMapArray | ||
); | ||
|
||
Object.entries( | ||
o.node.props.elements as Record<string, Record<string, unknown>> | ||
).forEach(([_, element]) => { | ||
const markdownAST = markdownElementModelAdapter.fromElementModel( | ||
element, | ||
{ walkerContext, elements } | ||
); | ||
if (markdownAST) { | ||
walkerContext.openNode(markdownAST, 'children').closeNode(); | ||
} | ||
}); | ||
} | ||
}, | ||
}, | ||
}; | ||
|
||
export const EdgelessSurfaceBlockMarkdownAdapterExtension = | ||
BlockMarkdownAdapterExtension(edgelessSurfaceBlockMarkdownAdapterMatcher); |
17 changes: 3 additions & 14 deletions
17
packages/affine/block-surface/src/adapters/plain-text/element-adapter/elements/connector.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.