mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-26 10:45:57 +08: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 {
|
import {
|
||||||
EdgelessSurfaceBlockMarkdownAdapterExtension,
|
EdgelessSurfaceBlockMarkdownAdapterExtension,
|
||||||
SurfaceBlockMarkdownAdapterExtension,
|
SurfaceBlockMarkdownAdapterExtension,
|
||||||
@@ -10,12 +11,14 @@ import {
|
|||||||
|
|
||||||
export const SurfaceBlockAdapterExtensions = [
|
export const SurfaceBlockAdapterExtensions = [
|
||||||
...elementToPlainTextAdapterMatchers,
|
...elementToPlainTextAdapterMatchers,
|
||||||
|
...elementToMarkdownAdapterMatchers,
|
||||||
SurfaceBlockPlainTextAdapterExtension,
|
SurfaceBlockPlainTextAdapterExtension,
|
||||||
SurfaceBlockMarkdownAdapterExtension,
|
SurfaceBlockMarkdownAdapterExtension,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const EdgelessSurfaceBlockAdapterExtensions = [
|
export const EdgelessSurfaceBlockAdapterExtensions = [
|
||||||
...elementToPlainTextAdapterMatchers,
|
...elementToPlainTextAdapterMatchers,
|
||||||
|
...elementToMarkdownAdapterMatchers,
|
||||||
EdgelessSurfaceBlockPlainTextAdapterExtension,
|
EdgelessSurfaceBlockPlainTextAdapterExtension,
|
||||||
EdgelessSurfaceBlockMarkdownAdapterExtension,
|
EdgelessSurfaceBlockMarkdownAdapterExtension,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js';
|
import { ElementToMarkdownAdapterExtension } from '../type.js';
|
||||||
|
|
||||||
export const brushToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher =
|
export const brushToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||||
{
|
name: 'brush',
|
||||||
name: 'brush',
|
match: elementModel => elementModel.type === 'brush',
|
||||||
match: elementModel => elementModel.type === 'brush',
|
toAST: () => {
|
||||||
toAST: () => {
|
const content = `Brush Stroke`;
|
||||||
const content = `Brush Stroke`;
|
return {
|
||||||
return {
|
type: 'paragraph',
|
||||||
type: 'paragraph',
|
children: [
|
||||||
children: [
|
{
|
||||||
{
|
type: 'text',
|
||||||
type: 'text',
|
value: content,
|
||||||
value: content,
|
},
|
||||||
},
|
],
|
||||||
],
|
};
|
||||||
};
|
},
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { getConnectorText } from '../../../utils/text.js';
|
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',
|
name: 'connector',
|
||||||
match: elementModel => elementModel.type === 'connector',
|
match: elementModel => elementModel.type === 'connector',
|
||||||
toAST: elementModel => {
|
toAST: elementModel => {
|
||||||
@@ -22,4 +22,4 @@ export const connectorToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMat
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import { getGroupTitle } from '../../../utils/text.js';
|
import { getGroupTitle } from '../../../utils/text.js';
|
||||||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js';
|
import { ElementToMarkdownAdapterExtension } from '../type.js';
|
||||||
|
|
||||||
export const groupToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher =
|
export const groupToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||||
{
|
name: 'group',
|
||||||
name: 'group',
|
match: elementModel => elementModel.type === 'group',
|
||||||
match: elementModel => elementModel.type === 'group',
|
toAST: elementModel => {
|
||||||
toAST: elementModel => {
|
const title = getGroupTitle(elementModel);
|
||||||
const title = getGroupTitle(elementModel);
|
if (!title) {
|
||||||
if (!title) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const content = `Group, with title "${title}"`;
|
const content = `Group, with title "${title}"`;
|
||||||
return {
|
return {
|
||||||
type: 'paragraph',
|
type: 'paragraph',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: content,
|
value: content,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import type { MindMapTreeNode } from '../../../types/mindmap.js';
|
import type { MindMapTreeNode } from '../../../types/mindmap.js';
|
||||||
import { buildMindMapTree } from '../../../utils/mindmap.js';
|
import { buildMindMapTree } from '../../../utils/mindmap.js';
|
||||||
import { getShapeText } from '../../../utils/text.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',
|
name: 'mindmap',
|
||||||
match: elementModel => elementModel.type === 'mindmap',
|
match: elementModel => elementModel.type === 'mindmap',
|
||||||
toAST: (elementModel, context) => {
|
toAST: (elementModel, context) => {
|
||||||
@@ -64,4 +64,4 @@ export const mindmapToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatch
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -1,46 +1,45 @@
|
|||||||
import type { MindMapTreeNode } from '../../../types/mindmap.js';
|
import type { MindMapTreeNode } from '../../../types/mindmap.js';
|
||||||
import { getShapeText, getShapeType } from '../../../utils/text.js';
|
import { getShapeText, getShapeType } from '../../../utils/text.js';
|
||||||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js';
|
import { ElementToMarkdownAdapterExtension } from '../type.js';
|
||||||
|
|
||||||
export const shapeToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher =
|
export const shapeToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||||
{
|
name: 'shape',
|
||||||
name: 'shape',
|
match: elementModel => elementModel.type === 'shape',
|
||||||
match: elementModel => elementModel.type === 'shape',
|
toAST: (elementModel, context) => {
|
||||||
toAST: (elementModel, context) => {
|
let content = '';
|
||||||
let content = '';
|
const { walkerContext } = context;
|
||||||
const { walkerContext } = context;
|
const mindMapNodeMaps = walkerContext.getGlobalContext(
|
||||||
const mindMapNodeMaps = walkerContext.getGlobalContext(
|
'surface:mindMap:nodeMapArray'
|
||||||
'surface:mindMap:nodeMapArray'
|
) as Array<Map<string, MindMapTreeNode>>;
|
||||||
) as Array<Map<string, MindMapTreeNode>>;
|
if (mindMapNodeMaps && mindMapNodeMaps.length > 0) {
|
||||||
if (mindMapNodeMaps && mindMapNodeMaps.length > 0) {
|
// Check if the elementModel is a mindMap node
|
||||||
// Check if the elementModel is a mindMap node
|
// If it is, we should return { content: '' } directly
|
||||||
// If it is, we should return { content: '' } directly
|
// And get the content when we handle the whole mindMap
|
||||||
// And get the content when we handle the whole mindMap
|
const isMindMapNode = mindMapNodeMaps.some(nodeMap =>
|
||||||
const isMindMapNode = mindMapNodeMaps.some(nodeMap =>
|
nodeMap.has(elementModel.id as string)
|
||||||
nodeMap.has(elementModel.id as string)
|
);
|
||||||
);
|
if (isMindMapNode) {
|
||||||
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const shapeType = type.charAt(0).toUpperCase() + type.slice(1);
|
// If it is not, we should return the text and shapeType
|
||||||
content = `${shapeType}, with text label "${text}"`;
|
const text = getShapeText(elementModel);
|
||||||
return {
|
const type = getShapeType(elementModel);
|
||||||
type: 'paragraph',
|
if (!text && !type) {
|
||||||
children: [
|
return null;
|
||||||
{
|
}
|
||||||
type: 'text',
|
|
||||||
value: content,
|
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 { getTextElementText } from '../../../utils/text.js';
|
||||||
import type { ElementModelToMarkdownAdapterMatcher } from '../type.js';
|
import { ElementToMarkdownAdapterExtension } from '../type.js';
|
||||||
|
|
||||||
export const textToMarkdownAdapterMatcher: ElementModelToMarkdownAdapterMatcher =
|
export const textToMarkdownAdapterMatcher = ElementToMarkdownAdapterExtension({
|
||||||
{
|
name: 'text',
|
||||||
name: 'text',
|
match: elementModel => elementModel.type === 'text',
|
||||||
match: elementModel => elementModel.type === 'text',
|
toAST: elementModel => {
|
||||||
toAST: elementModel => {
|
const content = getTextElementText(elementModel);
|
||||||
const content = getTextElementText(elementModel);
|
if (!content) {
|
||||||
if (!content) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'paragraph',
|
type: 'paragraph',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: content,
|
value: content,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -4,15 +4,14 @@ import {
|
|||||||
ElementModelAdapter,
|
ElementModelAdapter,
|
||||||
type ElementModelAdapterContext,
|
type ElementModelAdapterContext,
|
||||||
} from '../../type.js';
|
} from '../../type.js';
|
||||||
import { elementToMarkdownAdapterMatchers } from './elements/index.js';
|
import type { ElementToMarkdownAdapterMatcher } from './type.js';
|
||||||
import type { ElementModelToMarkdownAdapterMatcher } from './type.js';
|
|
||||||
|
|
||||||
export class MarkdownElementModelAdapter extends ElementModelAdapter<
|
export class MarkdownElementModelAdapter extends ElementModelAdapter<
|
||||||
MarkdownAST,
|
MarkdownAST,
|
||||||
MarkdownAST
|
MarkdownAST
|
||||||
> {
|
> {
|
||||||
constructor(
|
constructor(
|
||||||
readonly elementModelMatchers: ElementModelToMarkdownAdapterMatcher[] = elementToMarkdownAdapterMatchers
|
readonly elementModelMatchers: ElementToMarkdownAdapterMatcher[]
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
|
import type { ExtensionType } from '@blocksuite/affine/store';
|
||||||
import type { MarkdownAST } from '@blocksuite/affine-shared/adapters';
|
import type { MarkdownAST } from '@blocksuite/affine-shared/adapters';
|
||||||
|
import {
|
||||||
|
createIdentifier,
|
||||||
|
type ServiceIdentifier,
|
||||||
|
} from '@blocksuite/global/di';
|
||||||
|
|
||||||
import type { ElementModelMatcher } from '../../type.js';
|
import type { ElementModelMatcher } from '../../type.js';
|
||||||
|
|
||||||
export type ElementModelToMarkdownAdapterMatcher =
|
export type ElementToMarkdownAdapterMatcher = ElementModelMatcher<MarkdownAST>;
|
||||||
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 { getMindMapNodeMap } from '../utils/mindmap.js';
|
||||||
import { MarkdownElementModelAdapter } from './element-adapter/index.js';
|
import { MarkdownElementModelAdapter } from './element-adapter/index.js';
|
||||||
|
import { ElementToMarkdownAdapterMatcherIdentifier } from './element-adapter/type.js';
|
||||||
|
|
||||||
export const surfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
export const surfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher = {
|
||||||
flavour: 'affine:surface',
|
flavour: 'affine:surface',
|
||||||
@@ -29,8 +30,18 @@ export const edgelessSurfaceBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMat
|
|||||||
toBlockSnapshot: {},
|
toBlockSnapshot: {},
|
||||||
fromBlockSnapshot: {
|
fromBlockSnapshot: {
|
||||||
enter: (o, context) => {
|
enter: (o, context) => {
|
||||||
const { walkerContext } = context;
|
const { walkerContext, provider } = context;
|
||||||
const markdownElementModelAdapter = new MarkdownElementModelAdapter();
|
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) {
|
if ('elements' in o.node.props) {
|
||||||
const elements = o.node.props.elements as Record<
|
const elements = o.node.props.elements as Record<
|
||||||
string,
|
string,
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
configs: this.configs,
|
configs: this.configs,
|
||||||
job: this.job,
|
job: this.job,
|
||||||
deltaConverter: this.deltaConverter,
|
deltaConverter: this.deltaConverter,
|
||||||
|
provider: this.provider,
|
||||||
textBuffer: { content: '' },
|
textBuffer: { content: '' },
|
||||||
assets,
|
assets,
|
||||||
};
|
};
|
||||||
@@ -92,6 +93,7 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
configs: this.configs,
|
configs: this.configs,
|
||||||
job: this.job,
|
job: this.job,
|
||||||
deltaConverter: this.deltaConverter,
|
deltaConverter: this.deltaConverter,
|
||||||
|
provider: this.provider,
|
||||||
textBuffer: { content: '' },
|
textBuffer: { content: '' },
|
||||||
assets,
|
assets,
|
||||||
};
|
};
|
||||||
@@ -126,6 +128,7 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
configs: this.configs,
|
configs: this.configs,
|
||||||
job: this.job,
|
job: this.job,
|
||||||
deltaConverter: this.deltaConverter,
|
deltaConverter: this.deltaConverter,
|
||||||
|
provider: this.provider,
|
||||||
textBuffer: { content: '' },
|
textBuffer: { content: '' },
|
||||||
assets,
|
assets,
|
||||||
updateAssetIds: (assetsId: string) => {
|
updateAssetIds: (assetsId: string) => {
|
||||||
@@ -149,6 +152,7 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
configs: this.configs,
|
configs: this.configs,
|
||||||
job: this.job,
|
job: this.job,
|
||||||
deltaConverter: this.deltaConverter,
|
deltaConverter: this.deltaConverter,
|
||||||
|
provider: this.provider,
|
||||||
textBuffer: { content: '' },
|
textBuffer: { content: '' },
|
||||||
assets,
|
assets,
|
||||||
};
|
};
|
||||||
@@ -166,7 +170,10 @@ export class MarkdownAdapter extends BaseAdapter<Markdown> {
|
|||||||
|
|
||||||
readonly blockMatchers: BlockMarkdownAdapterMatcher[];
|
readonly blockMatchers: BlockMarkdownAdapterMatcher[];
|
||||||
|
|
||||||
constructor(job: Job, provider: ServiceProvider) {
|
constructor(
|
||||||
|
job: Job,
|
||||||
|
readonly provider: ServiceProvider
|
||||||
|
) {
|
||||||
super(job);
|
super(job);
|
||||||
const blockMatchers = Array.from(
|
const blockMatchers = Array.from(
|
||||||
provider.getAll(BlockMarkdownAdapterMatcherIdentifier).values()
|
provider.getAll(BlockMarkdownAdapterMatcherIdentifier).values()
|
||||||
|
|||||||
Reference in New Issue
Block a user