mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: markdown to document
This commit is contained in:
parent
fc35f74751
commit
4622a412b7
@ -50,7 +50,7 @@ class TextInsert extends TextOperation {
|
|||||||
final result = <String, dynamic>{
|
final result = <String, dynamic>{
|
||||||
'insert': text,
|
'insert': text,
|
||||||
};
|
};
|
||||||
if (_attributes != null) {
|
if (_attributes != null && _attributes!.isNotEmpty) {
|
||||||
result['attributes'] = attributes;
|
result['attributes'] = attributes;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -87,7 +87,7 @@ class TextRetain extends TextOperation {
|
|||||||
final result = <String, dynamic>{
|
final result = <String, dynamic>{
|
||||||
'retain': length,
|
'retain': length,
|
||||||
};
|
};
|
||||||
if (_attributes != null) {
|
if (_attributes != null && _attributes!.isNotEmpty) {
|
||||||
result['attributes'] = attributes;
|
result['attributes'] = attributes;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/src/core/document/attributes.dart';
|
||||||
|
import 'package:appflowy_editor/src/core/document/text_delta.dart';
|
||||||
|
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
|
||||||
import 'package:markdown/markdown.dart' as md;
|
import 'package:markdown/markdown.dart' as md;
|
||||||
|
|
||||||
class DeltaMarkdownDecoder extends Converter<String, Delta>
|
class DeltaMarkdownDecoder extends Converter<String, Delta>
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
|
class DocumentMarkdownDecoder extends Converter<String, Document> {
|
||||||
|
@override
|
||||||
|
Document convert(String input) {
|
||||||
|
final lines = input.split('\n');
|
||||||
|
final document = Document.empty();
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (final line in lines) {
|
||||||
|
document.insert([i++], [_convertLineToNode(line)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node _convertLineToNode(String text) {
|
||||||
|
final decoder = DeltaMarkdownDecoder();
|
||||||
|
// Heading Style
|
||||||
|
if (text.startsWith('### ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(4)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
|
||||||
|
BuiltInAttributeKey.heading: BuiltInAttributeKey.h3,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('## ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(3)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
|
||||||
|
BuiltInAttributeKey.heading: BuiltInAttributeKey.h2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('# ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(2)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
|
||||||
|
BuiltInAttributeKey.heading: BuiltInAttributeKey.h1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('- [ ] ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(6)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||||
|
BuiltInAttributeKey.checkbox: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('- [x] ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(6)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
|
||||||
|
BuiltInAttributeKey.checkbox: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('> ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(2)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('- ') || text.startsWith('* ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(2)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (text.startsWith('> ')) {
|
||||||
|
return TextNode(
|
||||||
|
delta: decoder.convert(text.substring(2)),
|
||||||
|
attributes: {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.isNotEmpty) {
|
||||||
|
return TextNode(delta: decoder.convert(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextNode(delta: Delta());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
library delta_markdown;
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/src/core/document/document.dart';
|
||||||
|
import 'package:appflowy_editor/src/plugins/markdown/encoder/document_markdown_encoder.dart';
|
||||||
|
|
||||||
|
/// Codec used to convert between Markdown and AppFlowy Editor Document.
|
||||||
|
const AppFlowyEditorMarkdownCodec _kCodec = AppFlowyEditorMarkdownCodec();
|
||||||
|
|
||||||
|
Document markdownToDocument(String markdown) {
|
||||||
|
return _kCodec.decode(markdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
String documentToMarkdown(Document document) {
|
||||||
|
return _kCodec.encode(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppFlowyEditorMarkdownCodec extends Codec<Document, String> {
|
||||||
|
const AppFlowyEditorMarkdownCodec();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Converter<String, Document> get decoder => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Converter<Document, String> get encoder {
|
||||||
|
return DocumentMarkdownEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/src/plugins/markdown/decoder/document_markdown_decoder.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
group('document_markdown_decoder.dart', () {
|
||||||
|
const example = '''
|
||||||
|
{
|
||||||
|
"document": {
|
||||||
|
"type": "editor",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "heading", "heading": "h2"},
|
||||||
|
"delta": [
|
||||||
|
{"insert": "👋 "},
|
||||||
|
{"insert": "Welcome to", "attributes": {"bold": true}},
|
||||||
|
{"insert": " "},
|
||||||
|
{
|
||||||
|
"insert": "AppFlowy Editor",
|
||||||
|
"attributes": {"italic": true, "bold": true, "href": "appflowy.io"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"delta": [
|
||||||
|
{"insert": "AppFlowy Editor is a "},
|
||||||
|
{"insert": "highly customizable", "attributes": {"bold": true}},
|
||||||
|
{"insert": " "},
|
||||||
|
{"insert": "rich-text editor", "attributes": {"italic": true}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "checkbox", "checkbox": true},
|
||||||
|
"delta": [{"insert": "Customizable"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "checkbox", "checkbox": true},
|
||||||
|
"delta": [{"insert": "Test-covered"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "checkbox", "checkbox": false},
|
||||||
|
"delta": [{"insert": "more to come!"}]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "quote"},
|
||||||
|
"delta": [{"insert": "Here is an example you can give a try"}]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"delta": [
|
||||||
|
{"insert": "You can also use "},
|
||||||
|
{
|
||||||
|
"insert": "AppFlowy Editor",
|
||||||
|
"attributes": {"italic": true, "bold": true}
|
||||||
|
},
|
||||||
|
{"insert": " as a component to build your own app."}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "bulleted-list"},
|
||||||
|
"delta": [{"insert": "Use / to insert blocks"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"attributes": {"subtype": "bulleted-list"},
|
||||||
|
"delta": [
|
||||||
|
{
|
||||||
|
"insert": "Select text to trigger to the toolbar to format your notes."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"delta": [
|
||||||
|
{
|
||||||
|
"insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type": "text", "delta": []},
|
||||||
|
{"type": "text", "delta": [{"insert": ""}]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
setUpAll(() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('parser document', () async {
|
||||||
|
const markdown = '''
|
||||||
|
## 👋 **Welcome to** ***[AppFlowy Editor](appflowy.io)***
|
||||||
|
|
||||||
|
AppFlowy Editor is a **highly customizable** _rich-text editor_
|
||||||
|
- [x] Customizable
|
||||||
|
- [x] Test-covered
|
||||||
|
- [ ] more to come!
|
||||||
|
|
||||||
|
> Here is an example you can give a try
|
||||||
|
|
||||||
|
You can also use ***AppFlowy Editor*** as a component to build your own app.
|
||||||
|
|
||||||
|
* Use / to insert blocks
|
||||||
|
* Select text to trigger to the toolbar to format your notes.
|
||||||
|
|
||||||
|
If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!
|
||||||
|
''';
|
||||||
|
final result = DocumentMarkdownDecoder().convert(markdown);
|
||||||
|
final data = Map<String, Object>.from(json.decode(example));
|
||||||
|
expect(result.toJson(), data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user