mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
refactor(editor): surface markdown adapter extensionalize (#9560)
[BS-2212](https://linear.app/affine-design/issue/BS-2212/adapter-extension化修复)
This commit is contained in:
@@ -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,
|
||||
];
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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<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) {
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<MarkdownAST>;
|
||||
export type ElementToMarkdownAdapterMatcher = ElementModelMatcher<MarkdownAST>;
|
||||
|
||||
export const ElementToMarkdownAdapterMatcherIdentifier =
|
||||
createIdentifier<ElementToMarkdownAdapterMatcher>(
|
||||
'elementToMarkdownAdapterMatcher'
|
||||
);
|
||||
|
||||
export function ElementToMarkdownAdapterExtension(
|
||||
matcher: ElementToMarkdownAdapterMatcher
|
||||
): ExtensionType & {
|
||||
identifier: ServiceIdentifier<ElementToMarkdownAdapterMatcher>;
|
||||
} {
|
||||
const identifier = ElementToMarkdownAdapterMatcherIdentifier(matcher.name);
|
||||
return {
|
||||
setup: di => {
|
||||
di.addImpl(identifier, () => matcher);
|
||||
},
|
||||
identifier,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user