feat(editor): support footnote adapter (#9844)

[BS-2373](https://linear.app/affine-design/issue/BS-2373/适配-footnote-adapter)
This commit is contained in:
donteatfriedrice
2025-01-22 06:42:35 +00:00
parent a5025cf470
commit bf797c7a0c
15 changed files with 385 additions and 11 deletions

View File

@@ -0,0 +1,2 @@
export * from './inline-delta';
export * from './markdown-inline';

View File

@@ -1,4 +1,5 @@
import {
FOOTNOTE_DEFINITION_PREFIX,
InlineDeltaToMarkdownAdapterExtension,
TextUtils,
} from '@blocksuite/affine-shared/adapters';
@@ -146,6 +147,46 @@ export const latexDeltaToMarkdownAdapterMatcher =
},
});
export const footnoteReferenceDeltaToMarkdownAdapterMatcher =
InlineDeltaToMarkdownAdapterExtension({
name: 'footnote-reference',
match: delta => !!delta.attributes?.footnote,
toAST: (delta, context) => {
const mdast: PhrasingContent = {
type: 'text',
value: delta.insert,
};
const footnote = delta.attributes?.footnote;
if (!footnote) {
return mdast;
}
const footnoteDefinitionKey = `${FOOTNOTE_DEFINITION_PREFIX}${footnote.label}`;
const { configs } = context;
// FootnoteReference should be paired with FootnoteDefinition
// If the footnoteDefinition is not in the configs, set it to configs
// We should add the footnoteDefinition markdown ast nodes to tree after all the footnoteReference markdown ast nodes are added
if (!configs.has(footnoteDefinitionKey)) {
// clone the footnote reference
const clonedFootnoteReference = { ...footnote.reference };
// If the footnote reference contains url, encode it
if (clonedFootnoteReference.url) {
clonedFootnoteReference.url = encodeURIComponent(
clonedFootnoteReference.url
);
}
configs.set(
footnoteDefinitionKey,
JSON.stringify(clonedFootnoteReference)
);
}
return {
type: 'footnoteReference',
label: footnote.label,
identifier: footnote.label,
};
},
});
export const InlineDeltaToMarkdownAdapterExtensions = [
referenceDeltaToMarkdownAdapterMatcher,
linkDeltaToMarkdownAdapterMatcher,
@@ -154,4 +195,5 @@ export const InlineDeltaToMarkdownAdapterExtensions = [
italicDeltaToMarkdownAdapterMatcher,
strikeDeltaToMarkdownAdapterMatcher,
latexDeltaToMarkdownAdapterMatcher,
footnoteReferenceDeltaToMarkdownAdapterMatcher,
];

View File

@@ -1,4 +1,8 @@
import { MarkdownASTToDeltaExtension } from '@blocksuite/affine-shared/adapters';
import { FootNoteReferenceParamsSchema } from '@blocksuite/affine-model';
import {
FOOTNOTE_DEFINITION_PREFIX,
MarkdownASTToDeltaExtension,
} from '@blocksuite/affine-shared/adapters';
export const markdownTextToDeltaMatcher = MarkdownASTToDeltaExtension({
name: 'text',
@@ -138,6 +142,43 @@ export const markdownInlineMathToDeltaMatcher = MarkdownASTToDeltaExtension({
},
});
export const markdownFootnoteReferenceToDeltaMatcher =
MarkdownASTToDeltaExtension({
name: 'footnote-reference',
match: ast => ast.type === 'footnoteReference',
toDelta: (ast, context) => {
if (ast.type !== 'footnoteReference') {
return [];
}
try {
const { configs } = context;
const footnoteDefinitionKey = `${FOOTNOTE_DEFINITION_PREFIX}${ast.identifier}`;
const footnoteDefinition = configs.get(footnoteDefinitionKey);
if (!footnoteDefinition) {
return [];
}
const footnoteDefinitionJson = JSON.parse(footnoteDefinition);
// If the footnote definition contains url, decode it
if (footnoteDefinitionJson.url) {
footnoteDefinitionJson.url = decodeURIComponent(
footnoteDefinitionJson.url
);
}
const footnoteReference = FootNoteReferenceParamsSchema.parse(
footnoteDefinitionJson
);
const footnote = {
label: ast.identifier,
reference: footnoteReference,
};
return [{ insert: ' ', attributes: { footnote } }];
} catch (error) {
console.error('Error parsing footnote reference', error);
return [];
}
},
});
export const MarkdownInlineToDeltaAdapterExtensions = [
markdownTextToDeltaMatcher,
markdownInlineCodeToDeltaMatcher,
@@ -147,4 +188,5 @@ export const MarkdownInlineToDeltaAdapterExtensions = [
markdownLinkToDeltaMatcher,
markdownInlineMathToDeltaMatcher,
markdownListToDeltaMatcher,
markdownFootnoteReferenceToDeltaMatcher,
];

View File

@@ -1,8 +1,7 @@
export * from './adapters/extensions';
export * from './adapters/html/html-inline';
export * from './adapters/html/inline-delta';
export * from './adapters/markdown/inline-delta';
export * from './adapters/markdown/markdown-inline';
export * from './adapters/markdown';
export * from './adapters/notion-html/html-inline';
export * from './adapters/plain-text/inline-delta';
export * from './presets/affine-inline-specs';