mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: add toggle list and callout node parser (#3700)
This commit is contained in:
@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
import 'util/mock/mock_file_picker.dart';
|
import 'util/mock/mock_file_picker.dart';
|
||||||
import 'util/util.dart';
|
import 'util/util.dart';
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedMarkdown = r'''
|
const expectedMarkdown = '''
|
||||||
# Welcome to AppFlowy!
|
# Welcome to AppFlowy!
|
||||||
## Here are the basics
|
## Here are the basics
|
||||||
- [ ] Click anywhere and just start typing.
|
- [ ] Click anywhere and just start typing.
|
||||||
@ -105,6 +106,15 @@ fn main() {
|
|||||||
## Have a question❓
|
## Have a question❓
|
||||||
> Click `?` at the bottom right for help and support.
|
> Click `?` at the bottom right for help and support.
|
||||||
|
|
||||||
|
> 🥰
|
||||||
|
>
|
||||||
|
> Like AppFlowy? Follow us:
|
||||||
|
> [GitHub](https://github.com/AppFlowy-IO/AppFlowy)
|
||||||
|
> [Twitter](https://twitter.com/appflowy): @appflowy
|
||||||
|
> [Newsletter](https://blog-appflowy.ghost.io/)
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
''';
|
''';
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
|
class CalloutNodeParser extends NodeParser {
|
||||||
|
const CalloutNodeParser();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get id => CalloutBlockKeys.type;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||||
|
assert(node.children.isEmpty);
|
||||||
|
final icon = node.attributes[CalloutBlockKeys.icon];
|
||||||
|
final delta = node.delta ?? Delta()
|
||||||
|
..insert('');
|
||||||
|
final String markdown = DeltaMarkdownEncoder()
|
||||||
|
.convert(delta)
|
||||||
|
.split('\n')
|
||||||
|
.map((e) => '> $e')
|
||||||
|
.join('\n');
|
||||||
|
return '''
|
||||||
|
> $icon
|
||||||
|
$markdown
|
||||||
|
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ class CodeBlockNodeParser extends NodeParser {
|
|||||||
String get id => 'code_block';
|
String get id => 'code_block';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String transform(Node node) {
|
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||||
return '```\n${node.attributes['code_block']}\n```';
|
return '```\n${node.attributes['code_block']}\n```';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ class DividerNodeParser extends NodeParser {
|
|||||||
String get id => 'divider';
|
String get id => 'divider';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String transform(Node node) {
|
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||||
return '---\n';
|
return '---\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
export 'callout_node_parser.dart';
|
||||||
|
export 'code_block_node_parser.dart';
|
||||||
|
export 'divider_node_parser.dart';
|
||||||
|
export 'math_equation_node_parser.dart';
|
||||||
|
export 'toggle_list_node_parser.dart';
|
@ -0,0 +1,5 @@
|
|||||||
|
export 'callout_node_parser.dart';
|
||||||
|
export 'code_block_node_parser.dart';
|
||||||
|
export 'divider_node_parser.dart';
|
||||||
|
export 'math_equation_node_parser.dart';
|
||||||
|
export 'toggle_list_node_parser.dart';
|
@ -8,7 +8,7 @@ class MathEquationNodeParser extends NodeParser {
|
|||||||
String get id => MathEquationBlockKeys.type;
|
String get id => MathEquationBlockKeys.type;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String transform(Node node) {
|
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||||
return '\$\$${node.attributes[id]}\$\$';
|
return '\$\$${node.attributes[id]}\$\$';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
|
enum ToggleListExportStyle {
|
||||||
|
github,
|
||||||
|
markdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToggleListNodeParser extends NodeParser {
|
||||||
|
const ToggleListNodeParser({
|
||||||
|
this.exportStyle = ToggleListExportStyle.markdown,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ToggleListExportStyle exportStyle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get id => ToggleListBlockKeys.type;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String transform(Node node, DocumentMarkdownEncoder? encoder) {
|
||||||
|
final delta = node.delta ?? Delta()
|
||||||
|
..insert('');
|
||||||
|
String markdown = DeltaMarkdownEncoder().convert(delta);
|
||||||
|
final details = encoder?.convertNodes(
|
||||||
|
node.children,
|
||||||
|
withIndent: true,
|
||||||
|
);
|
||||||
|
switch (exportStyle) {
|
||||||
|
case ToggleListExportStyle.github:
|
||||||
|
return '''<details>
|
||||||
|
<summary>$markdown</summary>
|
||||||
|
|
||||||
|
$details
|
||||||
|
</details>
|
||||||
|
''';
|
||||||
|
case ToggleListExportStyle.markdown:
|
||||||
|
markdown = '- $markdown\n';
|
||||||
|
if (details != null && details.isNotEmpty) {
|
||||||
|
markdown += details;
|
||||||
|
}
|
||||||
|
return markdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ export 'openai/widgets/auto_completion_node_widget.dart';
|
|||||||
export 'openai/widgets/smart_edit_node_widget.dart';
|
export 'openai/widgets/smart_edit_node_widget.dart';
|
||||||
export 'openai/widgets/smart_edit_toolbar_item.dart';
|
export 'openai/widgets/smart_edit_toolbar_item.dart';
|
||||||
export 'outline/outline_block_component.dart';
|
export 'outline/outline_block_component.dart';
|
||||||
|
export 'parsers/markdown_parsers.dart';
|
||||||
export 'table/table_menu.dart';
|
export 'table/table_menu.dart';
|
||||||
export 'table/table_option_action.dart';
|
export 'table/table_option_action.dart';
|
||||||
export 'toggle/toggle_block_component.dart';
|
export 'toggle/toggle_block_component.dart';
|
||||||
|
@ -3,15 +3,21 @@ import 'dart:convert';
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
|
||||||
import 'package:appflowy/plugins/document/application/prelude.dart';
|
import 'package:appflowy/plugins/document/application/prelude.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
|
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
|
const List<NodeParser> _customParsers = [
|
||||||
|
DividerNodeParser(),
|
||||||
|
MathEquationNodeParser(),
|
||||||
|
CodeBlockNodeParser(),
|
||||||
|
CalloutNodeParser(),
|
||||||
|
ToggleListNodeParser(),
|
||||||
|
];
|
||||||
|
|
||||||
enum DocumentExportType {
|
enum DocumentExportType {
|
||||||
json,
|
json,
|
||||||
markdown,
|
markdown,
|
||||||
@ -43,11 +49,7 @@ class DocumentExporter {
|
|||||||
case DocumentExportType.markdown:
|
case DocumentExportType.markdown:
|
||||||
final markdown = documentToMarkdown(
|
final markdown = documentToMarkdown(
|
||||||
document,
|
document,
|
||||||
customParsers: [
|
customParsers: _customParsers,
|
||||||
const DividerNodeParser(),
|
|
||||||
const MathEquationNodeParser(),
|
|
||||||
const CodeBlockNodeParser(),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
return right(markdown);
|
return right(markdown);
|
||||||
case DocumentExportType.text:
|
case DocumentExportType.text:
|
||||||
|
@ -54,8 +54,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: d1027c4
|
ref: "9ae85ea"
|
||||||
resolved-ref: d1027c4d83a8cf78227e7d742c050ca945cef23a
|
resolved-ref: "9ae85ea162606b79483c49550266c154c0cb500c"
|
||||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.4.4"
|
version: "1.4.4"
|
||||||
|
@ -47,7 +47,7 @@ dependencies:
|
|||||||
appflowy_editor:
|
appflowy_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||||
ref: "d1027c4"
|
ref: "9ae85ea"
|
||||||
appflowy_popover:
|
appflowy_popover:
|
||||||
path: packages/appflowy_popover
|
path: packages/appflowy_popover
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
|
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(result, r'$$E = MC^2$$');
|
expect(result, r'$$E = MC^2$$');
|
||||||
});
|
});
|
||||||
// Changes
|
|
||||||
test('code block', () {
|
test('code block', () {
|
||||||
const text = '''
|
const text = '''
|
||||||
{
|
{
|
||||||
@ -63,6 +61,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(result, '```\nSome Code\n```');
|
expect(result, '```\nSome Code\n```');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('divider', () {
|
test('divider', () {
|
||||||
const text = '''
|
const text = '''
|
||||||
{
|
{
|
||||||
@ -87,5 +86,73 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(result, '---\n');
|
expect(result, '---\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('callout', () {
|
||||||
|
const text = '''
|
||||||
|
{
|
||||||
|
"document":{
|
||||||
|
"type":"page",
|
||||||
|
"children":[
|
||||||
|
{
|
||||||
|
"type":"callout",
|
||||||
|
"data":{
|
||||||
|
"icon": "😁",
|
||||||
|
"delta": [
|
||||||
|
{
|
||||||
|
"insert": "Callout"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
final document = Document.fromJson(
|
||||||
|
Map<String, Object>.from(json.decode(text)),
|
||||||
|
);
|
||||||
|
final result = documentToMarkdown(
|
||||||
|
document,
|
||||||
|
customParsers: [
|
||||||
|
const CalloutNodeParser(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
expect(result, '''> 😁
|
||||||
|
> Callout
|
||||||
|
|
||||||
|
''');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toggle list', () {
|
||||||
|
const text = '''
|
||||||
|
{
|
||||||
|
"document":{
|
||||||
|
"type":"page",
|
||||||
|
"children":[
|
||||||
|
{
|
||||||
|
"type":"toggle_list",
|
||||||
|
"data":{
|
||||||
|
"delta": [
|
||||||
|
{
|
||||||
|
"insert": "Toggle list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
final document = Document.fromJson(
|
||||||
|
Map<String, Object>.from(json.decode(text)),
|
||||||
|
);
|
||||||
|
final result = documentToMarkdown(
|
||||||
|
document,
|
||||||
|
customParsers: [
|
||||||
|
const ToggleListNodeParser(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
expect(result, '- Toggle list\n');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user