mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 21:38:44 +08:00
fix(editor): import & save logic (#15098)
fix #15080 fix #15085 fix #15031 fix #15094 #### PR Dependency Tree * **PR #15098** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved code-block paste behavior for plain-text insertion * Fixed block selection ordering to reflect document model * Made table cell formatting resilient to conversion errors * Ensured user feature list is consistently returned as an array * **Refactor** * Streamlined authentication session fetch and profile enrichment flow * **Tests** * Added tests for markdown blockquote list preservation * Added authentication session validation tests <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -5,10 +5,11 @@ import {
|
||||
IN_PARAGRAPH_NODE_CONTEXT_KEY,
|
||||
isCalloutNode,
|
||||
type MarkdownAST,
|
||||
type MarkdownDeltaConverter,
|
||||
} from '@blocksuite/affine-shared/adapters';
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
import type { BlockSnapshot, DeltaInsert } from '@blocksuite/store';
|
||||
import { nanoid } from '@blocksuite/store';
|
||||
import type { Heading } from 'mdast';
|
||||
import type { Blockquote, Heading, List, ListItem } from 'mdast';
|
||||
|
||||
/**
|
||||
* Extend the HeadingData type to include the collapsed property
|
||||
@@ -24,6 +25,131 @@ const PARAGRAPH_MDAST_TYPE = new Set(['paragraph', 'heading', 'blockquote']);
|
||||
const isParagraphMDASTType = (node: MarkdownAST) =>
|
||||
PARAGRAPH_MDAST_TYPE.has(node.type);
|
||||
|
||||
const joinDeltaLines = (
|
||||
lines: DeltaInsert[][],
|
||||
prefix?: string
|
||||
): DeltaInsert[] => {
|
||||
const deltas: DeltaInsert[] = [];
|
||||
lines.forEach(line => {
|
||||
if (deltas.length) deltas.push({ insert: '\n' });
|
||||
if (prefix) deltas.push({ insert: prefix });
|
||||
deltas.push(...line);
|
||||
});
|
||||
return deltas;
|
||||
};
|
||||
|
||||
const flattenListItemToDelta = (
|
||||
node: ListItem,
|
||||
deltaConverter: MarkdownDeltaConverter,
|
||||
prefix: string,
|
||||
depth: number
|
||||
): DeltaInsert[] => {
|
||||
const firstParagraph = node.children[0];
|
||||
const lines: DeltaInsert[][] = [];
|
||||
if (firstParagraph?.type === 'paragraph') {
|
||||
lines.push([
|
||||
{ insert: prefix },
|
||||
...deltaConverter.astToDelta(firstParagraph),
|
||||
]);
|
||||
} else {
|
||||
lines.push([{ insert: prefix.trimEnd() }]);
|
||||
}
|
||||
node.children
|
||||
.slice(firstParagraph?.type === 'paragraph' ? 1 : 0)
|
||||
.forEach(child => {
|
||||
const delta = flattenMarkdownBlockToDelta(
|
||||
child as MarkdownAST,
|
||||
deltaConverter,
|
||||
depth + 1
|
||||
);
|
||||
if (delta.length) {
|
||||
lines.push(delta);
|
||||
}
|
||||
});
|
||||
return joinDeltaLines(lines);
|
||||
};
|
||||
|
||||
const flattenMarkdownBlockToDelta = (
|
||||
node: MarkdownAST,
|
||||
deltaConverter: MarkdownDeltaConverter,
|
||||
depth = 0
|
||||
): DeltaInsert[] => {
|
||||
switch (node.type) {
|
||||
case 'paragraph':
|
||||
case 'heading':
|
||||
return deltaConverter.astToDelta(node);
|
||||
case 'list': {
|
||||
const list = node as List;
|
||||
return joinDeltaLines(
|
||||
list.children.map((item, index) => {
|
||||
const order = (list.start ?? 1) + index;
|
||||
const prefix =
|
||||
' '.repeat(depth) + (list.ordered ? `${order}. ` : '- ');
|
||||
return flattenListItemToDelta(item, deltaConverter, prefix, depth);
|
||||
})
|
||||
);
|
||||
}
|
||||
case 'blockquote':
|
||||
return flattenBlockquoteToDelta(node as Blockquote, deltaConverter);
|
||||
default:
|
||||
return 'children' in node
|
||||
? joinDeltaLines(
|
||||
(node.children as MarkdownAST[]).map(child =>
|
||||
flattenMarkdownBlockToDelta(child, deltaConverter, depth)
|
||||
)
|
||||
)
|
||||
: [];
|
||||
}
|
||||
};
|
||||
|
||||
const flattenBlockquoteToDelta = (
|
||||
node: Blockquote,
|
||||
deltaConverter: MarkdownDeltaConverter
|
||||
) =>
|
||||
joinDeltaLines(
|
||||
node.children.map(child =>
|
||||
flattenMarkdownBlockToDelta(child as MarkdownAST, deltaConverter)
|
||||
)
|
||||
);
|
||||
|
||||
const getSnapshotTextDelta = (node: BlockSnapshot): DeltaInsert[] => {
|
||||
const text = (node.props.text ?? { delta: [] }) as {
|
||||
delta: DeltaInsert[];
|
||||
};
|
||||
return text.delta;
|
||||
};
|
||||
|
||||
const flattenSnapshotBlockToDelta = (
|
||||
node: BlockSnapshot,
|
||||
depth = 0
|
||||
): DeltaInsert[] => {
|
||||
if (node.flavour === 'affine:list') {
|
||||
const type = node.props.type;
|
||||
const order = (node.props.order as number | undefined) ?? 1;
|
||||
const prefix =
|
||||
' '.repeat(depth) + (type === 'numbered' ? `${order}. ` : '- ');
|
||||
return joinDeltaLines([
|
||||
[{ insert: prefix }, ...getSnapshotTextDelta(node)],
|
||||
...node.children.map(child =>
|
||||
flattenSnapshotBlockToDelta(child, depth + 1)
|
||||
),
|
||||
]);
|
||||
}
|
||||
return joinDeltaLines([
|
||||
getSnapshotTextDelta(node),
|
||||
...node.children.map(child => flattenSnapshotBlockToDelta(child, depth)),
|
||||
]);
|
||||
};
|
||||
|
||||
const flattenQuoteSnapshotToDelta = (
|
||||
text: DeltaInsert[],
|
||||
children: BlockSnapshot[]
|
||||
) =>
|
||||
joinDeltaLines([
|
||||
text,
|
||||
...children.map(child => flattenSnapshotBlockToDelta(child)),
|
||||
]);
|
||||
|
||||
export const paragraphBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher =
|
||||
{
|
||||
flavour: ParagraphBlockSchema.model.flavour,
|
||||
@@ -93,7 +219,10 @@ export const paragraphBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher =
|
||||
type: 'quote',
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: deltaConverter.astToDelta(o.node),
|
||||
delta: flattenBlockquoteToDelta(
|
||||
o.node as Blockquote,
|
||||
deltaConverter
|
||||
),
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
@@ -160,6 +289,10 @@ export const paragraphBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher =
|
||||
break;
|
||||
}
|
||||
case 'quote': {
|
||||
const quoteDelta = flattenQuoteSnapshotToDelta(
|
||||
text.delta,
|
||||
o.node.children
|
||||
);
|
||||
walkerContext
|
||||
.openNode(
|
||||
{
|
||||
@@ -171,12 +304,13 @@ export const paragraphBlockMarkdownAdapterMatcher: BlockMarkdownAdapterMatcher =
|
||||
.openNode(
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: deltaConverter.deltaToAST(text.delta),
|
||||
children: deltaConverter.deltaToAST(quoteDelta),
|
||||
},
|
||||
'children'
|
||||
)
|
||||
.closeNode()
|
||||
.closeNode();
|
||||
walkerContext.skipAllChildren();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user