Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion lib/src/plugins/markdown/decoder/document_markdown_decoder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
encodeHtml: false,
).parse(formattedMarkdown);

final List<md.Node> processedNodes = _processNodes(mdNodes);
final document = Document.blank();
final nodes = mdNodes
final nodes = processedNodes
.map((e) => _parseNode(e))
.nonNulls
.flattened
Expand All @@ -42,6 +43,48 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
return document;
}

List<md.Node> _processNodes(List<md.Node> nodes) {
List<md.Node> result = [];

for (var node in nodes) {
if (node is md.Element &&
node.children != null &&
node.children!.length > 1) {
// Store image elements that need to be extracted
List<int> imageIndices = [];

// Find all image elements
for (var i = 0; i < node.children!.length; i++) {
var child = node.children![i];
if (child is md.Element && child.tag == 'img') {
imageIndices.add(i);
}
}

if (imageIndices.isNotEmpty) {
// Extract images from back to front to maintain correct indices
for (var i = imageIndices.length - 1; i >= 0; i--) {
var index = imageIndices[i];
var imageElement = node.children!.removeAt(index);
// Create a paragraph element containing the image
result.add(md.Element('p', [imageElement]));
}

// Add the original node if it still has children
if (node.children!.isNotEmpty) {
result.add(node);
}
} else {
result.add(node);
}
} else {
result.add(node);
}
}

return result;
}

// handle node itself and its children
List<Node> _parseNode(md.Node mdNode) {
List<Node> nodes = [];
Expand Down
26 changes: 26 additions & 0 deletions test/plugins/markdown/document_markdown_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,32 @@ void main() {
expect(nodes[0].delta?.toPlainText(), 'This is the first line');
expect(nodes[1].attributes['url'], 'https://example.com/image.png');
});

test('paragraph + image with custom style', () {
const markdown = '''![](http://test.com/1.png)
**第一幕:奇葩公司新规**
入职第一天发现我们公司有个祖传制度——迟到1分钟要讲1个笑话。前台小姐姐神秘兮兮地说:"上周市场部Jason讲了三个谐音梗,现在还在走廊罚站呢。"我盯着手机屏幕的9:01分,感觉今天要成为《饥饿游戏》真人版主角。

`![](http://test.com/2.png)
**第二幕:社恐の终极挑战**
当我抱着简历冲进会议室时,行政总监、HR和部门主管突然集体转身。别问,问就是三堂会审现场。更可怕的是行政总监脖子上挂着"今日段子质检员"工牌,手里还攥着评分表!此时投影仪突然发出放屁般的故障音,大老板推门而入:"听说新同事准备了特别节目?"''';
final document = markdownToDocument(markdown);
final nodes = document.root.children;
expect(nodes.length, 6); // 2 images + 4 paragraphs
expect(nodes[0].attributes['url'], 'http://test.com/1.png');
expect(nodes[1].delta?.toPlainText(), '第一幕:奇葩公司新规');
expect(
nodes[2].delta?.toPlainText(),
'入职第一天发现我们公司有个祖传制度——迟到1分钟要讲1个笑话。前台小姐姐神秘兮兮地说:"上周市场部Jason讲了三个谐音梗,现在还在走廊罚站呢。"我盯着手机屏幕的9:01分,感觉今天要成为《饥饿游戏》真人版主角。',
);

expect(nodes[3].attributes['url'], 'http://test.com/2.png');
expect(nodes[4].delta?.toPlainText(), '第二幕:社恐の终极挑战');
expect(
nodes[5].delta?.toPlainText(),
'当我抱着简历冲进会议室时,行政总监、HR和部门主管突然集体转身。别问,问就是三堂会审现场。更可怕的是行政总监脖子上挂着"今日段子质检员"工牌,手里还攥着评分表!此时投影仪突然发出放屁般的故障音,大老板推门而入:"听说新同事准备了特别节目?"',
);
});
});
}

Expand Down
Loading