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

@@ -2350,6 +2350,109 @@ World!
});
expect(target.file).toBe(docMd);
});
test('footnote', async () => {
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'block:vu6SK6WJpW',
flavour: 'affine:page',
props: {
title: {
'$blocksuite:internal:text$': true,
delta: [],
},
},
children: [
{
type: 'block',
id: 'block:Tk4gSPocAt',
flavour: 'affine:surface',
props: {
elements: {},
},
children: [],
},
{
type: 'block',
id: 'block:WfnS5ZDCJT',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'block:zxDyvrg1Mh',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'aaa',
},
{
insert: ' ',
attributes: {
footnote: {
label: '1',
reference: {
type: 'url',
url: 'https://www.example.com',
},
},
},
},
{
insert: ' ',
attributes: {
footnote: {
label: '2',
reference: {
type: 'doc',
docId: 'deadbeef',
},
},
},
},
{
insert: ' ',
attributes: {
footnote: {
label: '3',
reference: {
type: 'attachment',
blobId: 'abcdefg',
fileName: 'test.txt',
fileType: 'text/plain',
},
},
},
},
],
},
},
children: [],
},
],
},
],
};
const markdown =
'aaa[^1][^2][^3]\n\n[^1]: {"type":"url","url":"https%3A%2F%2Fwww.example.com"}\n\n[^2]: {"type":"doc","docId":"deadbeef"}\n\n[^3]: {"type":"attachment","blobId":"abcdefg","fileName":"test.txt","fileType":"text/plain"}\n';
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const target = await mdAdapter.fromBlockSnapshot({
snapshot: blockSnapshot,
});
expect(target.file).toBe(markdown);
});
});
describe('markdown to snapshot', () => {
@@ -3858,4 +3961,85 @@ hhh
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
test('without footnote middleware', async () => {
const markdown =
'aaa[^1][^2][^3]\n\n[^1]: {"type":"url","url":"https%3A%2F%2Fwww.example.com"}\n\n[^2]: {"type":"doc","docId":"deadbeef"}\n\n[^3]: {"type":"attachment","blobId":"abcdefg","fileName":"test.txt","fileType":"text/plain"}\n';
const blockSnapshot: BlockSnapshot = {
type: 'block',
id: 'matchesReplaceMap[0]',
flavour: 'affine:note',
props: {
xywh: '[0,0,800,95]',
background: DefaultTheme.noteBackgrounColor,
index: 'a0',
hidden: false,
displayMode: NoteDisplayMode.DocAndEdgeless,
},
children: [
{
type: 'block',
id: 'matchesReplaceMap[1]',
flavour: 'affine:paragraph',
props: {
type: 'text',
text: {
'$blocksuite:internal:text$': true,
delta: [
{
insert: 'aaa',
},
{
insert: ' ',
attributes: {
footnote: {
label: '1',
reference: {
type: 'url',
url: 'https://www.example.com',
},
},
},
},
{
insert: ' ',
attributes: {
footnote: {
label: '2',
reference: {
type: 'doc',
docId: 'deadbeef',
},
},
},
},
{
insert: ' ',
attributes: {
footnote: {
label: '3',
reference: {
type: 'attachment',
blobId: 'abcdefg',
fileName: 'test.txt',
fileType: 'text/plain',
},
},
},
},
],
},
},
children: [],
},
],
};
const mdAdapter = new MarkdownAdapter(createJob(), provider);
const rawBlockSnapshot = await mdAdapter.toBlockSnapshot({
file: markdown,
});
expect(nanoidReplacement(rawBlockSnapshot)).toEqual(blockSnapshot);
});
});

View File

@@ -13,11 +13,14 @@ import {
import { ImageBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-image';
import { LatexBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-latex';
import { ListBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-list';
import { DocNoteBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-note';
import { ParagraphBlockMarkdownAdapterExtension } from '@blocksuite/affine-block-paragraph';
import { RootBlockMarkdownAdapterExtension } from '../../../root-block/adapters/markdown.js';
export const defaultBlockMarkdownAdapterMatchers = [
RootBlockMarkdownAdapterExtension,
DocNoteBlockMarkdownAdapterExtension,
EmbedFigmaMarkdownAdapterExtension,
EmbedGithubMarkdownAdapterExtension,
EmbedLinkedDocMarkdownAdapterExtension,
@@ -32,5 +35,4 @@ export const defaultBlockMarkdownAdapterMatchers = [
DividerBlockMarkdownAdapterExtension,
ImageBlockMarkdownAdapterExtension,
LatexBlockMarkdownAdapterExtension,
RootBlockMarkdownAdapterExtension,
];

View File

@@ -26,6 +26,7 @@ const escapedSnapshotAttributes = new Set([
'"background"',
'"LinkedPage"',
'"elementIds"',
'"attachment"',
]);
function nanoidReplacementString(snapshotString: string) {