From 7bb058129224ef15132d3fdf831e5f6cb8e1e3c4 Mon Sep 17 00:00:00 2001 From: Matt Carroll Date: Wed, 10 Jun 2026 20:02:18 -0700 Subject: [PATCH] [SuperEditor][Markdown] - Parse images with captions immediately above them (Resolves #3041) (#3042) - BREAKING: Force all TextNode 'textAlign' properties to use Strings instead of randomly switching between Strings and TextAligns. --- .../default_editor/tables/table_markdown.dart | 12 +- super_editor/lib/src/default_editor/text.dart | 15 ++- .../serialization/html/html_table.dart | 13 +- .../document_to_markdown_serializer.dart | 2 - .../markdown_to_document_parsing.dart | 59 +++++++-- .../serialization/markdown/table.dart | 14 +-- .../quill/serializing/serializers.dart | 5 +- .../html/document_to_html_test.dart | 6 +- .../markdown/super_editor_markdown_test.dart | 115 +++++++++++++----- .../markdown/table_parser_test.dart | 70 ++++++----- .../quill/parsing/parsing_test.dart | 2 +- .../test/copy_and_paste_test.dart | 6 +- 12 files changed, 212 insertions(+), 107 deletions(-) diff --git a/super_editor/lib/src/default_editor/tables/table_markdown.dart b/super_editor/lib/src/default_editor/tables/table_markdown.dart index bf6089b9d5..04e6aebc65 100644 --- a/super_editor/lib/src/default_editor/tables/table_markdown.dart +++ b/super_editor/lib/src/default_editor/tables/table_markdown.dart @@ -49,7 +49,7 @@ class MarkdownTableComponentBuilder implements ComponentBuilder { nodeId: cell.id, createdAt: cell.metadata[NodeMetadata.createdAt], text: cell.text, - textAlign: cell.getMetadataValue(TextNodeMetadata.textAlign) ?? TextAlign.left, + textAlign: _maybeParseTextAlign(cell.getMetadataValue(TextNodeMetadata.textAlign)) ?? TextAlign.left, textStyleBuilder: noStyleBuilder, padding: const EdgeInsets.all(8.0), // ^ Default padding, can be overridden through the stylesheet. @@ -62,6 +62,16 @@ class MarkdownTableComponentBuilder implements ComponentBuilder { ); } + TextAlign? _maybeParseTextAlign(String? textAlign) { + return switch (textAlign) { + 'left' => TextAlign.left, + 'center' => TextAlign.center, + 'right' => TextAlign.right, + 'justify' => TextAlign.justify, + _ => null, + }; + } + @override Widget? createComponent( SingleColumnDocumentComponentContext componentContext, SingleColumnLayoutComponentViewModel componentViewModel) { diff --git a/super_editor/lib/src/default_editor/text.dart b/super_editor/lib/src/default_editor/text.dart index aeac5684ba..b0264cdb2d 100644 --- a/super_editor/lib/src/default_editor/text.dart +++ b/super_editor/lib/src/default_editor/text.dart @@ -15,8 +15,8 @@ import 'package:super_editor/src/core/edit_context.dart'; import 'package:super_editor/src/core/editor.dart'; import 'package:super_editor/src/core/styles.dart'; import 'package:super_editor/src/default_editor/attributions.dart'; -import 'package:super_editor/src/default_editor/text_ai.dart'; import 'package:super_editor/src/default_editor/text/custom_underlines.dart'; +import 'package:super_editor/src/default_editor/text_ai.dart'; import 'package:super_editor/src/infrastructure/_logging.dart'; import 'package:super_editor/src/infrastructure/attributed_text_styles.dart'; import 'package:super_editor/src/infrastructure/composable_text.dart'; @@ -38,7 +38,18 @@ class TextNode extends DocumentNode { required this.id, required this.text, super.metadata, - }); + }) { + if (metadata['textAlign'] == null) { + initAddToMetadata({ + 'textAlign': 'left', + }); + } else { + assert( + metadata['textAlign'] is String?, + 'Expected "textAlign" metadata to be of type String, but got: ${metadata['textAlign']}', + ); + } + } @override final String id; diff --git a/super_editor/lib/src/infrastructure/serialization/html/html_table.dart b/super_editor/lib/src/infrastructure/serialization/html/html_table.dart index 3521e6f66b..6d57a932e3 100644 --- a/super_editor/lib/src/infrastructure/serialization/html/html_table.dart +++ b/super_editor/lib/src/infrastructure/serialization/html/html_table.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:super_editor/src/core/document.dart'; import 'package:super_editor/src/default_editor/selection_upstream_downstream.dart'; import 'package:super_editor/src/default_editor/tables/table_block.dart'; @@ -111,15 +110,15 @@ extension TableBlockNodeToHtml on TableBlockNode { String _getTextAlignStyle(TextNode cell) { final textAlign = cell.getMetadataValue('textAlign'); - if (textAlign == TextAlign.left) { - // Default alignment is left, so we don't need to specify it. - return ''; - } final textAlignString = switch (textAlign) { - TextAlign.center => 'center', - TextAlign.right => 'right', + 'center' => 'center', + 'right' => 'right', _ => 'left', }; + if (textAlign == 'left') { + // Default alignment is left, so we don't need to specify it. + return ''; + } return textAlign != null ? ' style="text-align:$textAlignString"' : ''; } } diff --git a/super_editor/lib/src/infrastructure/serialization/markdown/document_to_markdown_serializer.dart b/super_editor/lib/src/infrastructure/serialization/markdown/document_to_markdown_serializer.dart index 3d837015d5..167ce3b0a1 100644 --- a/super_editor/lib/src/infrastructure/serialization/markdown/document_to_markdown_serializer.dart +++ b/super_editor/lib/src/infrastructure/serialization/markdown/document_to_markdown_serializer.dart @@ -2,7 +2,6 @@ import 'dart:ui'; import 'package:attributed_text/attributed_text.dart'; import 'package:flutter/foundation.dart'; -import 'package:markdown/markdown.dart' hide Document; import 'package:super_editor/src/core/document.dart'; import 'package:super_editor/src/core/document_selection.dart'; import 'package:super_editor/src/default_editor/attributions.dart'; @@ -14,7 +13,6 @@ import 'package:super_editor/src/default_editor/selection_upstream_downstream.da import 'package:super_editor/src/default_editor/tables/table_block.dart'; import 'package:super_editor/src/default_editor/tasks.dart'; import 'package:super_editor/src/default_editor/text.dart'; - import 'package:super_editor/src/infrastructure/serialization/markdown/super_editor_syntax.dart'; /// Serializes the given [doc] to Markdown text. diff --git a/super_editor/lib/src/infrastructure/serialization/markdown/markdown_to_document_parsing.dart b/super_editor/lib/src/infrastructure/serialization/markdown/markdown_to_document_parsing.dart index b11b7b92dd..40594537fd 100644 --- a/super_editor/lib/src/infrastructure/serialization/markdown/markdown_to_document_parsing.dart +++ b/super_editor/lib/src/infrastructure/serialization/markdown/markdown_to_document_parsing.dart @@ -213,13 +213,25 @@ class _MarkdownToDocument implements md.NodeVisitor { if (blockImage != null) { _addImage(blockImage); } else { - final attributedText = parseInlineMarkdown( - element.textContent, - inlineMarkdownSyntaxes: inlineMarkdownSyntaxes, - inlineHtmlSyntaxes: inlineHtmlSyntaxes, - encodeHtml: encodeHtml, - ); - _addParagraph(attributedText, element.attributes); + final captionAndImage = _maybeParseImageWithCaption(element.textContent); + if (captionAndImage != null) { + final captionText = parseInlineMarkdown( + captionAndImage.caption, + inlineMarkdownSyntaxes: inlineMarkdownSyntaxes, + inlineHtmlSyntaxes: inlineHtmlSyntaxes, + encodeHtml: encodeHtml, + ); + _addParagraph(captionText, element.attributes); + _addImage(captionAndImage.image); + } else { + final attributedText = parseInlineMarkdown( + element.textContent, + inlineMarkdownSyntaxes: inlineMarkdownSyntaxes, + inlineHtmlSyntaxes: inlineHtmlSyntaxes, + encodeHtml: encodeHtml, + ); + _addParagraph(attributedText, element.attributes); + } } break; @@ -328,28 +340,25 @@ class _MarkdownToDocument implements md.NodeVisitor { break; } - final textAlign = element.attributes['textAlign']; _content.add( ParagraphNode( id: Editor.createNodeId(), text: _parseInlineText(element.textContent), metadata: { 'blockType': headerAttribution, - 'textAlign': textAlign, + 'textAlign': element.attributes['textAlign'] ?? 'left', }, ), ); } void _addParagraph(AttributedText attributedText, Map attributes) { - final textAlign = attributes['textAlign']; - _content.add( ParagraphNode( id: Editor.createNodeId(), text: attributedText, metadata: { - 'textAlign': textAlign, + 'textAlign': attributes['textAlign'] ?? 'left', }, ), ); @@ -480,6 +489,25 @@ class _MarkdownToDocument implements md.NodeVisitor { syntax: syntax, ); } + + /// If the last line of [markdown] is a block image and there is at least one + /// preceding line (the caption), returns a [_CaptionAndImage] containing the + /// caption text and the parsed image. Otherwise returns `null`. + _CaptionAndImage? _maybeParseImageWithCaption(String markdown) { + final newlineIndex = markdown.lastIndexOf('\n'); + if (newlineIndex < 0) { + return null; + } + + final lastLine = markdown.substring(newlineIndex + 1); + final image = _maybeParseBlockImage(lastLine); + if (image == null) { + return null; + } + + final caption = markdown.substring(0, newlineIndex); + return _CaptionAndImage(caption: caption, image: image); + } } /// Converts a deserialized Markdown element into a [DocumentNode]. @@ -842,6 +870,13 @@ class _HeaderWithAlignmentSyntax extends md.BlockSyntax { } } +class _CaptionAndImage { + const _CaptionAndImage({required this.caption, required this.image}); + + final String caption; + final _MarkdownImage image; +} + class _MarkdownImage { _MarkdownImage({ required this.url, diff --git a/super_editor/lib/src/infrastructure/serialization/markdown/table.dart b/super_editor/lib/src/infrastructure/serialization/markdown/table.dart index 88289efeac..0fa35fccf6 100644 --- a/super_editor/lib/src/infrastructure/serialization/markdown/table.dart +++ b/super_editor/lib/src/infrastructure/serialization/markdown/table.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:markdown/markdown.dart' as md; import 'package:super_editor/src/core/document.dart'; import 'package:super_editor/src/core/editor.dart'; @@ -55,7 +53,7 @@ extension ElementTableExtension on md.Element { ), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ); @@ -81,17 +79,17 @@ extension ElementTableExtension on md.Element { throw Exception('Table body cells must be elements'); } final textAlign = switch ((headerRow.children![i] as md.Element).attributes['align']) { - 'left' => TextAlign.left, - 'center' => TextAlign.center, - 'right' => TextAlign.right, - _ => TextAlign.left, + 'left' => 'left', + 'center' => 'center', + 'right' => 'right', + _ => 'left', }; row.add(TextNode( id: Editor.createNodeId(), text: parseInlineMarkdown(cellElement.textContent), metadata: { - if (textAlign != TextAlign.left) TextNodeMetadata.textAlign: textAlign, + if (textAlign != 'left') TextNodeMetadata.textAlign: textAlign, }, )); } diff --git a/super_editor/lib/src/infrastructure/serialization/quill/serializing/serializers.dart b/super_editor/lib/src/infrastructure/serialization/quill/serializing/serializers.dart index fe645eb266..ff73756789 100644 --- a/super_editor/lib/src/infrastructure/serialization/quill/serializing/serializers.dart +++ b/super_editor/lib/src/infrastructure/serialization/quill/serializing/serializers.dart @@ -298,8 +298,9 @@ class TextBlockDeltaSerializer implements DeltaSerializer { final blockAttributes = {}; // Add all the block-level formats that aren't mutually exclusive. - if (textBlock.metadata["textAlign"] != null) { - blockAttributes["align"] = textBlock.metadata["textAlign"]; + final textAlign = textBlock.metadata["textAlign"]; + if (textAlign != null && textAlign != "left") { + blockAttributes["align"] = textAlign; } final blockType = textBlock.metadata["blockType"] as Attribution?; diff --git a/super_editor/test/infrastructure/serialization/html/document_to_html_test.dart b/super_editor/test/infrastructure/serialization/html/document_to_html_test.dart index dffd822e46..dfb3c21099 100644 --- a/super_editor/test/infrastructure/serialization/html/document_to_html_test.dart +++ b/super_editor/test/infrastructure/serialization/html/document_to_html_test.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter_test/flutter_test.dart'; import 'package:super_editor/super_editor.dart'; @@ -665,12 +663,12 @@ void main() { TextNode( id: "1.1.1", text: AttributedText("Value 1.1"), - metadata: const {"textAlign": TextAlign.right}, + metadata: const {"textAlign": "right"}, ), TextNode( id: "1.1.2", text: AttributedText("Value 1.2"), - metadata: const {"textAlign": TextAlign.center}, + metadata: const {"textAlign": "center"}, ), ], ], diff --git a/super_editor/test/infrastructure/serialization/markdown/super_editor_markdown_test.dart b/super_editor/test/infrastructure/serialization/markdown/super_editor_markdown_test.dart index 24cfb74426..6547e8ee8d 100644 --- a/super_editor/test/infrastructure/serialization/markdown/super_editor_markdown_test.dart +++ b/super_editor/test/infrastructure/serialization/markdown/super_editor_markdown_test.dart @@ -1,4 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:super_editor/src/test/super_editor_test/supereditor_test_tools.dart'; import 'package:super_editor/super_editor.dart'; void main() { @@ -107,7 +108,7 @@ void main() { id: '1', text: AttributedText('Header1'), metadata: const { - 'textAlign': 'left', + 'textAlign': "left", 'blockType': header1Attribution, }, ), @@ -126,7 +127,7 @@ void main() { id: '1', text: AttributedText('Header1'), metadata: const { - 'textAlign': 'center', + 'textAlign': "center", 'blockType': header1Attribution, }, ), @@ -140,7 +141,7 @@ void main() { id: '1', text: AttributedText('Header1'), metadata: const { - 'textAlign': 'right', + 'textAlign': "right", 'blockType': header1Attribution, }, ), @@ -154,7 +155,7 @@ void main() { id: '1', text: AttributedText('Header1'), metadata: const { - 'textAlign': 'justify', + 'textAlign': "justify", 'blockType': header1Attribution, }, ), @@ -366,7 +367,7 @@ This is some code id: '1', text: AttributedText('Paragraph1'), metadata: const { - 'textAlign': 'left', + 'textAlign': "left", }, ), ]); @@ -385,7 +386,7 @@ This is some code id: '1', text: AttributedText('Paragraph1'), metadata: const { - 'textAlign': 'center', + 'textAlign': "center", }, ), ]); @@ -399,7 +400,7 @@ This is some code id: '1', text: AttributedText('Paragraph1'), metadata: const { - 'textAlign': 'right', + 'textAlign': "right", }, ), ]); @@ -413,7 +414,7 @@ This is some code id: '1', text: AttributedText('Paragraph1'), metadata: const { - 'textAlign': 'justify', + 'textAlign': "justify", }, ), ]); @@ -427,7 +428,7 @@ This is some code id: '1', text: AttributedText('Paragraph1'), metadata: const { - 'textAlign': 'center', + 'textAlign': "center", }, ), ]); @@ -781,22 +782,22 @@ with multiple lines ParagraphNode( id: Editor.createNodeId(), text: AttributedText('Example Doc With Left Alignment'), - metadata: const {'blockType': header1Attribution, 'textAlign': 'left'}, + metadata: const {'blockType': header1Attribution, 'textAlign': "left"}, ), ParagraphNode( id: Editor.createNodeId(), text: AttributedText('Example Doc With Center Alignment'), - metadata: const {'blockType': header1Attribution, 'textAlign': 'center'}, + metadata: const {'blockType': header1Attribution, 'textAlign': "center"}, ), ParagraphNode( id: Editor.createNodeId(), text: AttributedText('Example Doc With Right Alignment'), - metadata: const {'blockType': header1Attribution, 'textAlign': 'right'}, + metadata: const {'blockType': header1Attribution, 'textAlign': "right"}, ), ParagraphNode( id: Editor.createNodeId(), text: AttributedText('Example Doc With Justify Alignment'), - metadata: const {'blockType': header1Attribution, 'textAlign': 'justify'}, + metadata: const {'blockType': header1Attribution, 'textAlign': "justify"}, ), HorizontalRuleNode(id: Editor.createNodeId()), ParagraphNode( @@ -915,7 +916,7 @@ with multiple lines final headerLeftAlignment1 = deserializeMarkdownToDocument(':---\n# Header 1'); final header = headerLeftAlignment1.first as ParagraphNode; expect(header.getMetadataValue('blockType'), header1Attribution); - expect(header.getMetadataValue('textAlign'), 'left'); + expect(header.getMetadataValue('textAlign'), "left"); expect(header.text.toPlainText(), 'Header 1'); }); @@ -923,7 +924,7 @@ with multiple lines final headerLeftAlignment1 = deserializeMarkdownToDocument(':---:\n# Header 1'); final header = headerLeftAlignment1.first as ParagraphNode; expect(header.getMetadataValue('blockType'), header1Attribution); - expect(header.getMetadataValue('textAlign'), 'center'); + expect(header.getMetadataValue('textAlign'), "center"); expect(header.text.toPlainText(), 'Header 1'); }); @@ -931,7 +932,7 @@ with multiple lines final headerLeftAlignment1 = deserializeMarkdownToDocument('---:\n# Header 1'); final header = headerLeftAlignment1.first as ParagraphNode; expect(header.getMetadataValue('blockType'), header1Attribution); - expect(header.getMetadataValue('textAlign'), 'right'); + expect(header.getMetadataValue('textAlign'), "right"); expect(header.text.toPlainText(), 'Header 1'); }); @@ -939,7 +940,7 @@ with multiple lines final headerLeftAlignment1 = deserializeMarkdownToDocument('-::-\n# Header 1'); final header = headerLeftAlignment1.first as ParagraphNode; expect(header.getMetadataValue('blockType'), header1Attribution); - expect(header.getMetadataValue('textAlign'), 'justify'); + expect(header.getMetadataValue('textAlign'), "justify"); expect(header.text.toPlainText(), 'Header 1'); }); @@ -1035,6 +1036,64 @@ This is some code expect(image.expectedBitmapSize?.height, isNull); }); + test('image with caption above', () { + final doc = deserializeMarkdownToDocument( + ''' +Document with image with caption. + +A caption: +![Image alt text](https://images.com/some/image.png) + +Paragraph after the captioned image.''', + ); + + expect( + doc, + documentEquivalentTo( + MutableDocument(nodes: [ + ParagraphNode(id: "1", text: AttributedText("Document with image with caption.")), + ParagraphNode(id: "2", text: AttributedText("A caption:")), + ImageNode(id: "3", imageUrl: "https://images.com/some/image.png", altText: "Image alt text"), + ParagraphNode(id: "4", text: AttributedText("Paragraph after the captioned image.")), + ]), + ), + ); + }); + + test('multiple images with captions above', () { + final doc = deserializeMarkdownToDocument( + ''' +Document with image with caption. + +A caption: +![Image 1](https://images.com/some/image1.png) + +B caption: +![Image 2](https://images.com/some/image2.png) + +C caption: +![Image 3](https://images.com/some/image3.png) + +Paragraph after the captioned imagea.''', + ); + + expect( + doc, + documentEquivalentTo( + MutableDocument(nodes: [ + ParagraphNode(id: "1", text: AttributedText("Document with image with caption.")), + ParagraphNode(id: "2", text: AttributedText("A caption:")), + ImageNode(id: "3", imageUrl: "https://images.com/some/image1.png", altText: "Image 1"), + ParagraphNode(id: "4", text: AttributedText("B caption:")), + ImageNode(id: "5", imageUrl: "https://images.com/some/image2.png", altText: "Image 2"), + ParagraphNode(id: "6", text: AttributedText("C caption:")), + ImageNode(id: "7", imageUrl: "https://images.com/some/image3.png", altText: "Image 3"), + ParagraphNode(id: "8", text: AttributedText("Paragraph after the captioned imagea.")), + ]), + ), + ); + }); + test('single unstyled paragraph', () { const markdown = 'This is some unstyled text to parse as markdown'; @@ -1529,19 +1588,19 @@ with multiple lines expect(document.getNodeAt(21)!, isA()); expect((document.getNodeAt(21)! as ParagraphNode).getMetadataValue('blockType'), header1Attribution); - expect((document.getNodeAt(21)! as ParagraphNode).getMetadataValue('textAlign'), 'left'); + expect((document.getNodeAt(21)! as ParagraphNode).getMetadataValue('textAlign'), "left"); expect(document.getNodeAt(22)!, isA()); expect((document.getNodeAt(22)! as ParagraphNode).getMetadataValue('blockType'), header1Attribution); - expect((document.getNodeAt(22)! as ParagraphNode).getMetadataValue('textAlign'), 'center'); + expect((document.getNodeAt(22)! as ParagraphNode).getMetadataValue('textAlign'), "center"); expect(document.getNodeAt(23)!, isA()); expect((document.getNodeAt(23)! as ParagraphNode).getMetadataValue('blockType'), header1Attribution); - expect((document.getNodeAt(23)! as ParagraphNode).getMetadataValue('textAlign'), 'right'); + expect((document.getNodeAt(23)! as ParagraphNode).getMetadataValue('textAlign'), "right"); expect(document.getNodeAt(24)!, isA()); expect((document.getNodeAt(24)! as ParagraphNode).getMetadataValue('blockType'), header1Attribution); - expect((document.getNodeAt(24)! as ParagraphNode).getMetadataValue('textAlign'), 'justify'); + expect((document.getNodeAt(24)! as ParagraphNode).getMetadataValue('textAlign'), "justify"); expect(document.getNodeAt(25)!, isA()); }); @@ -1598,7 +1657,7 @@ with multiple lines final doc = deserializeMarkdownToDocument(':---\nParagraph1'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), 'left'); + expect(paragraph.getMetadataValue('textAlign'), "left"); expect(paragraph.text.toPlainText(), 'Paragraph1'); }); @@ -1606,7 +1665,7 @@ with multiple lines final doc = deserializeMarkdownToDocument(':---:\nParagraph1'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), 'center'); + expect(paragraph.getMetadataValue('textAlign'), "center"); expect(paragraph.text.toPlainText(), 'Paragraph1'); }); @@ -1614,7 +1673,7 @@ with multiple lines final doc = deserializeMarkdownToDocument('---:\nParagraph1'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), 'right'); + expect(paragraph.getMetadataValue('textAlign'), "right"); expect(paragraph.text.toPlainText(), 'Paragraph1'); }); @@ -1622,7 +1681,7 @@ with multiple lines final doc = deserializeMarkdownToDocument('-::-\nParagraph1'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), 'justify'); + expect(paragraph.getMetadataValue('textAlign'), "justify"); expect(paragraph.text.toPlainText(), 'Paragraph1'); }); @@ -1630,7 +1689,7 @@ with multiple lines final doc = deserializeMarkdownToDocument('---:'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), isNull); + expect(paragraph.getMetadataValue('textAlign'), "left"); expect(paragraph.text.toPlainText(), '---:'); }); @@ -1638,7 +1697,7 @@ with multiple lines final doc = deserializeMarkdownToDocument('---:\n - - -'); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), isNull); + expect(paragraph.getMetadataValue('textAlign'), "left"); expect(paragraph.text.toPlainText(), '---:'); // Ensure the horizontal rule is parsed. @@ -1649,7 +1708,7 @@ with multiple lines final doc = deserializeMarkdownToDocument(':---\nParagraph1', syntax: MarkdownSyntax.normal); final paragraph = doc.first as ParagraphNode; - expect(paragraph.getMetadataValue('textAlign'), isNull); + expect(paragraph.getMetadataValue('textAlign'), "left"); expect(paragraph.text.toPlainText(), ':---\nParagraph1'); }); diff --git a/super_editor/test/infrastructure/serialization/markdown/table_parser_test.dart b/super_editor/test/infrastructure/serialization/markdown/table_parser_test.dart index ed7138824e..a12494e4cb 100644 --- a/super_editor/test/infrastructure/serialization/markdown/table_parser_test.dart +++ b/super_editor/test/infrastructure/serialization/markdown/table_parser_test.dart @@ -1,7 +1,5 @@ import 'dart:math'; -import 'dart:ui'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:super_editor/super_editor.dart'; import 'package:text_table/text_table.dart'; @@ -22,7 +20,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ) ], @@ -48,7 +46,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -56,7 +54,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -87,7 +85,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -95,7 +93,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ) ], @@ -132,7 +130,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -140,7 +138,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -148,7 +146,7 @@ void main() { text: AttributedText('header 3'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -158,14 +156,14 @@ void main() { id: '2-2', text: AttributedText('data 2'), metadata: const { - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( id: '2-3', text: AttributedText('data 3'), metadata: const { - TextNodeMetadata.textAlign: TextAlign.right, + TextNodeMetadata.textAlign: 'right', }, ), ], @@ -206,7 +204,7 @@ void main() { ), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ) ], @@ -250,7 +248,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -258,7 +256,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -283,7 +281,7 @@ void main() { text: AttributedText('header|1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -327,7 +325,7 @@ void main() { ), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -353,7 +351,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -361,7 +359,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -390,7 +388,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -398,7 +396,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -427,7 +425,7 @@ void main() { text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -435,7 +433,7 @@ void main() { text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -464,7 +462,7 @@ data 1 | data 2 ''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -472,7 +470,7 @@ data 1 | data 2 ''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -501,7 +499,7 @@ data 1 | data 2 ''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -509,7 +507,7 @@ data 1 | data 2 ''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -538,7 +536,7 @@ data 1 | data 2 ''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -546,7 +544,7 @@ data 1 | data 2 ''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -575,7 +573,7 @@ data 1 | data 2 ''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -583,7 +581,7 @@ data 1 | data 2 ''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -591,7 +589,7 @@ data 1 | data 2 ''', text: AttributedText('header 3'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -622,7 +620,7 @@ data 1 | data 2 ''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -630,7 +628,7 @@ data 1 | data 2 ''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], @@ -662,7 +660,7 @@ paragraph''', text: AttributedText('header 1'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), TextNode( @@ -670,7 +668,7 @@ paragraph''', text: AttributedText('header 2'), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - TextNodeMetadata.textAlign: TextAlign.center, + TextNodeMetadata.textAlign: 'center', }, ), ], diff --git a/super_editor/test/infrastructure/serialization/quill/parsing/parsing_test.dart b/super_editor/test/infrastructure/serialization/quill/parsing/parsing_test.dart index c74a0bf539..2e4466aa80 100644 --- a/super_editor/test/infrastructure/serialization/quill/parsing/parsing_test.dart +++ b/super_editor/test/infrastructure/serialization/quill/parsing/parsing_test.dart @@ -82,7 +82,7 @@ void main() { // Paragraph - left aligned. expect(node, isA()); expect((node as TextNode).text.toPlainText(), "Left aligned"); - expect(node.metadata["textAlign"], isNull); + expect(node.metadata["textAlign"], "left"); nodes.moveNext(); node = nodes.current; diff --git a/super_editor_clipboard/test/copy_and_paste_test.dart b/super_editor_clipboard/test/copy_and_paste_test.dart index a8f1262bd1..db63d7953d 100644 --- a/super_editor_clipboard/test/copy_and_paste_test.dart +++ b/super_editor_clipboard/test/copy_and_paste_test.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/foundation.dart'; import 'package:flutter/src/foundation/basic_types.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -579,7 +577,7 @@ final _tableCells = [ text: AttributedText("BMI Category"), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - 'textAlign': TextAlign.center, + 'textAlign': "center", }, ), TextNode( @@ -587,7 +585,7 @@ final _tableCells = [ text: AttributedText("BMI Range (kg/m²)"), metadata: const { NodeMetadata.blockType: tableHeaderAttribution, - 'textAlign': TextAlign.center, + 'textAlign': "center", }, ), ],