Browse Source

[flutter]: toolbar color selection

appflowy 3 years ago
parent
commit
db9d9bc840
100 changed files with 411 additions and 8444 deletions
  1. 2 1
      app_flowy/.gitignore
  2. 1 1
      app_flowy/lib/workspace/application/doc/doc_bloc.dart
  3. 1 1
      app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart
  4. 3 3
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/check_button.dart
  5. 272 0
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/color_picker.dart
  6. 3 7
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/header_button.dart
  7. 36 36
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/image_button.dart
  8. 4 2
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/link_button.dart
  9. 3 3
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toggle_button.dart
  10. 37 28
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/tool_bar.dart
  11. 2 0
      app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toolbar_icon_button.dart
  12. 37 36
      app_flowy/lib/workspace/presentation/widgets/dialogs.dart
  13. 10 10
      app_flowy/lib/workspace/presentation/widgets/pop_up_window.dart
  14. 0 2
      app_flowy/macos/Flutter/GeneratedPluginRegistrant.swift
  15. 0 6
      app_flowy/macos/Podfile.lock
  16. 0 29
      app_flowy/packages/editor/.gitignore
  17. 0 10
      app_flowy/packages/editor/.metadata
  18. 0 3
      app_flowy/packages/editor/CHANGELOG.md
  19. 0 1
      app_flowy/packages/editor/LICENSE
  20. 0 15
      app_flowy/packages/editor/README.md
  21. 0 4
      app_flowy/packages/editor/analysis_options.yaml
  22. 0 46
      app_flowy/packages/editor/example/.gitignore
  23. 0 10
      app_flowy/packages/editor/example/.metadata
  24. 0 16
      app_flowy/packages/editor/example/README.md
  25. 0 29
      app_flowy/packages/editor/example/analysis_options.yaml
  26. 0 33
      app_flowy/packages/editor/example/lib/main.dart
  27. 0 7
      app_flowy/packages/editor/example/macos/.gitignore
  28. 0 2
      app_flowy/packages/editor/example/macos/Flutter/Flutter-Debug.xcconfig
  29. 0 2
      app_flowy/packages/editor/example/macos/Flutter/Flutter-Release.xcconfig
  30. 0 14
      app_flowy/packages/editor/example/macos/Flutter/GeneratedPluginRegistrant.swift
  31. 0 40
      app_flowy/packages/editor/example/macos/Podfile
  32. 0 572
      app_flowy/packages/editor/example/macos/Runner.xcodeproj/project.pbxproj
  33. 0 8
      app_flowy/packages/editor/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  34. 0 87
      app_flowy/packages/editor/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  35. 0 7
      app_flowy/packages/editor/example/macos/Runner.xcworkspace/contents.xcworkspacedata
  36. 0 8
      app_flowy/packages/editor/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  37. 0 9
      app_flowy/packages/editor/example/macos/Runner/AppDelegate.swift
  38. 0 68
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  39. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
  40. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
  41. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
  42. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
  43. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
  44. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
  45. BIN
      app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
  46. 0 339
      app_flowy/packages/editor/example/macos/Runner/Base.lproj/MainMenu.xib
  47. 0 14
      app_flowy/packages/editor/example/macos/Runner/Configs/AppInfo.xcconfig
  48. 0 2
      app_flowy/packages/editor/example/macos/Runner/Configs/Debug.xcconfig
  49. 0 2
      app_flowy/packages/editor/example/macos/Runner/Configs/Release.xcconfig
  50. 0 13
      app_flowy/packages/editor/example/macos/Runner/Configs/Warnings.xcconfig
  51. 0 12
      app_flowy/packages/editor/example/macos/Runner/DebugProfile.entitlements
  52. 0 32
      app_flowy/packages/editor/example/macos/Runner/Info.plist
  53. 0 15
      app_flowy/packages/editor/example/macos/Runner/MainFlutterWindow.swift
  54. 0 8
      app_flowy/packages/editor/example/macos/Runner/Release.entitlements
  55. 0 404
      app_flowy/packages/editor/example/pubspec.lock
  56. 0 84
      app_flowy/packages/editor/example/pubspec.yaml
  57. 0 11
      app_flowy/packages/editor/example/test/widget_test.dart
  58. 0 11
      app_flowy/packages/editor/lib/flutter_quill.dart
  59. 0 3
      app_flowy/packages/editor/lib/models/documents/attribute.dart
  60. 0 3
      app_flowy/packages/editor/lib/models/documents/document.dart
  61. 0 3
      app_flowy/packages/editor/lib/models/documents/history.dart
  62. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/block.dart
  63. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/container.dart
  64. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/embed.dart
  65. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/leaf.dart
  66. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/line.dart
  67. 0 3
      app_flowy/packages/editor/lib/models/documents/nodes/node.dart
  68. 0 3
      app_flowy/packages/editor/lib/models/documents/style.dart
  69. 0 3
      app_flowy/packages/editor/lib/models/quill_delta.dart
  70. 0 3
      app_flowy/packages/editor/lib/models/rules/delete.dart
  71. 0 3
      app_flowy/packages/editor/lib/models/rules/format.dart
  72. 0 3
      app_flowy/packages/editor/lib/models/rules/insert.dart
  73. 0 3
      app_flowy/packages/editor/lib/models/rules/rule.dart
  74. 0 313
      app_flowy/packages/editor/lib/src/models/documents/attribute.dart
  75. 0 291
      app_flowy/packages/editor/lib/src/models/documents/document.dart
  76. 0 134
      app_flowy/packages/editor/lib/src/models/documents/history.dart
  77. 0 72
      app_flowy/packages/editor/lib/src/models/documents/nodes/block.dart
  78. 0 160
      app_flowy/packages/editor/lib/src/models/documents/nodes/container.dart
  79. 0 45
      app_flowy/packages/editor/lib/src/models/documents/nodes/embed.dart
  80. 0 252
      app_flowy/packages/editor/lib/src/models/documents/nodes/leaf.dart
  81. 0 414
      app_flowy/packages/editor/lib/src/models/documents/nodes/line.dart
  82. 0 134
      app_flowy/packages/editor/lib/src/models/documents/nodes/node.dart
  83. 0 128
      app_flowy/packages/editor/lib/src/models/documents/style.dart
  84. 0 803
      app_flowy/packages/editor/lib/src/models/quill_delta.dart
  85. 0 126
      app_flowy/packages/editor/lib/src/models/rules/delete.dart
  86. 0 161
      app_flowy/packages/editor/lib/src/models/rules/format.dart
  87. 0 385
      app_flowy/packages/editor/lib/src/models/rules/insert.dart
  88. 0 76
      app_flowy/packages/editor/lib/src/models/rules/rule.dart
  89. 0 125
      app_flowy/packages/editor/lib/src/utils/color.dart
  90. 0 103
      app_flowy/packages/editor/lib/src/utils/diff_delta.dart
  91. 0 4
      app_flowy/packages/editor/lib/src/utils/media_pick_setting.dart
  92. 0 16
      app_flowy/packages/editor/lib/src/utils/string_helper.dart
  93. 0 122
      app_flowy/packages/editor/lib/src/widgets/box.dart
  94. 0 255
      app_flowy/packages/editor/lib/src/widgets/controller.dart
  95. 0 341
      app_flowy/packages/editor/lib/src/widgets/cursor.dart
  96. 0 235
      app_flowy/packages/editor/lib/src/widgets/default_styles.dart
  97. 0 152
      app_flowy/packages/editor/lib/src/widgets/delegate.dart
  98. 0 1289
      app_flowy/packages/editor/lib/src/widgets/editor.dart
  99. 0 31
      app_flowy/packages/editor/lib/src/widgets/image.dart
  100. 0 129
      app_flowy/packages/editor/lib/src/widgets/keyboard_listener.dart

+ 2 - 1
app_flowy/.gitignore

@@ -45,4 +45,5 @@ app.*.map.json
 /android/app/profile
 /android/app/release
 
-/packages/flowy_protobuf
+/packages/flowy_protobuf
+/packages/flutter-quill

+ 1 - 1
app_flowy/lib/workspace/application/doc/doc_bloc.dart

@@ -1,6 +1,6 @@
 import 'dart:convert';
 
-import 'package:editor/flutter_quill.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flowy_log/flowy_log.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 1 - 1
app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart

@@ -1,7 +1,7 @@
 import 'dart:io';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/doc/doc_bloc.dart';
-import 'package:editor/flutter_quill.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flowy_infra_ui/style_widget/progress_indicator.dart';
 import 'package:flowy_infra_ui/widget/error_page.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';

+ 3 - 3
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/check_button.dart

@@ -1,5 +1,5 @@
-import 'package:editor/flutter_quill.dart';
-import 'package:editor/models/documents/style.dart';
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill/models/documents/style.dart';
 import 'package:flutter/material.dart';
 
 import 'toolbar_icon_button.dart';
@@ -8,7 +8,7 @@ class FlowyCheckListButton extends StatefulWidget {
   const FlowyCheckListButton({
     required this.controller,
     required this.attribute,
-    this.iconSize = kDefaultIconSize,
+    this.iconSize = defaultIconSize,
     this.fillColor,
     this.childBuilder = defaultToggleStyleButtonBuilder,
     Key? key,

+ 272 - 0
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/color_picker.dart

@@ -0,0 +1,272 @@
+import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill/models/documents/style.dart';
+import 'package:flutter_quill/utils/color.dart';
+
+import 'toolbar_icon_button.dart';
+
+class FlowyColorButton extends StatefulWidget {
+  const FlowyColorButton({
+    required this.icon,
+    required this.controller,
+    required this.background,
+    this.iconSize = defaultIconSize,
+    this.iconTheme,
+    Key? key,
+  }) : super(key: key);
+
+  final IconData icon;
+  final double iconSize;
+  final bool background;
+  final QuillController controller;
+  final QuillIconTheme? iconTheme;
+
+  @override
+  _FlowyColorButtonState createState() => _FlowyColorButtonState();
+}
+
+class _FlowyColorButtonState extends State<FlowyColorButton> {
+  late bool _isToggledColor;
+  late bool _isToggledBackground;
+  late bool _isWhite;
+  late bool _isWhitebackground;
+
+  Style get _selectionStyle => widget.controller.getSelectionStyle();
+
+  void _didChangeEditingValue() {
+    setState(() {
+      _isToggledColor = _getIsToggledColor(widget.controller.getSelectionStyle().attributes);
+      _isToggledBackground = _getIsToggledBackground(widget.controller.getSelectionStyle().attributes);
+      _isWhite = _isToggledColor && _selectionStyle.attributes['color']!.value == '#ffffff';
+      _isWhitebackground = _isToggledBackground && _selectionStyle.attributes['background']!.value == '#ffffff';
+    });
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _isToggledColor = _getIsToggledColor(_selectionStyle.attributes);
+    _isToggledBackground = _getIsToggledBackground(_selectionStyle.attributes);
+    _isWhite = _isToggledColor && _selectionStyle.attributes['color']!.value == '#ffffff';
+    _isWhitebackground = _isToggledBackground && _selectionStyle.attributes['background']!.value == '#ffffff';
+    widget.controller.addListener(_didChangeEditingValue);
+  }
+
+  bool _getIsToggledColor(Map<String, Attribute> attrs) {
+    return attrs.containsKey(Attribute.color.key);
+  }
+
+  bool _getIsToggledBackground(Map<String, Attribute> attrs) {
+    return attrs.containsKey(Attribute.background.key);
+  }
+
+  @override
+  void didUpdateWidget(covariant FlowyColorButton oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (oldWidget.controller != widget.controller) {
+      oldWidget.controller.removeListener(_didChangeEditingValue);
+      widget.controller.addListener(_didChangeEditingValue);
+      _isToggledColor = _getIsToggledColor(_selectionStyle.attributes);
+      _isToggledBackground = _getIsToggledBackground(_selectionStyle.attributes);
+      _isWhite = _isToggledColor && _selectionStyle.attributes['color']!.value == '#ffffff';
+      _isWhitebackground = _isToggledBackground && _selectionStyle.attributes['background']!.value == '#ffffff';
+    }
+  }
+
+  @override
+  void dispose() {
+    widget.controller.removeListener(_didChangeEditingValue);
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+    final iconColor = _isToggledColor && !widget.background && !_isWhite
+        ? stringToColor(_selectionStyle.attributes['color']!.value)
+        : (widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color);
+
+    final iconColorBackground = _isToggledBackground && widget.background && !_isWhitebackground
+        ? stringToColor(_selectionStyle.attributes['background']!.value)
+        : (widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color);
+
+    final fillColor = _isToggledColor && !widget.background && _isWhite
+        ? stringToColor('#ffffff')
+        : (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor);
+    final fillColorBackground = _isToggledBackground && widget.background && _isWhitebackground
+        ? stringToColor('#ffffff')
+        : (widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor);
+
+    return QuillIconButton(
+      highlightElevation: 0,
+      hoverElevation: 0,
+      size: widget.iconSize * kIconButtonFactor,
+      icon: Icon(widget.icon, size: widget.iconSize, color: widget.background ? iconColorBackground : iconColor),
+      fillColor: widget.background ? fillColorBackground : fillColor,
+      onPressed: _showColorPicker,
+    );
+  }
+
+  void _changeColor(BuildContext context, Color color) {
+    var hex = color.value.toRadixString(16);
+    if (hex.startsWith('ff')) {
+      hex = hex.substring(2);
+    }
+    hex = '#$hex';
+    widget.controller.formatSelection(widget.background ? BackgroundAttribute(hex) : ColorAttribute(hex));
+    Navigator.of(context).pop();
+  }
+
+  void _showColorPicker() {
+    // FlowyPoppuWindow.show(
+    //   context,
+    //   size: Size(600, 200),
+    //   child: MaterialPicker(
+    //     pickerColor: const Color(0x00000000),
+    //     onColorChanged: (color) => _changeColor(context, color),
+    //   ),
+    // );
+
+    final style = widget.controller.getSelectionStyle();
+    final values = style.values.where((v) => v.key == Attribute.background.key).map((v) => v.value);
+    int initailColor = 0;
+    if (values.isNotEmpty) {
+      assert(values.length == 1);
+      initailColor = stringToHex(values.first);
+    }
+
+    StyledDialog(
+      child: SingleChildScrollView(
+        child: FlowyColorPicker(
+          onColorChanged: (color) {
+            if (color == null) {
+              widget.controller.formatSelection(BackgroundAttribute(null));
+              Navigator.of(context).pop();
+            } else {
+              _changeColor(context, color);
+            }
+          },
+          initailColor: initailColor,
+        ),
+      ),
+    ).show(context);
+  }
+}
+
+int stringToHex(String code) {
+  return int.parse(code.substring(1, 7), radix: 16) + 0xFF000000;
+}
+
+class FlowyColorPicker extends StatefulWidget {
+  final List<int> colors = [
+    0xffe8e0ff,
+    0xffffe7fd,
+    0xffffe7ee,
+    0xffffefe3,
+    0xfffff2cd,
+    0xfff5ffdc,
+    0xffddffd6,
+    0xffdefff1,
+    0xffdefff1,
+  ];
+  final Function(Color?) onColorChanged;
+  final int initailColor;
+  FlowyColorPicker({Key? key, required this.onColorChanged, this.initailColor = 0}) : super(key: key);
+
+  @override
+  State<FlowyColorPicker> createState() => _FlowyColorPickerState();
+}
+
+// if (shrinkWrap) {
+//       innerContent = IntrinsicWidth(child: IntrinsicHeight(child: innerContent));
+//     }
+class _FlowyColorPickerState extends State<FlowyColorPicker> {
+  @override
+  Widget build(BuildContext context) {
+    const double width = 480;
+    const int crossAxisCount = 6;
+    const double mainAxisSpacing = 10;
+    const double crossAxisSpacing = 10;
+    final numberOfRows = (widget.colors.length / crossAxisCount).ceil();
+
+    const perRowHeight = ((width - ((crossAxisCount - 1) * mainAxisSpacing)) / crossAxisCount);
+    final totalHeight = numberOfRows * perRowHeight + numberOfRows * crossAxisSpacing;
+
+    return Container(
+      constraints: BoxConstraints.tightFor(width: width, height: totalHeight),
+      child: CustomScrollView(
+        scrollDirection: Axis.vertical,
+        controller: ScrollController(),
+        physics: const ClampingScrollPhysics(),
+        slivers: [
+          SliverGrid(
+            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
+              crossAxisCount: crossAxisCount,
+              mainAxisSpacing: mainAxisSpacing,
+              crossAxisSpacing: crossAxisSpacing,
+              childAspectRatio: 1.0,
+            ),
+            delegate: SliverChildBuilderDelegate(
+              (BuildContext context, int index) {
+                if (widget.colors.length > index) {
+                  final isSelected = widget.colors[index] == widget.initailColor;
+                  return ColorItem(
+                    color: Color(widget.colors[index]),
+                    onPressed: widget.onColorChanged,
+                    isSelected: isSelected,
+                  );
+                } else {
+                  return null;
+                }
+              },
+              childCount: widget.colors.length,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+class ColorItem extends StatelessWidget {
+  final Function(Color?) onPressed;
+  final bool isSelected;
+  final Color color;
+  const ColorItem({
+    Key? key,
+    required this.color,
+    required this.onPressed,
+    this.isSelected = false,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    if (!isSelected) {
+      return RawMaterialButton(
+        onPressed: () {
+          onPressed(color);
+        },
+        elevation: 0,
+        hoverElevation: 0.6,
+        fillColor: color,
+        shape: const CircleBorder(),
+      );
+    } else {
+      return RawMaterialButton(
+        shape: const CircleBorder(side: BorderSide(color: Colors.white, width: 8)) +
+            CircleBorder(side: BorderSide(color: color, width: 4)),
+        onPressed: () {
+          if (isSelected) {
+            onPressed(null);
+          } else {
+            onPressed(color);
+          }
+        },
+        elevation: 1.0,
+        hoverElevation: 0.6,
+        fillColor: color,
+      );
+    }
+  }
+}

+ 3 - 7
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/header_button.dart

@@ -1,18 +1,14 @@
-import 'package:editor/flutter_quill.dart';
-import 'package:flowy_infra/image.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flutter/foundation.dart';
-import 'package:editor/models/documents/style.dart';
+import 'package:flutter_quill/models/documents/style.dart';
 import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
 
 import 'toolbar_icon_button.dart';
 
 class FlowyHeaderStyleButton extends StatefulWidget {
   const FlowyHeaderStyleButton({
     required this.controller,
-    this.iconSize = kDefaultIconSize,
+    this.iconSize = defaultIconSize,
     Key? key,
   }) : super(key: key);
 

+ 36 - 36
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/image_button.dart

@@ -1,13 +1,13 @@
-import 'package:editor/flutter_quill.dart';
+import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flutter/material.dart';
-import 'package:image_picker/image_picker.dart';
 
 import 'toolbar_icon_button.dart';
 
 class FlowyImageButton extends StatelessWidget {
   const FlowyImageButton({
     required this.controller,
-    this.iconSize = kDefaultIconSize,
+    this.iconSize = defaultIconSize,
     this.onImagePickCallback,
     this.fillColor,
     this.filePickImpl,
@@ -41,43 +41,43 @@ class FlowyImageButton extends StatelessWidget {
   }
 
   Future<void> _onPressedHandler(BuildContext context) async {
-    if (onImagePickCallback != null) {
-      final selector = mediaPickSettingSelector ?? ImageVideoUtils.selectMediaPickSetting;
-      final source = await selector(context);
-      if (source != null) {
-        if (source == MediaPickSetting.Gallery) {
-          _pickImage(context);
-        } else {
-          _typeLink(context);
-        }
-      }
-    } else {
-      _typeLink(context);
-    }
+    // if (onImagePickCallback != null) {
+    //   final selector = mediaPickSettingSelector ?? ImageVideoUtils.selectMediaPickSetting;
+    //   final source = await selector(context);
+    //   if (source != null) {
+    //     if (source == MediaPickSetting.Gallery) {
+    //       _pickImage(context);
+    //     } else {
+    //       _typeLink(context);
+    //     }
+    //   }
+    // } else {
+    //   _typeLink(context);
+    // }
   }
 
-  void _pickImage(BuildContext context) => ImageVideoUtils.handleImageButtonTap(
-        context,
-        controller,
-        ImageSource.gallery,
-        onImagePickCallback!,
-        filePickImpl: filePickImpl,
-        webImagePickImpl: webImagePickImpl,
-      );
+  // void _pickImage(BuildContext context) => ImageVideoUtils.handleImageButtonTap(
+  //       context,
+  //       controller,
+  //       ImageSource.gallery,
+  //       onImagePickCallback!,
+  //       filePickImpl: filePickImpl,
+  //       webImagePickImpl: webImagePickImpl,
+  //     );
 
   void _typeLink(BuildContext context) {
-    // showDialog<String>(
-    //   context: context,
-    //   builder: (_) => const LinkDialog(),
-    // ).then(_linkSubmitted);
-  }
-
-  void _linkSubmitted(String? value) {
-    if (value != null && value.isNotEmpty) {
-      final index = controller.selection.baseOffset;
-      final length = controller.selection.extentOffset - index;
+    TextFieldDialog(
+      title: 'URL',
+      value: "",
+      confirm: (newValue) {
+        if (newValue.isEmpty) {
+          return;
+        }
+        final index = controller.selection.baseOffset;
+        final length = controller.selection.extentOffset - index;
 
-      controller.replaceText(index, length, BlockEmbed.image(value), null);
-    }
+        controller.replaceText(index, length, BlockEmbed.image(newValue), null);
+      },
+    ).show(context);
   }
 }

+ 4 - 2
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/link_button.dart

@@ -1,15 +1,17 @@
 import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
-import 'package:editor/flutter_quill.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
+import 'toolbar_icon_button.dart';
+
 class FlowyLinkStyleButton extends StatefulWidget {
   const FlowyLinkStyleButton({
     required this.controller,
-    this.iconSize = kDefaultIconSize,
+    this.iconSize = defaultIconSize,
     Key? key,
   }) : super(key: key);
 

+ 3 - 3
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toggle_button.dart

@@ -1,5 +1,5 @@
-import 'package:editor/flutter_quill.dart';
-import 'package:editor/models/documents/style.dart';
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill/models/documents/style.dart';
 import 'package:flutter/material.dart';
 
 import 'toolbar_icon_button.dart';
@@ -14,7 +14,7 @@ class FlowyToggleStyleButton extends StatefulWidget {
     required this.attribute,
     required this.normalIcon,
     required this.controller,
-    this.iconSize = kDefaultIconSize,
+    this.iconSize = defaultIconSize,
     Key? key,
   }) : super(key: key);
 

+ 37 - 28
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/tool_bar.dart

@@ -1,14 +1,16 @@
 import 'dart:async';
 import 'dart:math';
 
-import 'package:editor/flutter_quill.dart';
+import 'package:flutter_quill/flutter_quill.dart';
 import 'package:flutter/material.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'check_button.dart';
+import 'color_picker.dart';
 import 'header_button.dart';
 import 'image_button.dart';
 import 'link_button.dart';
 import 'toggle_button.dart';
+import 'toolbar_icon_button.dart';
 
 class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
   final List<Widget> children;
@@ -36,7 +38,7 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
 
   factory EditorToolbar.basic({
     required QuillController controller,
-    double toolbarIconSize = kDefaultIconSize,
+    double toolbarIconSize = defaultIconSize,
     OnImagePickCallback? onImagePickCallback,
     OnVideoPickCallback? onVideoPickCallback,
     MediaPickSettingSelector? mediaPickSettingSelector,
@@ -85,7 +87,7 @@ class EditorToolbar extends StatelessWidget implements PreferredSizeWidget {
           iconSize: toolbarIconSize,
           controller: controller,
         ),
-        ColorButton(
+        FlowyColorButton(
           icon: Icons.format_color_fill,
           iconSize: toolbarIconSize,
           controller: controller,
@@ -175,14 +177,14 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
     return LayoutBuilder(
       builder: (BuildContext context, BoxConstraints constraints) {
         List<Widget> children = [];
-        double width = (widget.buttons.length + 2) * kDefaultIconSize * kIconButtonFactor;
+        double width = (widget.buttons.length + 2) * defaultIconSize * kIconButtonFactor;
         final isFit = constraints.maxWidth > width;
         if (!isFit) {
           children.add(_buildLeftArrow());
           width = width + 18;
         }
 
-        children.add(_buildScrollableList(constraints));
+        children.add(_buildScrollableList(constraints, isFit));
 
         if (!isFit) {
           children.add(_buildRightArrow());
@@ -228,31 +230,38 @@ class _ToolbarButtonListState extends State<ToolbarButtonList> with WidgetsBindi
     );
   }
 
-  Widget _buildScrollableList(BoxConstraints constraints) {
-    return ScrollConfiguration(
-      // Remove the glowing effect, as we already have the arrow indicators
-      behavior: _NoGlowBehavior(),
-      // The CustomScrollView is necessary so that the children are not
-      // stretched to the height of the toolbar, https://bit.ly/3uC3bjI
-      child: Expanded(
-        child: CustomScrollView(
-          scrollDirection: Axis.horizontal,
-          controller: _controller,
-          physics: const ClampingScrollPhysics(),
-          slivers: [
-            SliverList(
-              delegate: SliverChildBuilderDelegate(
-                (BuildContext context, int index) {
-                  return widget.buttons[index];
-                },
-                childCount: widget.buttons.length,
-                addAutomaticKeepAlives: false,
-              ),
-            )
-          ],
-        ),
+  // [[sliver: https://medium.com/flutter/slivers-demystified-6ff68ab0296f]]
+  Widget _buildScrollableList(BoxConstraints constraints, bool isFit) {
+    Widget child = Expanded(
+      child: CustomScrollView(
+        scrollDirection: Axis.horizontal,
+        controller: _controller,
+        physics: const ClampingScrollPhysics(),
+        slivers: [
+          SliverList(
+            delegate: SliverChildBuilderDelegate(
+              (BuildContext context, int index) {
+                return widget.buttons[index];
+              },
+              childCount: widget.buttons.length,
+              addAutomaticKeepAlives: false,
+            ),
+          )
+        ],
       ),
     );
+
+    if (!isFit) {
+      child = ScrollConfiguration(
+        // Remove the glowing effect, as we already have the arrow indicators
+        behavior: _NoGlowBehavior(),
+        // The CustomScrollView is necessary so that the children are not
+        // stretched to the height of the toolbar, https://bit.ly/3uC3bjI
+        child: child,
+      );
+    }
+
+    return child;
   }
 
   Widget _buildRightArrow() {

+ 2 - 0
app_flowy/lib/workspace/presentation/stack_page/doc/widget/toolbar/toolbar_icon_button.dart

@@ -4,6 +4,8 @@ import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
+const double defaultIconSize = 18;
+
 class ToolbarIconButton extends StatelessWidget {
   final double width;
   final VoidCallback? onPressed;

+ 37 - 36
app_flowy/lib/workspace/presentation/widgets/dialogs.dart

@@ -54,24 +54,22 @@ class _CreateTextFieldDialog extends State<TextFieldDialog> {
           ],
           FlowyFormTextInput(
             hintText: widget.value,
+            textStyle: const TextStyle(fontSize: 28, fontWeight: FontWeight.w400),
             autoFocus: true,
             onChanged: (text) {
               newValue = text;
             },
           ),
-          SizedBox(height: Insets.l),
-          SizedBox(
-            height: 40,
-            child: OkCancelButton(
-              onOkPressed: () {
-                widget.confirm(newValue);
-              },
-              onCancelPressed: () {
-                if (widget.cancel != null) {
-                  widget.cancel!();
-                }
-              },
-            ),
+          const VSpace(10),
+          OkCancelButton(
+            onOkPressed: () {
+              widget.confirm(newValue);
+            },
+            onCancelPressed: () {
+              if (widget.cancel != null) {
+                widget.cancel!();
+              }
+            },
           )
         ],
       ),
@@ -140,29 +138,32 @@ class OkCancelButton extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return Row(
-      mainAxisAlignment: MainAxisAlignment.end,
-      children: <Widget>[
-        if (onCancelPressed != null)
-          SecondaryTextButton(
-            cancelTitle ?? S.BTN_CANCEL,
-            onPressed: () {
-              onCancelPressed!();
-              AppGlobals.nav.pop();
-            },
-            bigMode: true,
-          ),
-        HSpace(Insets.m),
-        if (onOkPressed != null)
-          PrimaryTextButton(
-            okTitle ?? S.BTN_OK,
-            onPressed: () {
-              onOkPressed!();
-              AppGlobals.nav.pop();
-            },
-            bigMode: true,
-          ),
-      ],
+    return SizedBox(
+      height: 48,
+      child: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: <Widget>[
+          if (onCancelPressed != null)
+            SecondaryTextButton(
+              cancelTitle ?? S.BTN_CANCEL,
+              onPressed: () {
+                onCancelPressed!();
+                AppGlobals.nav.pop();
+              },
+              bigMode: true,
+            ),
+          HSpace(Insets.m),
+          if (onOkPressed != null)
+            PrimaryTextButton(
+              okTitle ?? S.BTN_OK,
+              onPressed: () {
+                onOkPressed!();
+                AppGlobals.nav.pop();
+              },
+              bigMode: true,
+            ),
+        ],
+      ),
     );
   }
 }

+ 10 - 10
app_flowy/lib/workspace/presentation/widgets/pop_up_window.dart

@@ -9,7 +9,10 @@ class FlowyPoppuWindow extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return child;
+    return Material(
+      child: child,
+      type: MaterialType.transparency,
+    );
   }
 
   static Future<void> show(
@@ -27,7 +30,7 @@ class FlowyPoppuWindow extends StatelessWidget {
       anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
       anchorSize: window.frame.size,
       anchorDirection: AnchorDirection.center,
-      style: FlowyOverlayStyle(blur: true),
+      style: FlowyOverlayStyle(blur: false),
     );
   }
 }
@@ -41,14 +44,11 @@ class PopupTextField extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return Material(
-      child: RoundedInputField(
-        style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
-        hintText: '',
-        normalBorderColor: const Color(0xffbdbdbd),
-        onChanged: textDidChange,
-      ),
-      type: MaterialType.transparency,
+    return RoundedInputField(
+      style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
+      hintText: '',
+      normalBorderColor: const Color(0xffbdbdbd),
+      onChanged: textDidChange,
     );
   }
 

+ 0 - 2
app_flowy/macos/Flutter/GeneratedPluginRegistrant.swift

@@ -5,7 +5,6 @@
 import FlutterMacOS
 import Foundation
 
-import editor
 import flowy_editor
 import flowy_infra_ui
 import flowy_sdk
@@ -14,7 +13,6 @@ import url_launcher_macos
 import window_size
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
-  EditorPlugin.register(with: registry.registrar(forPlugin: "EditorPlugin"))
   FlowyEditorPlugin.register(with: registry.registrar(forPlugin: "FlowyEditorPlugin"))
   FlowyInfraUIPlugin.register(with: registry.registrar(forPlugin: "FlowyInfraUIPlugin"))
   FlowySdkPlugin.register(with: registry.registrar(forPlugin: "FlowySdkPlugin"))

+ 0 - 6
app_flowy/macos/Podfile.lock

@@ -1,6 +1,4 @@
 PODS:
-  - editor (0.0.1):
-    - FlutterMacOS
   - flowy_editor (0.0.1):
     - FlutterMacOS
   - flowy_infra_ui (0.0.1):
@@ -16,7 +14,6 @@ PODS:
     - FlutterMacOS
 
 DEPENDENCIES:
-  - editor (from `Flutter/ephemeral/.symlinks/plugins/editor/macos`)
   - flowy_editor (from `Flutter/ephemeral/.symlinks/plugins/flowy_editor/macos`)
   - flowy_infra_ui (from `Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos`)
   - flowy_sdk (from `Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos`)
@@ -26,8 +23,6 @@ DEPENDENCIES:
   - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
 
 EXTERNAL SOURCES:
-  editor:
-    :path: Flutter/ephemeral/.symlinks/plugins/editor/macos
   flowy_editor:
     :path: Flutter/ephemeral/.symlinks/plugins/flowy_editor/macos
   flowy_infra_ui:
@@ -44,7 +39,6 @@ EXTERNAL SOURCES:
     :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
 
 SPEC CHECKSUMS:
-  editor: 380351c0334fbeb0e431e4e49629c9e2d925b66d
   flowy_editor: 26060a984848e6afac1f6a4455511f4114119d8d
   flowy_infra_ui: 9d5021b1610fe0476eb1191bf7cd41c4a4138d8f
   flowy_sdk: c302ac0a22dea596db0df8073b9637b2bf2ff6fd

+ 0 - 29
app_flowy/packages/editor/.gitignore

@@ -1,29 +0,0 @@
-# Miscellaneous
-*.class
-*.log
-*.pyc
-*.swp
-.DS_Store
-.atom/
-.buildlog/
-.history
-.svn/
-
-# IntelliJ related
-*.iml
-*.ipr
-*.iws
-.idea/
-
-# The .vscode folder contains launch configuration and tasks you configure in
-# VS Code which you may wish to be included in version control, so this line
-# is commented out by default.
-#.vscode/
-
-# Flutter/Dart/Pub related
-# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
-/pubspec.lock
-**/doc/api/
-.dart_tool/
-.packages
-build/

+ 0 - 10
app_flowy/packages/editor/.metadata

@@ -1,10 +0,0 @@
-# This file tracks properties of this Flutter project.
-# Used by Flutter tool to assess capabilities and perform upgrades etc.
-#
-# This file should be version controlled and should not be manually edited.
-
-version:
-  revision: 4b330ddbedab445481cc73d50a4695b9154b4e4f
-  channel: dev
-
-project_type: plugin

+ 0 - 3
app_flowy/packages/editor/CHANGELOG.md

@@ -1,3 +0,0 @@
-## 0.0.1
-
-* TODO: Describe initial release.

+ 0 - 1
app_flowy/packages/editor/LICENSE

@@ -1 +0,0 @@
-TODO: Add your license here.

+ 0 - 15
app_flowy/packages/editor/README.md

@@ -1,15 +0,0 @@
-# editor
-
-A new flutter plugin project.
-
-## Getting Started
-
-This project is a starting point for a Flutter
-[plug-in package](https://flutter.dev/developing-packages/),
-a specialized package that includes platform-specific implementation code for
-Android and/or iOS.
-
-For help getting started with Flutter, view our
-[online documentation](https://flutter.dev/docs), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
-

+ 0 - 4
app_flowy/packages/editor/analysis_options.yaml

@@ -1,4 +0,0 @@
-include: package:flutter_lints/flutter.yaml
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options

+ 0 - 46
app_flowy/packages/editor/example/.gitignore

@@ -1,46 +0,0 @@
-# Miscellaneous
-*.class
-*.log
-*.pyc
-*.swp
-.DS_Store
-.atom/
-.buildlog/
-.history
-.svn/
-
-# IntelliJ related
-*.iml
-*.ipr
-*.iws
-.idea/
-
-# The .vscode folder contains launch configuration and tasks you configure in
-# VS Code which you may wish to be included in version control, so this line
-# is commented out by default.
-#.vscode/
-
-# Flutter/Dart/Pub related
-**/doc/api/
-**/ios/Flutter/.last_build_id
-.dart_tool/
-.flutter-plugins
-.flutter-plugins-dependencies
-.packages
-.pub-cache/
-.pub/
-/build/
-
-# Web related
-lib/generated_plugin_registrant.dart
-
-# Symbolication related
-app.*.symbols
-
-# Obfuscation related
-app.*.map.json
-
-# Android Studio will place build artifacts here
-/android/app/debug
-/android/app/profile
-/android/app/release

+ 0 - 10
app_flowy/packages/editor/example/.metadata

@@ -1,10 +0,0 @@
-# This file tracks properties of this Flutter project.
-# Used by Flutter tool to assess capabilities and perform upgrades etc.
-#
-# This file should be version controlled and should not be manually edited.
-
-version:
-  revision: 4b330ddbedab445481cc73d50a4695b9154b4e4f
-  channel: dev
-
-project_type: app

+ 0 - 16
app_flowy/packages/editor/example/README.md

@@ -1,16 +0,0 @@
-# editor_example
-
-Demonstrates how to use the editor plugin.
-
-## Getting Started
-
-This project is a starting point for a Flutter application.
-
-A few resources to get you started if this is your first Flutter project:
-
-- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
-
-For help getting started with Flutter, view our
-[online documentation](https://flutter.dev/docs), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.

+ 0 - 29
app_flowy/packages/editor/example/analysis_options.yaml

@@ -1,29 +0,0 @@
-# This file configures the analyzer, which statically analyzes Dart code to
-# check for errors, warnings, and lints.
-#
-# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
-# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
-# invoked from the command line by running `flutter analyze`.
-
-# The following line activates a set of recommended lints for Flutter apps,
-# packages, and plugins designed to encourage good coding practices.
-include: package:flutter_lints/flutter.yaml
-
-linter:
-  # The lint rules applied to this project can be customized in the
-  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
-  # included above or to enable additional rules. A list of all available lints
-  # and their documentation is published at
-  # https://dart-lang.github.io/linter/lints/index.html.
-  #
-  # Instead of disabling a lint rule for the entire project in the
-  # section below, it can also be suppressed for a single line of code
-  # or a specific dart file by using the `// ignore: name_of_lint` and
-  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
-  # producing the lint.
-  rules:
-    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
-    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
-
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options

+ 0 - 33
app_flowy/packages/editor/example/lib/main.dart

@@ -1,33 +0,0 @@
-import 'package:flutter/material.dart';
-
-void main() {
-  runApp(const MyApp());
-}
-
-class MyApp extends StatefulWidget {
-  const MyApp({Key? key}) : super(key: key);
-
-  @override
-  State<MyApp> createState() => _MyAppState();
-}
-
-class _MyAppState extends State<MyApp> {
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return MaterialApp(
-      home: Scaffold(
-        appBar: AppBar(
-          title: const Text('Plugin example app'),
-        ),
-        body: const Center(
-          child: Text(''),
-        ),
-      ),
-    );
-  }
-}

+ 0 - 7
app_flowy/packages/editor/example/macos/.gitignore

@@ -1,7 +0,0 @@
-# Flutter-related
-**/Flutter/ephemeral/
-**/Pods/
-
-# Xcode-related
-**/dgph
-**/xcuserdata/

+ 0 - 2
app_flowy/packages/editor/example/macos/Flutter/Flutter-Debug.xcconfig

@@ -1,2 +0,0 @@
-#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
-#include "ephemeral/Flutter-Generated.xcconfig"

+ 0 - 2
app_flowy/packages/editor/example/macos/Flutter/Flutter-Release.xcconfig

@@ -1,2 +0,0 @@
-#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
-#include "ephemeral/Flutter-Generated.xcconfig"

+ 0 - 14
app_flowy/packages/editor/example/macos/Flutter/GeneratedPluginRegistrant.swift

@@ -1,14 +0,0 @@
-//
-//  Generated file. Do not edit.
-//
-
-import FlutterMacOS
-import Foundation
-
-import editor
-import url_launcher_macos
-
-func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
-  EditorPlugin.register(with: registry.registrar(forPlugin: "EditorPlugin"))
-  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
-}

+ 0 - 40
app_flowy/packages/editor/example/macos/Podfile

@@ -1,40 +0,0 @@
-platform :osx, '10.11'
-
-# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
-ENV['COCOAPODS_DISABLE_STATS'] = 'true'
-
-project 'Runner', {
-  'Debug' => :debug,
-  'Profile' => :release,
-  'Release' => :release,
-}
-
-def flutter_root
-  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
-  unless File.exist?(generated_xcode_build_settings_path)
-    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
-  end
-
-  File.foreach(generated_xcode_build_settings_path) do |line|
-    matches = line.match(/FLUTTER_ROOT\=(.*)/)
-    return matches[1].strip if matches
-  end
-  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
-end
-
-require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
-
-flutter_macos_podfile_setup
-
-target 'Runner' do
-  use_frameworks!
-  use_modular_headers!
-
-  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
-end
-
-post_install do |installer|
-  installer.pods_project.targets.each do |target|
-    flutter_additional_macos_build_settings(target)
-  end
-end

+ 0 - 572
app_flowy/packages/editor/example/macos/Runner.xcodeproj/project.pbxproj

@@ -1,572 +0,0 @@
-// !$*UTF8*$!
-{
-	archiveVersion = 1;
-	classes = {
-	};
-	objectVersion = 51;
-	objects = {
-
-/* Begin PBXAggregateTarget section */
-		33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
-			isa = PBXAggregateTarget;
-			buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
-			buildPhases = (
-				33CC111E2044C6BF0003C045 /* ShellScript */,
-			);
-			dependencies = (
-			);
-			name = "Flutter Assemble";
-			productName = FLX;
-		};
-/* End PBXAggregateTarget section */
-
-/* Begin PBXBuildFile section */
-		335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
-		33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
-		33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
-		33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
-		33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
-		33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
-			proxyType = 1;
-			remoteGlobalIDString = 33CC111A2044C6BA0003C045;
-			remoteInfo = FLX;
-		};
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXCopyFilesBuildPhase section */
-		33CC110E2044A8840003C045 /* Bundle Framework */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = "";
-			dstSubfolderSpec = 10;
-			files = (
-			);
-			name = "Bundle Framework";
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
-		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
-		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
-		33CC10ED2044A3C60003C045 /* editor_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "editor_example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
-		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
-		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
-		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
-		33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
-		33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
-		33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
-		33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
-		33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
-		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
-		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
-		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
-		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
-		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
-		33CC10EA2044A3C60003C045 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
-		33BA886A226E78AF003329D5 /* Configs */ = {
-			isa = PBXGroup;
-			children = (
-				33E5194F232828860026EE4D /* AppInfo.xcconfig */,
-				9740EEB21CF90195004384FC /* Debug.xcconfig */,
-				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
-				333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
-			);
-			path = Configs;
-			sourceTree = "<group>";
-		};
-		33CC10E42044A3C60003C045 = {
-			isa = PBXGroup;
-			children = (
-				33FAB671232836740065AC1E /* Runner */,
-				33CEB47122A05771004F2AC0 /* Flutter */,
-				33CC10EE2044A3C60003C045 /* Products */,
-				D73912EC22F37F3D000D13A0 /* Frameworks */,
-			);
-			sourceTree = "<group>";
-		};
-		33CC10EE2044A3C60003C045 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				33CC10ED2044A3C60003C045 /* editor_example.app */,
-			);
-			name = Products;
-			sourceTree = "<group>";
-		};
-		33CC11242044D66E0003C045 /* Resources */ = {
-			isa = PBXGroup;
-			children = (
-				33CC10F22044A3C60003C045 /* Assets.xcassets */,
-				33CC10F42044A3C60003C045 /* MainMenu.xib */,
-				33CC10F72044A3C60003C045 /* Info.plist */,
-			);
-			name = Resources;
-			path = ..;
-			sourceTree = "<group>";
-		};
-		33CEB47122A05771004F2AC0 /* Flutter */ = {
-			isa = PBXGroup;
-			children = (
-				335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
-				33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
-				33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
-				33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
-			);
-			path = Flutter;
-			sourceTree = "<group>";
-		};
-		33FAB671232836740065AC1E /* Runner */ = {
-			isa = PBXGroup;
-			children = (
-				33CC10F02044A3C60003C045 /* AppDelegate.swift */,
-				33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
-				33E51913231747F40026EE4D /* DebugProfile.entitlements */,
-				33E51914231749380026EE4D /* Release.entitlements */,
-				33CC11242044D66E0003C045 /* Resources */,
-				33BA886A226E78AF003329D5 /* Configs */,
-			);
-			path = Runner;
-			sourceTree = "<group>";
-		};
-		D73912EC22F37F3D000D13A0 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
-		33CC10EC2044A3C60003C045 /* Runner */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
-			buildPhases = (
-				33CC10E92044A3C60003C045 /* Sources */,
-				33CC10EA2044A3C60003C045 /* Frameworks */,
-				33CC10EB2044A3C60003C045 /* Resources */,
-				33CC110E2044A8840003C045 /* Bundle Framework */,
-				3399D490228B24CF009A79C7 /* ShellScript */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-				33CC11202044C79F0003C045 /* PBXTargetDependency */,
-			);
-			name = Runner;
-			productName = Runner;
-			productReference = 33CC10ED2044A3C60003C045 /* editor_example.app */;
-			productType = "com.apple.product-type.application";
-		};
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
-		33CC10E52044A3C60003C045 /* Project object */ = {
-			isa = PBXProject;
-			attributes = {
-				LastSwiftUpdateCheck = 0920;
-				LastUpgradeCheck = 1300;
-				ORGANIZATIONNAME = "";
-				TargetAttributes = {
-					33CC10EC2044A3C60003C045 = {
-						CreatedOnToolsVersion = 9.2;
-						LastSwiftMigration = 1100;
-						ProvisioningStyle = Automatic;
-						SystemCapabilities = {
-							com.apple.Sandbox = {
-								enabled = 1;
-							};
-						};
-					};
-					33CC111A2044C6BA0003C045 = {
-						CreatedOnToolsVersion = 9.2;
-						ProvisioningStyle = Manual;
-					};
-				};
-			};
-			buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
-			compatibilityVersion = "Xcode 9.3";
-			developmentRegion = en;
-			hasScannedForEncodings = 0;
-			knownRegions = (
-				en,
-				Base,
-			);
-			mainGroup = 33CC10E42044A3C60003C045;
-			productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
-			projectDirPath = "";
-			projectRoot = "";
-			targets = (
-				33CC10EC2044A3C60003C045 /* Runner */,
-				33CC111A2044C6BA0003C045 /* Flutter Assemble */,
-			);
-		};
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
-		33CC10EB2044A3C60003C045 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
-				33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
-		3399D490228B24CF009A79C7 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
-		};
-		33CC111E2044C6BF0003C045 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-				Flutter/ephemeral/FlutterInputs.xcfilelist,
-			);
-			inputPaths = (
-				Flutter/ephemeral/tripwire,
-			);
-			outputFileListPaths = (
-				Flutter/ephemeral/FlutterOutputs.xcfilelist,
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
-		};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
-		33CC10E92044A3C60003C045 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
-				33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
-				335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXTargetDependency section */
-		33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
-			targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
-		};
-/* End PBXTargetDependency section */
-
-/* Begin PBXVariantGroup section */
-		33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
-			isa = PBXVariantGroup;
-			children = (
-				33CC10F52044A3C60003C045 /* Base */,
-			);
-			name = MainMenu.xib;
-			path = Runner;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
-		338D0CE9231458BD00FA5F75 /* Profile */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_CXX_LIBRARY = "libc++";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_ARC = YES;
-				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-				CLANG_WARN_BOOL_CONVERSION = YES;
-				CLANG_WARN_CONSTANT_CONVERSION = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_EMPTY_BODY = YES;
-				CLANG_WARN_ENUM_CONVERSION = YES;
-				CLANG_WARN_INFINITE_RECURSION = YES;
-				CLANG_WARN_INT_CONVERSION = YES;
-				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
-				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
-				CLANG_WARN_SUSPICIOUS_MOVE = YES;
-				CODE_SIGN_IDENTITY = "-";
-				COPY_PHASE_STRIP = NO;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				ENABLE_NS_ASSERTIONS = NO;
-				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-				GCC_WARN_UNUSED_FUNCTION = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				SDKROOT = macosx;
-				SWIFT_COMPILATION_MODE = wholemodule;
-				SWIFT_OPTIMIZATION_LEVEL = "-O";
-			};
-			name = Profile;
-		};
-		338D0CEA231458BD00FA5F75 /* Profile */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
-			buildSettings = {
-				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				CLANG_ENABLE_MODULES = YES;
-				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
-				CODE_SIGN_STYLE = Automatic;
-				COMBINE_HIDPI_IMAGES = YES;
-				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-				);
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_VERSION = 5.0;
-			};
-			name = Profile;
-		};
-		338D0CEB231458BD00FA5F75 /* Profile */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CODE_SIGN_STYLE = Manual;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Profile;
-		};
-		33CC10F92044A3C60003C045 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_CXX_LIBRARY = "libc++";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_ARC = YES;
-				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-				CLANG_WARN_BOOL_CONVERSION = YES;
-				CLANG_WARN_CONSTANT_CONVERSION = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_EMPTY_BODY = YES;
-				CLANG_WARN_ENUM_CONVERSION = YES;
-				CLANG_WARN_INFINITE_RECURSION = YES;
-				CLANG_WARN_INT_CONVERSION = YES;
-				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
-				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
-				CLANG_WARN_SUSPICIOUS_MOVE = YES;
-				CODE_SIGN_IDENTITY = "-";
-				COPY_PHASE_STRIP = NO;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				ENABLE_TESTABILITY = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_DYNAMIC_NO_PIC = NO;
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-				);
-				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-				GCC_WARN_UNUSED_FUNCTION = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
-				MTL_ENABLE_DEBUG_INFO = YES;
-				ONLY_ACTIVE_ARCH = YES;
-				SDKROOT = macosx;
-				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-			};
-			name = Debug;
-		};
-		33CC10FA2044A3C60003C045 /* Release */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-				CLANG_CXX_LIBRARY = "libc++";
-				CLANG_ENABLE_MODULES = YES;
-				CLANG_ENABLE_OBJC_ARC = YES;
-				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-				CLANG_WARN_BOOL_CONVERSION = YES;
-				CLANG_WARN_CONSTANT_CONVERSION = YES;
-				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_EMPTY_BODY = YES;
-				CLANG_WARN_ENUM_CONVERSION = YES;
-				CLANG_WARN_INFINITE_RECURSION = YES;
-				CLANG_WARN_INT_CONVERSION = YES;
-				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
-				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
-				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
-				CLANG_WARN_SUSPICIOUS_MOVE = YES;
-				CODE_SIGN_IDENTITY = "-";
-				COPY_PHASE_STRIP = NO;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				ENABLE_NS_ASSERTIONS = NO;
-				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu11;
-				GCC_NO_COMMON_BLOCKS = YES;
-				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
-				GCC_WARN_UNUSED_FUNCTION = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				MACOSX_DEPLOYMENT_TARGET = 10.11;
-				MTL_ENABLE_DEBUG_INFO = NO;
-				SDKROOT = macosx;
-				SWIFT_COMPILATION_MODE = wholemodule;
-				SWIFT_OPTIMIZATION_LEVEL = "-O";
-			};
-			name = Release;
-		};
-		33CC10FC2044A3C60003C045 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
-			buildSettings = {
-				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				CLANG_ENABLE_MODULES = YES;
-				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
-				CODE_SIGN_STYLE = Automatic;
-				COMBINE_HIDPI_IMAGES = YES;
-				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-				);
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
-				SWIFT_VERSION = 5.0;
-			};
-			name = Debug;
-		};
-		33CC10FD2044A3C60003C045 /* Release */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
-			buildSettings = {
-				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
-				CLANG_ENABLE_MODULES = YES;
-				CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
-				CODE_SIGN_STYLE = Automatic;
-				COMBINE_HIDPI_IMAGES = YES;
-				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/../Frameworks",
-				);
-				PROVISIONING_PROFILE_SPECIFIER = "";
-				SWIFT_VERSION = 5.0;
-			};
-			name = Release;
-		};
-		33CC111C2044C6BA0003C045 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CODE_SIGN_STYLE = Manual;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		33CC111D2044C6BA0003C045 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				CODE_SIGN_STYLE = Automatic;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Release;
-		};
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
-		33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				33CC10F92044A3C60003C045 /* Debug */,
-				33CC10FA2044A3C60003C045 /* Release */,
-				338D0CE9231458BD00FA5F75 /* Profile */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				33CC10FC2044A3C60003C045 /* Debug */,
-				33CC10FD2044A3C60003C045 /* Release */,
-				338D0CEA231458BD00FA5F75 /* Profile */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				33CC111C2044C6BA0003C045 /* Debug */,
-				33CC111D2044C6BA0003C045 /* Release */,
-				338D0CEB231458BD00FA5F75 /* Profile */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-/* End XCConfigurationList section */
-	};
-	rootObject = 33CC10E52044A3C60003C045 /* Project object */;
-}

+ 0 - 8
app_flowy/packages/editor/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IDEDidComputeMac32BitWarning</key>
-	<true/>
-</dict>
-</plist>

+ 0 - 87
app_flowy/packages/editor/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "1300"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
-               BuildableName = "editor_example.app"
-               BlueprintName = "Runner"
-               ReferencedContainer = "container:Runner.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES">
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
-            BuildableName = "editor_example.app"
-            BlueprintName = "Runner"
-            ReferencedContainer = "container:Runner.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <Testables>
-      </Testables>
-   </TestAction>
-   <LaunchAction
-      buildConfiguration = "Debug"
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
-            BuildableName = "editor_example.app"
-            BlueprintName = "Runner"
-            ReferencedContainer = "container:Runner.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </LaunchAction>
-   <ProfileAction
-      buildConfiguration = "Profile"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
-            BuildableName = "editor_example.app"
-            BlueprintName = "Runner"
-            ReferencedContainer = "container:Runner.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 0 - 7
app_flowy/packages/editor/example/macos/Runner.xcworkspace/contents.xcworkspacedata

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Workspace
-   version = "1.0">
-   <FileRef
-      location = "group:Runner.xcodeproj">
-   </FileRef>
-</Workspace>

+ 0 - 8
app_flowy/packages/editor/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>IDEDidComputeMac32BitWarning</key>
-	<true/>
-</dict>
-</plist>

+ 0 - 9
app_flowy/packages/editor/example/macos/Runner/AppDelegate.swift

@@ -1,9 +0,0 @@
-import Cocoa
-import FlutterMacOS
-
-@NSApplicationMain
-class AppDelegate: FlutterAppDelegate {
-  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
-    return true
-  }
-}

+ 0 - 68
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -1,68 +0,0 @@
-{
-  "images" : [
-    {
-      "size" : "16x16",
-      "idiom" : "mac",
-      "filename" : "app_icon_16.png",
-      "scale" : "1x"
-    },
-    {
-      "size" : "16x16",
-      "idiom" : "mac",
-      "filename" : "app_icon_32.png",
-      "scale" : "2x"
-    },
-    {
-      "size" : "32x32",
-      "idiom" : "mac",
-      "filename" : "app_icon_32.png",
-      "scale" : "1x"
-    },
-    {
-      "size" : "32x32",
-      "idiom" : "mac",
-      "filename" : "app_icon_64.png",
-      "scale" : "2x"
-    },
-    {
-      "size" : "128x128",
-      "idiom" : "mac",
-      "filename" : "app_icon_128.png",
-      "scale" : "1x"
-    },
-    {
-      "size" : "128x128",
-      "idiom" : "mac",
-      "filename" : "app_icon_256.png",
-      "scale" : "2x"
-    },
-    {
-      "size" : "256x256",
-      "idiom" : "mac",
-      "filename" : "app_icon_256.png",
-      "scale" : "1x"
-    },
-    {
-      "size" : "256x256",
-      "idiom" : "mac",
-      "filename" : "app_icon_512.png",
-      "scale" : "2x"
-    },
-    {
-      "size" : "512x512",
-      "idiom" : "mac",
-      "filename" : "app_icon_512.png",
-      "scale" : "1x"
-    },
-    {
-      "size" : "512x512",
-      "idiom" : "mac",
-      "filename" : "app_icon_1024.png",
-      "scale" : "2x"
-    }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
-}

BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png


BIN
app_flowy/packages/editor/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png


+ 0 - 339
app_flowy/packages/editor/example/macos/Runner/Base.lproj/MainMenu.xib

@@ -1,339 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
-    <dependencies>
-        <deployment identifier="macosx"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
-            <connections>
-                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
-            </connections>
-        </customObject>
-        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
-        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
-        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
-            <connections>
-                <outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
-                <outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
-            </connections>
-        </customObject>
-        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
-        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
-            <items>
-                <menuItem title="APP_NAME" id="1Xt-HY-uBw">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
-                        <items>
-                            <menuItem title="About APP_NAME" id="5kV-Vb-QxS">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
-                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
-                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
-                            <menuItem title="Services" id="NMo-om-nkz">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
-                            <menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
-                                <connections>
-                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
-                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                <connections>
-                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Show All" id="Kd2-mp-pUS">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
-                            <menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
-                                <connections>
-                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Edit" id="5QF-Oa-p0T">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
-                        <items>
-                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
-                                <connections>
-                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
-                                <connections>
-                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
-                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
-                                <connections>
-                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
-                                <connections>
-                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
-                                <connections>
-                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
-                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                <connections>
-                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Delete" id="pa3-QI-u2k">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
-                                <connections>
-                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
-                            <menuItem title="Find" id="4EN-yA-p0u">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
-                                    <items>
-                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
-                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
-                                            <connections>
-                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
-                                            <connections>
-                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
-                                    <items>
-                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
-                                            <connections>
-                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
-                                            <connections>
-                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
-                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Substitutions" id="9ic-FL-obx">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
-                                    <items>
-                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
-                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Smart Links" id="cwL-P1-jid">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
-                                    <items>
-                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                            <menuItem title="Speech" id="xrE-MZ-jX0">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
-                                    <items>
-                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
-                                            </connections>
-                                        </menuItem>
-                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
-                                            <modifierMask key="keyEquivalentModifierMask"/>
-                                            <connections>
-                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
-                                            </connections>
-                                        </menuItem>
-                                    </items>
-                                </menu>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="View" id="H8h-7b-M4v">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="View" id="HyV-fh-RgO">
-                        <items>
-                            <menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
-                                <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
-                                <connections>
-                                    <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-                <menuItem title="Window" id="aUF-d1-5bR">
-                    <modifierMask key="keyEquivalentModifierMask"/>
-                    <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
-                        <items>
-                            <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
-                                <connections>
-                                    <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem title="Zoom" id="R4o-n2-Eq4">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
-                                </connections>
-                            </menuItem>
-                            <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
-                            <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
-                                <modifierMask key="keyEquivalentModifierMask"/>
-                                <connections>
-                                    <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
-                                </connections>
-                            </menuItem>
-                        </items>
-                    </menu>
-                </menuItem>
-            </items>
-            <point key="canvasLocation" x="142" y="-258"/>
-        </menu>
-        <window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
-            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
-            <rect key="contentRect" x="335" y="390" width="800" height="600"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
-            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
-                <rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
-                <autoresizingMask key="autoresizingMask"/>
-            </view>
-        </window>
-    </objects>
-</document>

+ 0 - 14
app_flowy/packages/editor/example/macos/Runner/Configs/AppInfo.xcconfig

@@ -1,14 +0,0 @@
-// Application-level settings for the Runner target.
-//
-// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
-// future. If not, the values below would default to using the project name when this becomes a
-// 'flutter create' template.
-
-// The application's name. By default this is also the title of the Flutter window.
-PRODUCT_NAME = editor_example
-
-// The application's bundle identifier
-PRODUCT_BUNDLE_IDENTIFIER = com.example.editorExample
-
-// The copyright displayed in application information
-PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved.

+ 0 - 2
app_flowy/packages/editor/example/macos/Runner/Configs/Debug.xcconfig

@@ -1,2 +0,0 @@
-#include "../../Flutter/Flutter-Debug.xcconfig"
-#include "Warnings.xcconfig"

+ 0 - 2
app_flowy/packages/editor/example/macos/Runner/Configs/Release.xcconfig

@@ -1,2 +0,0 @@
-#include "../../Flutter/Flutter-Release.xcconfig"
-#include "Warnings.xcconfig"

+ 0 - 13
app_flowy/packages/editor/example/macos/Runner/Configs/Warnings.xcconfig

@@ -1,13 +0,0 @@
-WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
-GCC_WARN_UNDECLARED_SELECTOR = YES
-CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
-CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
-CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
-CLANG_WARN_PRAGMA_PACK = YES
-CLANG_WARN_STRICT_PROTOTYPES = YES
-CLANG_WARN_COMMA = YES
-GCC_WARN_STRICT_SELECTOR_MATCH = YES
-CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
-CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
-GCC_WARN_SHADOW = YES
-CLANG_WARN_UNREACHABLE_CODE = YES

+ 0 - 12
app_flowy/packages/editor/example/macos/Runner/DebugProfile.entitlements

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>com.apple.security.app-sandbox</key>
-	<true/>
-	<key>com.apple.security.cs.allow-jit</key>
-	<true/>
-	<key>com.apple.security.network.server</key>
-	<true/>
-</dict>
-</plist>

+ 0 - 32
app_flowy/packages/editor/example/macos/Runner/Info.plist

@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>CFBundleDevelopmentRegion</key>
-	<string>$(DEVELOPMENT_LANGUAGE)</string>
-	<key>CFBundleExecutable</key>
-	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIconFile</key>
-	<string></string>
-	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-	<key>CFBundleInfoDictionaryVersion</key>
-	<string>6.0</string>
-	<key>CFBundleName</key>
-	<string>$(PRODUCT_NAME)</string>
-	<key>CFBundlePackageType</key>
-	<string>APPL</string>
-	<key>CFBundleShortVersionString</key>
-	<string>$(FLUTTER_BUILD_NAME)</string>
-	<key>CFBundleVersion</key>
-	<string>$(FLUTTER_BUILD_NUMBER)</string>
-	<key>LSMinimumSystemVersion</key>
-	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
-	<key>NSHumanReadableCopyright</key>
-	<string>$(PRODUCT_COPYRIGHT)</string>
-	<key>NSMainNibFile</key>
-	<string>MainMenu</string>
-	<key>NSPrincipalClass</key>
-	<string>NSApplication</string>
-</dict>
-</plist>

+ 0 - 15
app_flowy/packages/editor/example/macos/Runner/MainFlutterWindow.swift

@@ -1,15 +0,0 @@
-import Cocoa
-import FlutterMacOS
-
-class MainFlutterWindow: NSWindow {
-  override func awakeFromNib() {
-    let flutterViewController = FlutterViewController.init()
-    let windowFrame = self.frame
-    self.contentViewController = flutterViewController
-    self.setFrame(windowFrame, display: true)
-
-    RegisterGeneratedPlugins(registry: flutterViewController)
-
-    super.awakeFromNib()
-  }
-}

+ 0 - 8
app_flowy/packages/editor/example/macos/Runner/Release.entitlements

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>com.apple.security.app-sandbox</key>
-	<true/>
-</dict>
-</plist>

+ 0 - 404
app_flowy/packages/editor/example/pubspec.lock

@@ -1,404 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.8.2"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  charcode:
-    dependency: transitive
-    description:
-      name: charcode
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.15.0"
-  cross_file:
-    dependency: transitive
-    description:
-      name: cross_file
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.1+5"
-  csslib:
-    dependency: transitive
-    description:
-      name: csslib
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.17.1"
-  cupertino_icons:
-    dependency: "direct main"
-    description:
-      name: cupertino_icons
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.3"
-  diff_match_patch:
-    dependency: transitive
-    description:
-      name: diff_match_patch
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.1"
-  editor:
-    dependency: "direct main"
-    description:
-      path: ".."
-      relative: true
-    source: path
-    version: "0.0.1"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_colorpicker:
-    dependency: transitive
-    description:
-      name: flutter_colorpicker
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.5.0"
-  flutter_inappwebview:
-    dependency: transitive
-    description:
-      name: flutter_inappwebview
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.3.2"
-  flutter_keyboard_visibility:
-    dependency: transitive
-    description:
-      name: flutter_keyboard_visibility
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.0.3"
-  flutter_keyboard_visibility_platform_interface:
-    dependency: transitive
-    description:
-      name: flutter_keyboard_visibility_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  flutter_keyboard_visibility_web:
-    dependency: transitive
-    description:
-      name: flutter_keyboard_visibility_web
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.4"
-  flutter_plugin_android_lifecycle:
-    dependency: transitive
-    description:
-      name: flutter_plugin_android_lifecycle
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.3"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_web_plugins:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  html:
-    dependency: transitive
-    description:
-      name: html
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.15.0"
-  http:
-    dependency: transitive
-    description:
-      name: http
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.13.4"
-  http_parser:
-    dependency: transitive
-    description:
-      name: http_parser
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.0.0"
-  image_picker:
-    dependency: transitive
-    description:
-      name: image_picker
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.8.4+2"
-  image_picker_for_web:
-    dependency: transitive
-    description:
-      name: image_picker_for_web
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.3"
-  image_picker_platform_interface:
-    dependency: transitive
-    description:
-      name: image_picker_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.4.1"
-  js:
-    dependency: transitive
-    description:
-      name: js
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.6.3"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.1"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.11"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.7.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  pedantic:
-    dependency: transitive
-    description:
-      name: pedantic
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.11.1"
-  photo_view:
-    dependency: transitive
-    description:
-      name: photo_view
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.0"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.2"
-  quiver:
-    dependency: transitive
-    description:
-      name: quiver
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.1"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.0"
-  string_validator:
-    dependency: transitive
-    description:
-      name: string_validator
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.0"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.0"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.3"
-  tuple:
-    dependency: transitive
-    description:
-      name: tuple
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.0"
-  url_launcher:
-    dependency: transitive
-    description:
-      name: url_launcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.0.12"
-  url_launcher_linux:
-    dependency: transitive
-    description:
-      name: url_launcher_linux
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.2"
-  url_launcher_macos:
-    dependency: transitive
-    description:
-      name: url_launcher_macos
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.2"
-  url_launcher_platform_interface:
-    dependency: transitive
-    description:
-      name: url_launcher_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.4"
-  url_launcher_web:
-    dependency: transitive
-    description:
-      name: url_launcher_web
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.4"
-  url_launcher_windows:
-    dependency: transitive
-    description:
-      name: url_launcher_windows
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.2"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  video_player:
-    dependency: transitive
-    description:
-      name: video_player
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.2.5"
-  video_player_platform_interface:
-    dependency: transitive
-    description:
-      name: video_player_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.2.0"
-  video_player_web:
-    dependency: transitive
-    description:
-      name: video_player_web
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.4"
-  youtube_player_flutter:
-    dependency: transitive
-    description:
-      name: youtube_player_flutter
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "8.0.0"
-sdks:
-  dart: ">=2.15.0-116.0.dev <3.0.0"
-  flutter: ">=2.5.0"

+ 0 - 84
app_flowy/packages/editor/example/pubspec.yaml

@@ -1,84 +0,0 @@
-name: editor_example
-description: Demonstrates how to use the editor plugin.
-
-# The following line prevents the package from being accidentally published to
-# pub.dev using `flutter pub publish`. This is preferred for private packages.
-publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-
-environment:
-  sdk: ">=2.15.0-116.0.dev <3.0.0"
-
-# Dependencies specify other packages that your package needs in order to work.
-# To automatically upgrade your package dependencies to the latest versions
-# consider running `flutter pub upgrade --major-versions`. Alternatively,
-# dependencies can be manually updated by changing the version numbers below to
-# the latest version available on pub.dev. To see which dependencies have newer
-# versions available, run `flutter pub outdated`.
-dependencies:
-  flutter:
-    sdk: flutter
-
-  editor:
-    # When depending on this package from a real application you should use:
-    #   editor: ^x.y.z
-    # See https://dart.dev/tools/pub/dependencies#version-constraints
-    # The example app is bundled with the plugin so we use a path dependency on
-    # the parent directory to use the current plugin's version.
-    path: ../
-
-  # The following adds the Cupertino Icons font to your application.
-  # Use with the CupertinoIcons class for iOS style icons.
-  cupertino_icons: ^1.0.2
-
-dev_dependencies:
-  flutter_test:
-    sdk: flutter
-
-  # The "flutter_lints" package below contains a set of recommended lints to
-  # encourage good coding practices. The lint set provided by the package is
-  # activated in the `analysis_options.yaml` file located at the root of your
-  # package. See that file for information about deactivating specific lint
-  # rules and activating additional ones.
-  flutter_lints: ^1.0.0
-
-# For information on the generic Dart part of this file, see the
-# following page: https://dart.dev/tools/pub/pubspec
-
-# The following section is specific to Flutter.
-flutter:
-
-  # The following line ensures that the Material Icons font is
-  # included with your application, so that you can use the icons in
-  # the material Icons class.
-  uses-material-design: true
-
-  # To add assets to your application, add an assets section, like this:
-  # assets:
-  #   - images/a_dot_burr.jpeg
-  #   - images/a_dot_ham.jpeg
-
-  # An image asset can refer to one or more resolution-specific "variants", see
-  # https://flutter.dev/assets-and-images/#resolution-aware.
-
-  # For details regarding adding assets from package dependencies, see
-  # https://flutter.dev/assets-and-images/#from-packages
-
-  # To add custom fonts to your application, add a fonts section here,
-  # in this "flutter" section. Each entry in this list should have a
-  # "family" key with the font family name, and a "fonts" key with a
-  # list giving the asset and other descriptors for the font. For
-  # example:
-  # fonts:
-  #   - family: Schyler
-  #     fonts:
-  #       - asset: fonts/Schyler-Regular.ttf
-  #       - asset: fonts/Schyler-Italic.ttf
-  #         style: italic
-  #   - family: Trajan Pro
-  #     fonts:
-  #       - asset: fonts/TrajanPro.ttf
-  #       - asset: fonts/TrajanPro_Bold.ttf
-  #         weight: 700
-  #
-  # For details regarding fonts from package dependencies,
-  # see https://flutter.dev/custom-fonts/#from-packages

+ 0 - 11
app_flowy/packages/editor/example/test/widget_test.dart

@@ -1,11 +0,0 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility that Flutter provides. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-// import 'package:flutter/material.dart';
-// import 'package:flutter_test/flutter_test.dart';
-
-void main() {}

+ 0 - 11
app_flowy/packages/editor/lib/flutter_quill.dart

@@ -1,11 +0,0 @@
-library flutter_quill;
-
-export 'src/models/documents/attribute.dart';
-export 'src/models/documents/document.dart';
-export 'src/models/documents/nodes/embed.dart';
-export 'src/models/documents/nodes/leaf.dart';
-export 'src/models/quill_delta.dart';
-export 'src/widgets/controller.dart';
-export 'src/widgets/default_styles.dart';
-export 'src/widgets/editor.dart';
-export 'src/widgets/toolbar.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/attribute.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/documents/attribute.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/document.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/documents/document.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/history.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/documents/history.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/block.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/block.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/container.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/container.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/embed.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/embed.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/leaf.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/leaf.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/line.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/line.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/nodes/node.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../../src/models/documents/nodes/node.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/documents/style.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/documents/style.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/quill_delta.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../src/models/quill_delta.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/rules/delete.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/rules/delete.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/rules/format.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/rules/format.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/rules/insert.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/rules/insert.dart';

+ 0 - 3
app_flowy/packages/editor/lib/models/rules/rule.dart

@@ -1,3 +0,0 @@
-/// TODO: Remove this file in the next breaking release, because implementation
-/// files should be located in the src folder, https://bit.ly/3fA23Yz.
-export '../../src/models/rules/rule.dart';

+ 0 - 313
app_flowy/packages/editor/lib/src/models/documents/attribute.dart

@@ -1,313 +0,0 @@
-// ignore_for_file: constant_identifier_names
-
-import 'dart:collection';
-
-import 'package:quiver/core.dart';
-
-enum AttributeScope {
-  INLINE, // refer to https://quilljs.com/docs/formats/#inline
-  BLOCK, // refer to https://quilljs.com/docs/formats/#block
-  EMBEDS, // refer to https://quilljs.com/docs/formats/#embeds
-  IGNORE, // attributes that can be ignored
-}
-
-class Attribute<T> {
-  Attribute(this.key, this.scope, this.value);
-
-  final String key;
-  final AttributeScope scope;
-  final T value;
-
-  static final Map<String, Attribute> _registry = LinkedHashMap.of({
-    Attribute.bold.key: Attribute.bold,
-    Attribute.italic.key: Attribute.italic,
-    Attribute.small.key: Attribute.small,
-    Attribute.underline.key: Attribute.underline,
-    Attribute.strikeThrough.key: Attribute.strikeThrough,
-    Attribute.inlineCode.key: Attribute.inlineCode,
-    Attribute.font.key: Attribute.font,
-    Attribute.size.key: Attribute.size,
-    Attribute.link.key: Attribute.link,
-    Attribute.color.key: Attribute.color,
-    Attribute.background.key: Attribute.background,
-    Attribute.placeholder.key: Attribute.placeholder,
-    Attribute.header.key: Attribute.header,
-    Attribute.align.key: Attribute.align,
-    Attribute.list.key: Attribute.list,
-    Attribute.codeBlock.key: Attribute.codeBlock,
-    Attribute.blockQuote.key: Attribute.blockQuote,
-    Attribute.indent.key: Attribute.indent,
-    Attribute.width.key: Attribute.width,
-    Attribute.height.key: Attribute.height,
-    Attribute.style.key: Attribute.style,
-    Attribute.token.key: Attribute.token,
-  });
-
-  static final BoldAttribute bold = BoldAttribute();
-
-  static final ItalicAttribute italic = ItalicAttribute();
-
-  static final SmallAttribute small = SmallAttribute();
-
-  static final UnderlineAttribute underline = UnderlineAttribute();
-
-  static final StrikeThroughAttribute strikeThrough = StrikeThroughAttribute();
-
-  static final InlineCodeAttribute inlineCode = InlineCodeAttribute();
-
-  static final FontAttribute font = FontAttribute(null);
-
-  static final SizeAttribute size = SizeAttribute(null);
-
-  static final LinkAttribute link = LinkAttribute(null);
-
-  static final ColorAttribute color = ColorAttribute(null);
-
-  static final BackgroundAttribute background = BackgroundAttribute(null);
-
-  static final PlaceholderAttribute placeholder = PlaceholderAttribute();
-
-  static final HeaderAttribute header = HeaderAttribute();
-
-  static final IndentAttribute indent = IndentAttribute();
-
-  static final AlignAttribute align = AlignAttribute(null);
-
-  static final ListAttribute list = ListAttribute(null);
-
-  static final CodeBlockAttribute codeBlock = CodeBlockAttribute();
-
-  static final BlockQuoteAttribute blockQuote = BlockQuoteAttribute();
-
-  static final WidthAttribute width = WidthAttribute(null);
-
-  static final HeightAttribute height = HeightAttribute(null);
-
-  static final StyleAttribute style = StyleAttribute(null);
-
-  static final TokenAttribute token = TokenAttribute('');
-
-  static final Set<String> inlineKeys = {
-    Attribute.bold.key,
-    Attribute.italic.key,
-    Attribute.small.key,
-    Attribute.underline.key,
-    Attribute.strikeThrough.key,
-    Attribute.link.key,
-    Attribute.color.key,
-    Attribute.background.key,
-    Attribute.placeholder.key,
-  };
-
-  static final Set<String> blockKeys = LinkedHashSet.of({
-    Attribute.header.key,
-    Attribute.align.key,
-    Attribute.list.key,
-    Attribute.codeBlock.key,
-    Attribute.blockQuote.key,
-    Attribute.indent.key,
-  });
-
-  static final Set<String> blockKeysExceptHeader = LinkedHashSet.of({
-    Attribute.list.key,
-    Attribute.align.key,
-    Attribute.codeBlock.key,
-    Attribute.blockQuote.key,
-    Attribute.indent.key,
-  });
-
-  static final Set<String> exclusiveBlockKeys = LinkedHashSet.of({
-    Attribute.header.key,
-    Attribute.list.key,
-    Attribute.codeBlock.key,
-    Attribute.blockQuote.key,
-  });
-
-  static Attribute<int?> get h1 => HeaderAttribute(level: 1);
-
-  static Attribute<int?> get h2 => HeaderAttribute(level: 2);
-
-  static Attribute<int?> get h3 => HeaderAttribute(level: 3);
-
-  // "attributes":{"align":"left"}
-  static Attribute<String?> get leftAlignment => AlignAttribute('left');
-
-  // "attributes":{"align":"center"}
-  static Attribute<String?> get centerAlignment => AlignAttribute('center');
-
-  // "attributes":{"align":"right"}
-  static Attribute<String?> get rightAlignment => AlignAttribute('right');
-
-  // "attributes":{"align":"justify"}
-  static Attribute<String?> get justifyAlignment => AlignAttribute('justify');
-
-  // "attributes":{"list":"bullet"}
-  static Attribute<String?> get ul => ListAttribute('bullet');
-
-  // "attributes":{"list":"ordered"}
-  static Attribute<String?> get ol => ListAttribute('ordered');
-
-  // "attributes":{"list":"checked"}
-  static Attribute<String?> get checked => ListAttribute('checked');
-
-  // "attributes":{"list":"unchecked"}
-  static Attribute<String?> get unchecked => ListAttribute('unchecked');
-
-  // "attributes":{"indent":1"}
-  static Attribute<int?> get indentL1 => IndentAttribute(level: 1);
-
-  // "attributes":{"indent":2"}
-  static Attribute<int?> get indentL2 => IndentAttribute(level: 2);
-
-  // "attributes":{"indent":3"}
-  static Attribute<int?> get indentL3 => IndentAttribute(level: 3);
-
-  static Attribute<int?> getIndentLevel(int? level) {
-    if (level == 1) {
-      return indentL1;
-    }
-    if (level == 2) {
-      return indentL2;
-    }
-    if (level == 3) {
-      return indentL3;
-    }
-    return IndentAttribute(level: level);
-  }
-
-  bool get isInline => scope == AttributeScope.INLINE;
-
-  bool get isBlockExceptHeader => blockKeysExceptHeader.contains(key);
-
-  Map<String, dynamic> toJson() => <String, dynamic>{key: value};
-
-  static Attribute? fromKeyValue(String key, dynamic value) {
-    final origin = _registry[key];
-    if (origin == null) {
-      return null;
-    }
-    final attribute = clone(origin, value);
-    return attribute;
-  }
-
-  static int getRegistryOrder(Attribute attribute) {
-    var order = 0;
-    for (final attr in _registry.values) {
-      if (attr.key == attribute.key) {
-        break;
-      }
-      order++;
-    }
-
-    return order;
-  }
-
-  static Attribute clone(Attribute origin, dynamic value) {
-    return Attribute(origin.key, origin.scope, value);
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-    if (other is! Attribute) return false;
-    final typedOther = other;
-    return key == typedOther.key && scope == typedOther.scope && value == typedOther.value;
-  }
-
-  @override
-  int get hashCode => hash3(key, scope, value);
-
-  @override
-  String toString() {
-    return 'Attribute{key: $key, scope: $scope, value: $value}';
-  }
-}
-
-class BoldAttribute extends Attribute<bool> {
-  BoldAttribute() : super('bold', AttributeScope.INLINE, true);
-}
-
-class ItalicAttribute extends Attribute<bool> {
-  ItalicAttribute() : super('italic', AttributeScope.INLINE, true);
-}
-
-class SmallAttribute extends Attribute<bool> {
-  SmallAttribute() : super('small', AttributeScope.INLINE, true);
-}
-
-class UnderlineAttribute extends Attribute<bool> {
-  UnderlineAttribute() : super('underline', AttributeScope.INLINE, true);
-}
-
-class StrikeThroughAttribute extends Attribute<bool> {
-  StrikeThroughAttribute() : super('strike', AttributeScope.INLINE, true);
-}
-
-class InlineCodeAttribute extends Attribute<bool> {
-  InlineCodeAttribute() : super('code', AttributeScope.INLINE, true);
-}
-
-class FontAttribute extends Attribute<String?> {
-  FontAttribute(String? val) : super('font', AttributeScope.INLINE, val);
-}
-
-class SizeAttribute extends Attribute<String?> {
-  SizeAttribute(String? val) : super('size', AttributeScope.INLINE, val);
-}
-
-class LinkAttribute extends Attribute<String?> {
-  LinkAttribute(String? val) : super('link', AttributeScope.INLINE, val);
-}
-
-class ColorAttribute extends Attribute<String?> {
-  ColorAttribute(String? val) : super('color', AttributeScope.INLINE, val);
-}
-
-class BackgroundAttribute extends Attribute<String?> {
-  BackgroundAttribute(String? val) : super('background', AttributeScope.INLINE, val);
-}
-
-/// This is custom attribute for hint
-class PlaceholderAttribute extends Attribute<bool> {
-  PlaceholderAttribute() : super('placeholder', AttributeScope.INLINE, true);
-}
-
-class HeaderAttribute extends Attribute<int?> {
-  HeaderAttribute({int? level}) : super('header', AttributeScope.BLOCK, level);
-}
-
-class IndentAttribute extends Attribute<int?> {
-  IndentAttribute({int? level}) : super('indent', AttributeScope.BLOCK, level);
-}
-
-class AlignAttribute extends Attribute<String?> {
-  AlignAttribute(String? val) : super('align', AttributeScope.BLOCK, val);
-}
-
-class ListAttribute extends Attribute<String?> {
-  ListAttribute(String? val) : super('list', AttributeScope.BLOCK, val);
-}
-
-class CodeBlockAttribute extends Attribute<bool> {
-  CodeBlockAttribute() : super('code-block', AttributeScope.BLOCK, true);
-}
-
-class BlockQuoteAttribute extends Attribute<bool> {
-  BlockQuoteAttribute() : super('blockquote', AttributeScope.BLOCK, true);
-}
-
-class WidthAttribute extends Attribute<String?> {
-  WidthAttribute(String? val) : super('width', AttributeScope.IGNORE, val);
-}
-
-class HeightAttribute extends Attribute<String?> {
-  HeightAttribute(String? val) : super('height', AttributeScope.IGNORE, val);
-}
-
-class StyleAttribute extends Attribute<String?> {
-  StyleAttribute(String? val) : super('style', AttributeScope.IGNORE, val);
-}
-
-class TokenAttribute extends Attribute<String> {
-  TokenAttribute(String val) : super('token', AttributeScope.IGNORE, val);
-}

+ 0 - 291
app_flowy/packages/editor/lib/src/models/documents/document.dart

@@ -1,291 +0,0 @@
-import 'dart:async';
-
-import 'package:tuple/tuple.dart';
-
-import '../quill_delta.dart';
-import '../rules/rule.dart';
-import 'attribute.dart';
-import 'history.dart';
-import 'nodes/block.dart';
-import 'nodes/container.dart';
-import 'nodes/embed.dart';
-import 'nodes/line.dart';
-import 'nodes/node.dart';
-import 'style.dart';
-
-/// The rich text document
-class Document {
-  Document() : _delta = Delta()..insert('\n') {
-    _loadDocument(_delta);
-  }
-
-  Document.fromJson(List data) : _delta = _transform(Delta.fromJson(data)) {
-    _loadDocument(_delta);
-  }
-
-  Document.fromDelta(Delta delta) : _delta = delta {
-    _loadDocument(delta);
-  }
-
-  /// The root node of the document tree
-  final Root _root = Root();
-
-  Root get root => _root;
-
-  int get length => _root.length;
-
-  Delta _delta;
-
-  Delta toDelta() => Delta.from(_delta);
-
-  final Rules _rules = Rules.getInstance();
-
-  void setCustomRules(List<Rule> customRules) {
-    _rules.setCustomRules(customRules);
-  }
-
-  final StreamController<Tuple3<Delta, Delta, ChangeSource>> _observer =
-      StreamController.broadcast();
-
-  final History _history = History();
-
-  Stream<Tuple3<Delta, Delta, ChangeSource>> get changes => _observer.stream;
-
-  Delta insert(int index, Object? data, {int replaceLength = 0}) {
-    assert(index >= 0);
-    assert(data is String || data is Embeddable);
-    if (data is Embeddable) {
-      data = data.toJson();
-    } else if ((data as String).isEmpty) {
-      return Delta();
-    }
-
-    final delta = _rules.apply(RuleType.INSERT, this, index,
-        data: data, len: replaceLength);
-    compose(delta, ChangeSource.LOCAL);
-    return delta;
-  }
-
-  Delta delete(int index, int len) {
-    assert(index >= 0 && len > 0);
-    final delta = _rules.apply(RuleType.DELETE, this, index, len: len);
-    if (delta.isNotEmpty) {
-      compose(delta, ChangeSource.LOCAL);
-    }
-    return delta;
-  }
-
-  Delta replace(int index, int len, Object? data) {
-    assert(index >= 0);
-    assert(data is String || data is Embeddable);
-
-    final dataIsNotEmpty = (data is String) ? data.isNotEmpty : true;
-
-    assert(dataIsNotEmpty || len > 0);
-
-    var delta = Delta();
-
-    // We have to insert before applying delete rules
-    // Otherwise delete would be operating on stale document snapshot.
-    if (dataIsNotEmpty) {
-      delta = insert(index, data, replaceLength: len);
-    }
-
-    if (len > 0) {
-      final deleteDelta = delete(index, len);
-      delta = delta.compose(deleteDelta);
-    }
-
-    return delta;
-  }
-
-  Delta format(int index, int len, Attribute? attribute) {
-    assert(index >= 0 && len >= 0 && attribute != null);
-
-    var delta = Delta();
-
-    final formatDelta = _rules.apply(RuleType.FORMAT, this, index,
-        len: len, attribute: attribute);
-    if (formatDelta.isNotEmpty) {
-      compose(formatDelta, ChangeSource.LOCAL);
-      delta = delta.compose(formatDelta);
-    }
-
-    return delta;
-  }
-
-  /// Only attributes applied to all characters within this range are
-  /// included in the result.
-  Style collectStyle(int index, int len) {
-    final res = queryChild(index);
-    return (res.node as Line).collectStyle(res.offset, len);
-  }
-
-  /// Returns all styles for any character within the specified text range.
-  List<Style> collectAllStyles(int index, int len) {
-    final res = queryChild(index);
-    return (res.node as Line).collectAllStyles(res.offset, len);
-  }
-
-  ChildQuery queryChild(int offset) {
-    final res = _root.queryChild(offset, true);
-    if (res.node is Line) {
-      return res;
-    }
-    final block = res.node as Block;
-    return block.queryChild(res.offset, true);
-  }
-
-  void compose(Delta delta, ChangeSource changeSource) {
-    assert(!_observer.isClosed);
-    delta.trim();
-    assert(delta.isNotEmpty);
-
-    var offset = 0;
-    delta = _transform(delta);
-    final originalDelta = toDelta();
-    for (final op in delta.toList()) {
-      final style =
-          op.attributes != null ? Style.fromJson(op.attributes) : null;
-
-      if (op.isInsert) {
-        _root.insert(offset, _normalize(op.data), style);
-      } else if (op.isDelete) {
-        _root.delete(offset, op.length);
-      } else if (op.attributes != null) {
-        _root.retain(offset, op.length, style);
-      }
-
-      if (!op.isDelete) {
-        offset += op.length!;
-      }
-    }
-    try {
-      _delta = _delta.compose(delta);
-    } catch (e) {
-      throw '_delta compose failed';
-    }
-
-    if (_delta != _root.toDelta()) {
-      throw 'Compose failed';
-    }
-    final change = Tuple3(originalDelta, delta, changeSource);
-    _observer.add(change);
-    _history.handleDocChange(change);
-  }
-
-  Tuple2 undo() {
-    return _history.undo(this);
-  }
-
-  Tuple2 redo() {
-    return _history.redo(this);
-  }
-
-  bool get hasUndo => _history.hasUndo;
-
-  bool get hasRedo => _history.hasRedo;
-
-  static Delta _transform(Delta delta) {
-    final res = Delta();
-    final ops = delta.toList();
-    for (var i = 0; i < ops.length; i++) {
-      final op = ops[i];
-      res.push(op);
-      _autoAppendNewlineAfterEmbeddable(i, ops, op, res, 'video');
-    }
-    return res;
-  }
-
-  static void _autoAppendNewlineAfterEmbeddable(
-      int i, List<Operation> ops, Operation op, Delta res, String type) {
-    final nextOpIsEmbed = i + 1 < ops.length &&
-        ops[i + 1].isInsert &&
-        ops[i + 1].data is Map &&
-        (ops[i + 1].data as Map).containsKey(type);
-    if (nextOpIsEmbed &&
-        op.data is String &&
-        (op.data as String).isNotEmpty &&
-        !(op.data as String).endsWith('\n')) {
-      res.push(Operation.insert('\n'));
-    }
-    // embed could be image or video
-    final opInsertEmbed =
-        op.isInsert && op.data is Map && (op.data as Map).containsKey(type);
-    final nextOpIsLineBreak = i + 1 < ops.length &&
-        ops[i + 1].isInsert &&
-        ops[i + 1].data is String &&
-        (ops[i + 1].data as String).startsWith('\n');
-    if (opInsertEmbed && (i + 1 == ops.length - 1 || !nextOpIsLineBreak)) {
-      // automatically append '\n' for embeddable
-      res.push(Operation.insert('\n'));
-    }
-  }
-
-  Object _normalize(Object? data) {
-    if (data is String) {
-      return data;
-    }
-
-    if (data is Embeddable) {
-      return data;
-    }
-    return Embeddable.fromJson(data as Map<String, dynamic>);
-  }
-
-  void close() {
-    _observer.close();
-    _history.clear();
-  }
-
-  String toPlainText() => _root.children.map((e) => e.toPlainText()).join();
-
-  void _loadDocument(Delta doc) {
-    if (doc.isEmpty) {
-      throw ArgumentError.value(doc, 'Document Delta cannot be empty.');
-    }
-
-    assert((doc.last.data as String).endsWith('\n'));
-
-    var offset = 0;
-    for (final op in doc.toList()) {
-      if (!op.isInsert) {
-        throw ArgumentError.value(doc,
-            'Document can only contain insert operations but ${op.key} found.');
-      }
-      final style =
-          op.attributes != null ? Style.fromJson(op.attributes) : null;
-      final data = _normalize(op.data);
-      _root.insert(offset, data, style);
-      offset += op.length!;
-    }
-    final node = _root.last;
-    if (node is Line &&
-        node.parent is! Block &&
-        node.style.isEmpty &&
-        _root.childCount > 1) {
-      _root.remove(node);
-    }
-  }
-
-  bool isEmpty() {
-    if (root.children.length != 1) {
-      return false;
-    }
-
-    final node = root.children.first;
-    if (!node.isLast) {
-      return false;
-    }
-
-    final delta = node.toDelta();
-    return delta.length == 1 &&
-        delta.first.data == '\n' &&
-        delta.first.key == 'insert';
-  }
-}
-
-enum ChangeSource {
-  LOCAL,
-  REMOTE,
-}

+ 0 - 134
app_flowy/packages/editor/lib/src/models/documents/history.dart

@@ -1,134 +0,0 @@
-import 'package:tuple/tuple.dart';
-
-import '../quill_delta.dart';
-import 'document.dart';
-
-class History {
-  History({
-    this.ignoreChange = false,
-    this.interval = 400,
-    this.maxStack = 100,
-    this.userOnly = false,
-    this.lastRecorded = 0,
-  });
-
-  final HistoryStack stack = HistoryStack.empty();
-
-  bool get hasUndo => stack.undo.isNotEmpty;
-
-  bool get hasRedo => stack.redo.isNotEmpty;
-
-  /// used for disable redo or undo function
-  bool ignoreChange;
-
-  int lastRecorded;
-
-  /// Collaborative editing's conditions should be true
-  final bool userOnly;
-
-  ///max operation count for undo
-  final int maxStack;
-
-  ///record delay
-  final int interval;
-
-  void handleDocChange(Tuple3<Delta, Delta, ChangeSource> change) {
-    if (ignoreChange) return;
-    if (!userOnly || change.item3 == ChangeSource.LOCAL) {
-      record(change.item2, change.item1);
-    } else {
-      transform(change.item2);
-    }
-  }
-
-  void clear() {
-    stack.clear();
-  }
-
-  void record(Delta change, Delta before) {
-    if (change.isEmpty) return;
-    stack.redo.clear();
-    var undoDelta = change.invert(before);
-    final timeStamp = DateTime.now().millisecondsSinceEpoch;
-
-    if (lastRecorded + interval > timeStamp && stack.undo.isNotEmpty) {
-      final lastDelta = stack.undo.removeLast();
-      undoDelta = undoDelta.compose(lastDelta);
-    } else {
-      lastRecorded = timeStamp;
-    }
-
-    if (undoDelta.isEmpty) return;
-    stack.undo.add(undoDelta);
-
-    if (stack.undo.length > maxStack) {
-      stack.undo.removeAt(0);
-    }
-  }
-
-  ///
-  ///It will override pre local undo delta,replaced by remote change
-  ///
-  void transform(Delta delta) {
-    transformStack(stack.undo, delta);
-    transformStack(stack.redo, delta);
-  }
-
-  void transformStack(List<Delta> stack, Delta delta) {
-    for (var i = stack.length - 1; i >= 0; i -= 1) {
-      final oldDelta = stack[i];
-      stack[i] = delta.transform(oldDelta, true);
-      delta = oldDelta.transform(delta, false);
-      if (stack[i].length == 0) {
-        stack.removeAt(i);
-      }
-    }
-  }
-
-  Tuple2 _change(Document doc, List<Delta> source, List<Delta> dest) {
-    if (source.isEmpty) {
-      return const Tuple2(false, 0);
-    }
-    final delta = source.removeLast();
-    // look for insert or delete
-    int? len = 0;
-    final ops = delta.toList();
-    for (var i = 0; i < ops.length; i++) {
-      if (ops[i].key == Operation.insertKey) {
-        len = ops[i].length;
-      } else if (ops[i].key == Operation.deleteKey) {
-        len = ops[i].length! * -1;
-      }
-    }
-    final base = Delta.from(doc.toDelta());
-    final inverseDelta = delta.invert(base);
-    dest.add(inverseDelta);
-    lastRecorded = 0;
-    ignoreChange = true;
-    doc.compose(delta, ChangeSource.LOCAL);
-    ignoreChange = false;
-    return Tuple2(true, len);
-  }
-
-  Tuple2 undo(Document doc) {
-    return _change(doc, stack.undo, stack.redo);
-  }
-
-  Tuple2 redo(Document doc) {
-    return _change(doc, stack.redo, stack.undo);
-  }
-}
-
-class HistoryStack {
-  HistoryStack.empty()
-      : undo = [],
-        redo = [];
-
-  final List<Delta> undo;
-  final List<Delta> redo;
-
-  void clear() {
-    undo.clear();
-    redo.clear();
-  }
-}

+ 0 - 72
app_flowy/packages/editor/lib/src/models/documents/nodes/block.dart

@@ -1,72 +0,0 @@
-import '../../quill_delta.dart';
-import 'container.dart';
-import 'line.dart';
-import 'node.dart';
-
-/// Represents a group of adjacent [Line]s with the same block style.
-///
-/// Block elements are:
-/// - Blockquote
-/// - Header
-/// - Indent
-/// - List
-/// - Text Alignment
-/// - Text Direction
-/// - Code Block
-class Block extends Container<Line?> {
-  /// Creates new unmounted [Block].
-  @override
-  Node newInstance() => Block();
-
-  @override
-  Line get defaultChild => Line();
-
-  @override
-  Delta toDelta() {
-    return children
-        .map((child) => child.toDelta())
-        .fold(Delta(), (a, b) => a.concat(b));
-  }
-
-  @override
-  void adjust() {
-    if (isEmpty) {
-      final sibling = previous;
-      unlink();
-      if (sibling != null) {
-        sibling.adjust();
-      }
-      return;
-    }
-
-    var block = this;
-    final prev = block.previous;
-    // merging it with previous block if style is the same
-    if (!block.isFirst &&
-        block.previous is Block &&
-        prev!.style == block.style) {
-      block
-        ..moveChildToNewParent(prev as Container<Node?>?)
-        ..unlink();
-      block = prev as Block;
-    }
-    final next = block.next;
-    // merging it with next block if style is the same
-    if (!block.isLast && block.next is Block && next!.style == block.style) {
-      (next as Block).moveChildToNewParent(block);
-      next.unlink();
-    }
-  }
-
-  @override
-  String toString() {
-    final block = style.attributes.toString();
-    final buffer = StringBuffer('§ {$block}\n');
-    for (final child in children) {
-      final tree = child.isLast ? '└' : '├';
-      buffer.write('  $tree $child');
-      if (!child.isLast) buffer.writeln();
-    }
-    return buffer.toString();
-  }
-}

+ 0 - 160
app_flowy/packages/editor/lib/src/models/documents/nodes/container.dart

@@ -1,160 +0,0 @@
-import 'dart:collection';
-
-import '../style.dart';
-import 'leaf.dart';
-import 'line.dart';
-import 'node.dart';
-
-/// Container can accommodate other nodes.
-///
-/// Delegates insert, retain and delete operations to children nodes. For each
-/// operation container looks for a child at specified index position and
-/// forwards operation to that child.
-///
-/// Most of the operation handling logic is implemented by [Line] and [Text].
-abstract class Container<T extends Node?> extends Node {
-  final LinkedList<Node> _children = LinkedList<Node>();
-
-  /// List of children.
-  LinkedList<Node> get children => _children;
-
-  /// Returns total number of child nodes in this container.
-  ///
-  /// To get text length of this container see [length].
-  int get childCount => _children.length;
-
-  /// Returns the first child [Node].
-  Node get first => _children.first;
-
-  /// Returns the last child [Node].
-  Node get last => _children.last;
-
-  /// Returns `true` if this container has no child nodes.
-  bool get isEmpty => _children.isEmpty;
-
-  /// Returns `true` if this container has at least 1 child.
-  bool get isNotEmpty => _children.isNotEmpty;
-
-  /// Returns an instance of default child for this container node.
-  ///
-  /// Always returns fresh instance.
-  T get defaultChild;
-
-  /// Adds [node] to the end of this container children list.
-  void add(T node) {
-    assert(node?.parent == null);
-    node?.parent = this;
-    _children.add(node as Node);
-  }
-
-  /// Adds [node] to the beginning of this container children list.
-  void addFirst(T node) {
-    assert(node?.parent == null);
-    node?.parent = this;
-    _children.addFirst(node as Node);
-  }
-
-  /// Removes [node] from this container.
-  void remove(T node) {
-    assert(node?.parent == this);
-    node?.parent = null;
-    _children.remove(node as Node);
-  }
-
-  /// Moves children of this node to [newParent].
-  void moveChildToNewParent(Container? newParent) {
-    if (isEmpty) {
-      return;
-    }
-
-    final last = newParent!.isEmpty ? null : newParent.last as T?;
-    while (isNotEmpty) {
-      final child = first as T;
-      child?.unlink();
-      newParent.add(child);
-    }
-
-    /// In case [newParent] already had children we need to make sure
-    /// combined list is optimized.
-    if (last != null) last.adjust();
-  }
-
-  /// Queries the child [Node] at [offset] in this container.
-  ///
-  /// The result may contain the found node or `null` if no node is found
-  /// at specified offset.
-  ///
-  /// [ChildQuery.offset] is set to relative offset within returned child node
-  /// which points at the same character position in the document as the
-  /// original [offset].
-  ChildQuery queryChild(int offset, bool inclusive) {
-    if (offset < 0 || offset > length) {
-      return ChildQuery(null, 0);
-    }
-
-    for (final node in children) {
-      final len = node.length;
-      if (offset < len || (inclusive && offset == len && node.isLast)) {
-        return ChildQuery(node, offset);
-      }
-      offset -= len;
-    }
-    return ChildQuery(null, 0);
-  }
-
-  @override
-  String toPlainText() => children.map((child) => child.toPlainText()).join();
-
-  /// Content length of this node's children.
-  ///
-  /// To get number of children in this node use [childCount].
-  @override
-  int get length => _children.fold(0, (cur, node) => cur + node.length);
-
-  @override
-  void insert(int index, Object data, Style? style) {
-    assert(index == 0 || (index > 0 && index < length));
-
-    if (isNotEmpty) {
-      final child = queryChild(index, false);
-      child.node!.insert(child.offset, data, style);
-      return;
-    }
-
-    // empty
-    assert(index == 0);
-    final node = defaultChild;
-    add(node);
-    node?.insert(index, data, style);
-  }
-
-  @override
-  void retain(int index, int? length, Style? attributes) {
-    assert(isNotEmpty);
-    final child = queryChild(index, false);
-    child.node!.retain(child.offset, length, attributes);
-  }
-
-  @override
-  void delete(int index, int? length) {
-    assert(isNotEmpty);
-    final child = queryChild(index, false);
-    child.node!.delete(child.offset, length);
-  }
-
-  @override
-  String toString() => _children.join('\n');
-}
-
-/// Result of a child query in a [Container].
-class ChildQuery {
-  ChildQuery(this.node, this.offset);
-
-  /// The child node if found, otherwise `null`.
-  final Node? node;
-
-  /// Starting offset within the child [node] which points at the same
-  /// character in the document as the original offset passed to
-  /// [Container.queryChild] method.
-  final int offset;
-}

+ 0 - 45
app_flowy/packages/editor/lib/src/models/documents/nodes/embed.dart

@@ -1,45 +0,0 @@
-/// An object which can be embedded into a Quill document.
-///
-/// See also:
-///
-/// * [BlockEmbed] which represents a block embed.
-class Embeddable {
-  const Embeddable(this.type, this.data);
-
-  /// The type of this object.
-  final String type;
-
-  /// The data payload of this object.
-  final dynamic data;
-
-  Map<String, dynamic> toJson() {
-    final m = <String, String>{type: data};
-    return m;
-  }
-
-  static Embeddable fromJson(Map<String, dynamic> json) {
-    final m = Map<String, dynamic>.from(json);
-    assert(m.length == 1, 'Embeddable map has one key');
-
-    return BlockEmbed(m.keys.first, m.values.first);
-  }
-}
-
-/// An object which occupies an entire line in a document and cannot co-exist
-/// inline with regular text.
-///
-/// There are two built-in embed types supported by Quill documents, however
-/// the document model itself does not make any assumptions about the types
-/// of embedded objects and allows users to define their own types.
-class BlockEmbed extends Embeddable {
-  const BlockEmbed(String type, String data) : super(type, data);
-
-  static const String horizontalRuleType = 'divider';
-  static BlockEmbed horizontalRule = const BlockEmbed(horizontalRuleType, 'hr');
-
-  static const String imageType = 'image';
-  static BlockEmbed image(String imageUrl) => BlockEmbed(imageType, imageUrl);
-
-  static const String videoType = 'video';
-  static BlockEmbed video(String videoUrl) => BlockEmbed(videoType, videoUrl);
-}

+ 0 - 252
app_flowy/packages/editor/lib/src/models/documents/nodes/leaf.dart

@@ -1,252 +0,0 @@
-import 'dart:math' as math;
-
-import '../../quill_delta.dart';
-import '../style.dart';
-import 'embed.dart';
-import 'line.dart';
-import 'node.dart';
-
-/// A leaf in Quill document tree.
-abstract class Leaf extends Node {
-  /// Creates a new [Leaf] with specified [data].
-  factory Leaf(Object data) {
-    if (data is Embeddable) {
-      return Embed(data);
-    }
-    final text = data as String;
-    assert(text.isNotEmpty);
-    return Text(text);
-  }
-
-  Leaf.val(Object val) : _value = val;
-
-  /// Contents of this node, either a String if this is a [Text] or an
-  /// [Embed] if this is an [BlockEmbed].
-  Object get value => _value;
-  Object _value;
-
-  @override
-  void applyStyle(Style value) {
-    assert(value.isInline || value.isIgnored || value.isEmpty,
-        'Unable to apply Style to leaf: $value');
-    super.applyStyle(value);
-  }
-
-  @override
-  Line? get parent => super.parent as Line?;
-
-  @override
-  int get length {
-    if (_value is String) {
-      return (_value as String).length;
-    }
-    // return 1 for embedded object
-    return 1;
-  }
-
-  @override
-  Delta toDelta() {
-    final data =
-        _value is Embeddable ? (_value as Embeddable).toJson() : _value;
-    return Delta()..insert(data, style.toJson());
-  }
-
-  @override
-  void insert(int index, Object data, Style? style) {
-    assert(index >= 0 && index <= length);
-    final node = Leaf(data);
-    if (index < length) {
-      splitAt(index)!.insertBefore(node);
-    } else {
-      insertAfter(node);
-    }
-    node.format(style);
-  }
-
-  @override
-  void retain(int index, int? len, Style? style) {
-    if (style == null) {
-      return;
-    }
-
-    final local = math.min(length - index, len!);
-    final remain = len - local;
-    final node = _isolate(index, local);
-
-    if (remain > 0) {
-      assert(node.next != null);
-      node.next!.retain(0, remain, style);
-    }
-    node.format(style);
-  }
-
-  @override
-  void delete(int index, int? len) {
-    assert(index < length);
-
-    final local = math.min(length - index, len!);
-    final target = _isolate(index, local);
-    final prev = target.previous as Leaf?;
-    final next = target.next as Leaf?;
-    target.unlink();
-
-    final remain = len - local;
-    if (remain > 0) {
-      assert(next != null);
-      next!.delete(0, remain);
-    }
-
-    if (prev != null) {
-      prev.adjust();
-    }
-  }
-
-  /// Adjust this text node by merging it with adjacent nodes if they share
-  /// the same style.
-  @override
-  void adjust() {
-    if (this is Embed) {
-      // Embed nodes cannot be merged with text nor other embeds (in fact,
-      // there could be no two adjacent embeds on the same line since an
-      // embed occupies an entire line).
-      return;
-    }
-
-    // This is a text node and it can only be merged with other text nodes.
-    var node = this as Text;
-
-    // Merging it with previous node if style is the same.
-    final prev = node.previous;
-    if (!node.isFirst && prev is Text && prev.style == node.style) {
-      prev._value = prev.value + node.value;
-      node.unlink();
-      node = prev;
-    }
-
-    // Merging it with next node if style is the same.
-    final next = node.next;
-    if (!node.isLast && next is Text && next.style == node.style) {
-      node._value = node.value + next.value;
-      next.unlink();
-    }
-  }
-
-  /// Splits this leaf node at [index] and returns new node.
-  ///
-  /// If this is the last node in its list and [index] equals this node's
-  /// length then this method returns `null` as there is nothing left to split.
-  /// If there is another leaf node after this one and [index] equals this
-  /// node's length then the next leaf node is returned.
-  ///
-  /// If [index] equals to `0` then this node itself is returned unchanged.
-  ///
-  /// In case a new node is actually split from this one, it inherits this
-  /// node's style.
-  Leaf? splitAt(int index) {
-    assert(index >= 0 && index <= length);
-    if (index == 0) {
-      return this;
-    }
-    if (index == length) {
-      return isLast ? null : next as Leaf?;
-    }
-
-    assert(this is Text);
-    final text = _value as String;
-    _value = text.substring(0, index);
-    final split = Leaf(text.substring(index))..applyStyle(style);
-    insertAfter(split);
-    return split;
-  }
-
-  /// Cuts a leaf from [index] to the end of this node and returns new node
-  /// in detached state (e.g. [mounted] returns `false`).
-  ///
-  /// Splitting logic is identical to one described in [splitAt], meaning this
-  /// method may return `null`.
-  Leaf? cutAt(int index) {
-    assert(index >= 0 && index <= length);
-    final cut = splitAt(index);
-    cut?.unlink();
-    return cut;
-  }
-
-  /// Formats this node and optimizes it with adjacent leaf nodes if needed.
-  void format(Style? style) {
-    if (style != null && style.isNotEmpty) {
-      applyStyle(style);
-    }
-    adjust();
-  }
-
-  /// Isolates a new leaf starting at [index] with specified [length].
-  ///
-  /// Splitting logic is identical to one described in [splitAt], with one
-  /// exception that it is required for [index] to always be less than this
-  /// node's length. As a result this method always returns a [LeafNode]
-  /// instance. Returned node may still be the same as this node
-  /// if provided [index] is `0`.
-  Leaf _isolate(int index, int length) {
-    assert(
-        index >= 0 && index < this.length && (index + length <= this.length));
-    final target = splitAt(index)!..splitAt(length);
-    return target;
-  }
-}
-
-/// A span of formatted text within a line in a Quill document.
-///
-/// Text is a leaf node of a document tree.
-///
-/// Parent of a text node is always a [Line], and as a consequence text
-/// node's [value] cannot contain any line-break characters.
-///
-/// See also:
-///
-///   * [Embed], a leaf node representing an embeddable object.
-///   * [Line], a node representing a line of text.
-class Text extends Leaf {
-  Text([String text = ''])
-      : assert(!text.contains('\n')),
-        super.val(text);
-
-  @override
-  Node newInstance() => Text(value);
-
-  @override
-  String get value => _value as String;
-
-  @override
-  String toPlainText() => value;
-}
-
-/// An embed node inside of a line in a Quill document.
-///
-/// Embed node is a leaf node similar to [Text]. It represents an arbitrary
-/// piece of non-textual content embedded into a document, such as, image,
-/// horizontal rule, video, or any other object with defined structure,
-/// like a tweet, for instance.
-///
-/// Embed node's length is always `1` character and it is represented with
-/// unicode object replacement character in the document text.
-///
-/// Any inline style can be applied to an embed, however this does not
-/// necessarily mean the embed will look according to that style. For instance,
-/// applying "bold" style to an image gives no effect, while adding a "link" to
-/// an image actually makes the image react to user's action.
-class Embed extends Leaf {
-  Embed(Embeddable data) : super.val(data);
-
-  static const kObjectReplacementCharacter = '\uFFFC';
-
-  @override
-  Node newInstance() => throw UnimplementedError();
-
-  @override
-  Embeddable get value => super.value as Embeddable;
-
-  /// // Embed nodes are represented as unicode object replacement character in
-  // plain text.
-  @override
-  String toPlainText() => kObjectReplacementCharacter;
-}

+ 0 - 414
app_flowy/packages/editor/lib/src/models/documents/nodes/line.dart

@@ -1,414 +0,0 @@
-import 'dart:math' as math;
-
-import 'package:collection/collection.dart';
-
-import '../../quill_delta.dart';
-import '../attribute.dart';
-import '../style.dart';
-import 'block.dart';
-import 'container.dart';
-import 'embed.dart';
-import 'leaf.dart';
-import 'node.dart';
-
-/// A line of rich text in a Quill document.
-///
-/// Line serves as a container for [Leaf]s, like [Text] and [Embed].
-///
-/// When a line contains an embed, it fully occupies the line, no other embeds
-/// or text nodes are allowed.
-class Line extends Container<Leaf?> {
-  @override
-  Leaf get defaultChild => Text();
-
-  @override
-  int get length => super.length + 1;
-
-  /// Returns `true` if this line contains an embedded object.
-  bool get hasEmbed {
-    return children.any((child) => child is Embed);
-  }
-
-  /// Returns next [Line] or `null` if this is the last line in the document.
-  Line? get nextLine {
-    if (!isLast) {
-      return next is Block ? (next as Block).first as Line? : next as Line?;
-    }
-    if (parent is! Block) {
-      return null;
-    }
-
-    if (parent!.isLast) {
-      return null;
-    }
-    return parent!.next is Block
-        ? (parent!.next as Block).first as Line?
-        : parent!.next as Line?;
-  }
-
-  @override
-  Node newInstance() => Line();
-
-  @override
-  Delta toDelta() {
-    final delta = children
-        .map((child) => child.toDelta())
-        .fold(Delta(), (dynamic a, b) => a.concat(b));
-    var attributes = style;
-    if (parent is Block) {
-      final block = parent as Block;
-      attributes = attributes.mergeAll(block.style);
-    }
-    delta.insert('\n', attributes.toJson());
-    return delta;
-  }
-
-  @override
-  String toPlainText() => '${super.toPlainText()}\n';
-
-  @override
-  String toString() {
-    final body = children.join(' → ');
-    final styleString = style.isNotEmpty ? ' $style' : '';
-    return '¶ $body ⏎$styleString';
-  }
-
-  @override
-  void insert(int index, Object data, Style? style) {
-    if (data is Embeddable) {
-      // We do not check whether this line already has any children here as
-      // inserting an embed into a line with other text is acceptable from the
-      // Delta format perspective.
-      // We rely on heuristic rules to ensure that embeds occupy an entire line.
-      _insertSafe(index, data, style);
-      return;
-    }
-
-    final text = data as String;
-    final lineBreak = text.indexOf('\n');
-    if (lineBreak < 0) {
-      _insertSafe(index, text, style);
-      // No need to update line or block format since those attributes can only
-      // be attached to `\n` character and we already know it's not present.
-      return;
-    }
-
-    final prefix = text.substring(0, lineBreak);
-    _insertSafe(index, prefix, style);
-    if (prefix.isNotEmpty) {
-      index += prefix.length;
-    }
-
-    // Next line inherits our format.
-    final nextLine = _getNextLine(index);
-
-    // Reset our format and unwrap from a block if needed.
-    clearStyle();
-    if (parent is Block) {
-      _unwrap();
-    }
-
-    // Now we can apply new format and re-layout.
-    _format(style);
-
-    // Continue with remaining part.
-    final remain = text.substring(lineBreak + 1);
-    nextLine.insert(0, remain, style);
-  }
-
-  @override
-  void retain(int index, int? len, Style? style) {
-    if (style == null) {
-      return;
-    }
-    final thisLength = length;
-
-    final local = math.min(thisLength - index, len!);
-    // If index is at newline character then this is a line/block style update.
-    final isLineFormat = (index + local == thisLength) && local == 1;
-
-    if (isLineFormat) {
-      assert(style.values.every((attr) => attr.scope == AttributeScope.BLOCK),
-          'It is not allowed to apply inline attributes to line itself.');
-      _format(style);
-    } else {
-      // Otherwise forward to children as it's an inline format update.
-      assert(style.values.every((attr) => attr.scope == AttributeScope.INLINE));
-      assert(index + local != thisLength);
-      super.retain(index, local, style);
-    }
-
-    final remain = len - local;
-    if (remain > 0) {
-      assert(nextLine != null);
-      nextLine!.retain(0, remain, style);
-    }
-  }
-
-  @override
-  void delete(int index, int? len) {
-    final local = math.min(length - index, len!);
-    final isLFDeleted = index + local == length; // Line feed
-    if (isLFDeleted) {
-      // Our newline character deleted with all style information.
-      clearStyle();
-      if (local > 1) {
-        // Exclude newline character from delete range for children.
-        super.delete(index, local - 1);
-      }
-    } else {
-      super.delete(index, local);
-    }
-
-    final remaining = len - local;
-    if (remaining > 0) {
-      assert(nextLine != null);
-      nextLine!.delete(0, remaining);
-    }
-
-    if (isLFDeleted && isNotEmpty) {
-      // Since we lost our line-break and still have child text nodes those must
-      // migrate to the next line.
-
-      // nextLine might have been unmounted since last assert so we need to
-      // check again we still have a line after us.
-      assert(nextLine != null);
-
-      // Move remaining children in this line to the next line so that all
-      // attributes of nextLine are preserved.
-      nextLine!.moveChildToNewParent(this);
-      moveChildToNewParent(nextLine);
-    }
-
-    if (isLFDeleted) {
-      // Now we can remove this line.
-      final block = parent!; // remember reference before un-linking.
-      unlink();
-      block.adjust();
-    }
-  }
-
-  /// Formats this line.
-  void _format(Style? newStyle) {
-    if (newStyle == null || newStyle.isEmpty) {
-      return;
-    }
-
-    applyStyle(newStyle);
-    final blockStyle = newStyle.getBlockExceptHeader();
-    if (blockStyle == null) {
-      return;
-    } // No block-level changes
-
-    if (parent is Block) {
-      final parentStyle = (parent as Block).style.getBlocksExceptHeader();
-      // Ensure that we're only unwrapping the block only if we unset a single
-      // block format in the `parentStyle` and there are no more block formats
-      // left to unset.
-      if (blockStyle.value == null &&
-          parentStyle.containsKey(blockStyle.key) &&
-          parentStyle.length == 1) {
-        _unwrap();
-      } else if (!const MapEquality()
-          .equals(newStyle.getBlocksExceptHeader(), parentStyle)) {
-        _unwrap();
-        // Block style now can contain multiple attributes
-        if (newStyle.attributes.keys
-            .any(Attribute.exclusiveBlockKeys.contains)) {
-          parentStyle.removeWhere(
-              (key, attr) => Attribute.exclusiveBlockKeys.contains(key));
-        }
-        parentStyle.removeWhere(
-            (key, attr) => newStyle?.attributes.keys.contains(key) ?? false);
-        final parentStyleToMerge = Style.attr(parentStyle);
-        newStyle = newStyle.mergeAll(parentStyleToMerge);
-        _applyBlockStyles(newStyle);
-      } // else the same style, no-op.
-    } else if (blockStyle.value != null) {
-      // Only wrap with a new block if this is not an unset
-      _applyBlockStyles(newStyle);
-    }
-  }
-
-  void _applyBlockStyles(Style newStyle) {
-    var block = Block();
-    for (final style in newStyle.getBlocksExceptHeader().values) {
-      block = block..applyAttribute(style);
-    }
-    _wrap(block);
-    block.adjust();
-  }
-
-  /// Wraps this line with new parent [block].
-  ///
-  /// This line can not be in a [Block] when this method is called.
-  void _wrap(Block block) {
-    assert(parent != null && parent is! Block);
-    insertAfter(block);
-    unlink();
-    block.add(this);
-  }
-
-  /// Unwraps this line from it's parent [Block].
-  ///
-  /// This method asserts if current [parent] of this line is not a [Block].
-  void _unwrap() {
-    if (parent is! Block) {
-      throw ArgumentError('Invalid parent');
-    }
-    final block = parent as Block;
-
-    assert(block.children.contains(this));
-
-    if (isFirst) {
-      unlink();
-      block.insertBefore(this);
-    } else if (isLast) {
-      unlink();
-      block.insertAfter(this);
-    } else {
-      final before = block.clone() as Block;
-      block.insertBefore(before);
-
-      var child = block.first as Line;
-      while (child != this) {
-        child.unlink();
-        before.add(child);
-        child = block.first as Line;
-      }
-      unlink();
-      block.insertBefore(this);
-    }
-    block.adjust();
-  }
-
-  Line _getNextLine(int index) {
-    assert(index == 0 || (index > 0 && index < length));
-
-    final line = clone() as Line;
-    insertAfter(line);
-    if (index == length - 1) {
-      return line;
-    }
-
-    final query = queryChild(index, false);
-    while (!query.node!.isLast) {
-      final next = (last as Leaf)..unlink();
-      line.addFirst(next);
-    }
-    final child = query.node as Leaf;
-    final cut = child.splitAt(query.offset);
-    cut?.unlink();
-    line.addFirst(cut);
-    return line;
-  }
-
-  void _insertSafe(int index, Object data, Style? style) {
-    assert(index == 0 || (index > 0 && index < length));
-
-    if (data is String) {
-      assert(!data.contains('\n'));
-      if (data.isEmpty) {
-        return;
-      }
-    }
-
-    if (isEmpty) {
-      final child = Leaf(data);
-      add(child);
-      child.format(style);
-    } else {
-      final result = queryChild(index, true);
-      result.node!.insert(result.offset, data, style);
-    }
-  }
-
-  /// Returns style for specified text range.
-  ///
-  /// Only attributes applied to all characters within this range are
-  /// included in the result. Inline and line level attributes are
-  /// handled separately, e.g.:
-  ///
-  /// - line attribute X is included in the result only if it exists for
-  ///   every line within this range (partially included lines are counted).
-  /// - inline attribute X is included in the result only if it exists
-  ///   for every character within this range (line-break characters excluded).
-  Style collectStyle(int offset, int len) {
-    final local = math.min(length - offset, len);
-    var result = Style();
-    final excluded = <Attribute>{};
-
-    void _handle(Style style) {
-      if (result.isEmpty) {
-        excluded.addAll(style.values);
-      } else {
-        for (final attr in result.values) {
-          if (!style.containsKey(attr.key)) {
-            excluded.add(attr);
-          }
-        }
-      }
-      final remaining = style.removeAll(excluded);
-      result = result.removeAll(excluded);
-      result = result.mergeAll(remaining);
-    }
-
-    final data = queryChild(offset, true);
-    var node = data.node as Leaf?;
-    if (node != null) {
-      result = result.mergeAll(node.style);
-      var pos = node.length - data.offset;
-      while (!node!.isLast && pos < local) {
-        node = node.next as Leaf?;
-        _handle(node!.style);
-        pos += node.length;
-      }
-    }
-
-    result = result.mergeAll(style);
-    if (parent is Block) {
-      final block = parent as Block;
-      result = result.mergeAll(block.style);
-    }
-
-    final remaining = len - local;
-    if (remaining > 0) {
-      final rest = nextLine!.collectStyle(0, remaining);
-      _handle(rest);
-    }
-
-    return result;
-  }
-
-  /// Returns all styles for any character within the specified text range.
-  List<Style> collectAllStyles(int offset, int len) {
-    final local = math.min(length - offset, len);
-    final result = <Style>[];
-
-    final data = queryChild(offset, true);
-    var node = data.node as Leaf?;
-    if (node != null) {
-      result.add(node.style);
-      var pos = node.length - data.offset;
-      while (!node!.isLast && pos < local) {
-        node = node.next as Leaf?;
-        result.add(node!.style);
-        pos += node.length;
-      }
-    }
-
-    result.add(style);
-    if (parent is Block) {
-      final block = parent as Block;
-      result.add(block.style);
-    }
-
-    final remaining = len - local;
-    if (remaining > 0) {
-      final rest = nextLine!.collectAllStyles(0, remaining);
-      result.addAll(rest);
-    }
-
-    return result;
-  }
-}

+ 0 - 134
app_flowy/packages/editor/lib/src/models/documents/nodes/node.dart

@@ -1,134 +0,0 @@
-import 'dart:collection';
-
-import '../../quill_delta.dart';
-import '../attribute.dart';
-import '../style.dart';
-import 'container.dart';
-import 'line.dart';
-
-/// An abstract node in a document tree.
-///
-/// Represents a segment of a Quill document with specified [offset]
-/// and [length].
-///
-/// The [offset] property is relative to [parent]. See also [documentOffset]
-/// which provides absolute offset of this node within the document.
-///
-/// The current parent node is exposed by the [parent] property.
-abstract class Node extends LinkedListEntry<Node> {
-  /// Current parent of this node. May be null if this node is not mounted.
-  Container? parent;
-
-  Style get style => _style;
-  Style _style = Style();
-
-  /// Returns `true` if this node is the first node in the [parent] list.
-  bool get isFirst => list!.first == this;
-
-  /// Returns `true` if this node is the last node in the [parent] list.
-  bool get isLast => list!.last == this;
-
-  /// Length of this node in characters.
-  int get length;
-
-  Node clone() => newInstance()..applyStyle(style);
-
-  /// Offset in characters of this node relative to [parent] node.
-  ///
-  /// To get offset of this node in the document see [documentOffset].
-  int get offset {
-    var offset = 0;
-
-    if (list == null || isFirst) {
-      return offset;
-    }
-
-    var cur = this;
-    do {
-      cur = cur.previous!;
-      offset += cur.length;
-    } while (!cur.isFirst);
-    return offset;
-  }
-
-  /// Offset in characters of this node in the document.
-  int get documentOffset {
-    if (parent == null) {
-      return offset;
-    }
-    final parentOffset = (parent is! Root) ? parent!.documentOffset : 0;
-    return parentOffset + offset;
-  }
-
-  /// Returns `true` if this node contains character at specified [offset] in
-  /// the document.
-  bool containsOffset(int offset) {
-    final o = documentOffset;
-    return o <= offset && offset < o + length;
-  }
-
-  void applyAttribute(Attribute attribute) {
-    _style = _style.merge(attribute);
-  }
-
-  void applyStyle(Style value) {
-    _style = _style.mergeAll(value);
-  }
-
-  void clearStyle() {
-    _style = Style();
-  }
-
-  @override
-  void insertBefore(Node entry) {
-    assert(entry.parent == null && parent != null);
-    entry.parent = parent;
-    super.insertBefore(entry);
-  }
-
-  @override
-  void insertAfter(Node entry) {
-    assert(entry.parent == null && parent != null);
-    entry.parent = parent;
-    super.insertAfter(entry);
-  }
-
-  @override
-  void unlink() {
-    assert(parent != null);
-    parent = null;
-    super.unlink();
-  }
-
-  void adjust() {/* no-op */}
-
-  /// abstract methods begin
-
-  Node newInstance();
-
-  String toPlainText();
-
-  Delta toDelta();
-
-  void insert(int index, Object data, Style? style);
-
-  void retain(int index, int? len, Style? style);
-
-  void delete(int index, int? len);
-
-  /// abstract methods end
-}
-
-/// Root node of document tree.
-class Root extends Container<Container<Node?>> {
-  @override
-  Node newInstance() => Root();
-
-  @override
-  Container<Node?> get defaultChild => Line();
-
-  @override
-  Delta toDelta() => children
-      .map((child) => child.toDelta())
-      .fold(Delta(), (a, b) => a.concat(b));
-}

+ 0 - 128
app_flowy/packages/editor/lib/src/models/documents/style.dart

@@ -1,128 +0,0 @@
-import 'package:collection/collection.dart';
-import 'package:quiver/core.dart';
-
-import 'attribute.dart';
-
-/* Collection of style attributes */
-class Style {
-  Style() : _attributes = <String, Attribute>{};
-
-  Style.attr(this._attributes);
-
-  final Map<String, Attribute> _attributes;
-
-  static Style fromJson(Map<String, dynamic>? attributes) {
-    if (attributes == null) {
-      return Style();
-    }
-
-    final result = attributes.map((key, dynamic value) {
-      final attr = Attribute.fromKeyValue(key, value);
-      return MapEntry<String, Attribute>(
-          key, attr ?? Attribute(key, AttributeScope.IGNORE, value));
-    });
-    return Style.attr(result);
-  }
-
-  Map<String, dynamic>? toJson() => _attributes.isEmpty
-      ? null
-      : _attributes.map<String, dynamic>((_, attribute) =>
-          MapEntry<String, dynamic>(attribute.key, attribute.value));
-
-  Iterable<String> get keys => _attributes.keys;
-
-  Iterable<Attribute> get values => _attributes.values.sorted(
-      (a, b) => Attribute.getRegistryOrder(a) - Attribute.getRegistryOrder(b));
-
-  Map<String, Attribute> get attributes => _attributes;
-
-  bool get isEmpty => _attributes.isEmpty;
-
-  bool get isNotEmpty => _attributes.isNotEmpty;
-
-  bool get isInline => isNotEmpty && values.every((item) => item.isInline);
-
-  bool get isIgnored =>
-      isNotEmpty && values.every((item) => item.scope == AttributeScope.IGNORE);
-
-  Attribute get single => _attributes.values.single;
-
-  bool containsKey(String key) => _attributes.containsKey(key);
-
-  Attribute? getBlockExceptHeader() {
-    for (final val in values) {
-      if (val.isBlockExceptHeader && val.value != null) {
-        return val;
-      }
-    }
-    for (final val in values) {
-      if (val.isBlockExceptHeader) {
-        return val;
-      }
-    }
-    return null;
-  }
-
-  Map<String, Attribute> getBlocksExceptHeader() {
-    final m = <String, Attribute>{};
-    attributes.forEach((key, value) {
-      if (Attribute.blockKeysExceptHeader.contains(key)) {
-        m[key] = value;
-      }
-    });
-    return m;
-  }
-
-  Style merge(Attribute attribute) {
-    final merged = Map<String, Attribute>.from(_attributes);
-    if (attribute.value == null) {
-      merged.remove(attribute.key);
-    } else {
-      merged[attribute.key] = attribute;
-    }
-    return Style.attr(merged);
-  }
-
-  Style mergeAll(Style other) {
-    var result = Style.attr(_attributes);
-    for (final attribute in other.values) {
-      result = result.merge(attribute);
-    }
-    return result;
-  }
-
-  Style removeAll(Set<Attribute> attributes) {
-    final merged = Map<String, Attribute>.from(_attributes);
-    attributes.map((item) => item.key).forEach(merged.remove);
-    return Style.attr(merged);
-  }
-
-  Style put(Attribute attribute) {
-    final m = Map<String, Attribute>.from(attributes);
-    m[attribute.key] = attribute;
-    return Style.attr(m);
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) {
-      return true;
-    }
-    if (other is! Style) {
-      return false;
-    }
-    final typedOther = other;
-    const eq = MapEquality<String, Attribute>();
-    return eq.equals(_attributes, typedOther._attributes);
-  }
-
-  @override
-  int get hashCode {
-    final hashes =
-        _attributes.entries.map((entry) => hash2(entry.key, entry.value));
-    return hashObjects(hashes);
-  }
-
-  @override
-  String toString() => "{${_attributes.values.join(', ')}}";
-}

+ 0 - 803
app_flowy/packages/editor/lib/src/models/quill_delta.dart

@@ -1,803 +0,0 @@
-// Copyright (c) 2018, Anatoly Pulyaevskiy. All rights reserved. Use of this
-// source code is governed by a BSD-style license that can be found in the
-// LICENSE file.
-
-/// Implementation of Quill Delta format in Dart.
-library quill_delta;
-
-import 'dart:math' as math;
-
-import 'package:collection/collection.dart';
-import 'package:diff_match_patch/diff_match_patch.dart' as dmp;
-import 'package:quiver/core.dart';
-
-const _attributeEquality = DeepCollectionEquality();
-const _valueEquality = DeepCollectionEquality();
-
-/// Decoder function to convert raw `data` object into a user-defined data type.
-///
-/// Useful with embedded content.
-typedef DataDecoder = Object? Function(Object data);
-
-/// Default data decoder which simply passes through the original value.
-Object? _passThroughDataDecoder(Object? data) => data;
-
-/// Operation performed on a rich-text document.
-class Operation {
-  Operation._(this.key, this.length, this.data, Map? attributes)
-      : assert(_validKeys.contains(key), 'Invalid operation key "$key".'),
-        assert(() {
-          if (key != Operation.insertKey) return true;
-          return data is String ? data.length == length : length == 1;
-        }(), 'Length of insert operation must be equal to the data length.'),
-        _attributes =
-            attributes != null ? Map<String, dynamic>.from(attributes) : null;
-
-  /// Creates operation which deletes [length] of characters.
-  factory Operation.delete(int length) =>
-      Operation._(Operation.deleteKey, length, '', null);
-
-  /// Creates operation which inserts [text] with optional [attributes].
-  factory Operation.insert(dynamic data, [Map<String, dynamic>? attributes]) =>
-      Operation._(Operation.insertKey, data is String ? data.length : 1, data,
-          attributes);
-
-  /// Creates operation which retains [length] of characters and optionally
-  /// applies attributes.
-  factory Operation.retain(int? length, [Map<String, dynamic>? attributes]) =>
-      Operation._(Operation.retainKey, length, '', attributes);
-
-  /// Key of insert operations.
-  static const String insertKey = 'insert';
-
-  /// Key of delete operations.
-  static const String deleteKey = 'delete';
-
-  /// Key of retain operations.
-  static const String retainKey = 'retain';
-
-  /// Key of attributes collection.
-  static const String attributesKey = 'attributes';
-
-  static const List<String> _validKeys = [insertKey, deleteKey, retainKey];
-
-  /// Key of this operation, can be "insert", "delete" or "retain".
-  final String key;
-
-  /// Length of this operation.
-  final int? length;
-
-  /// Payload of "insert" operation, for other types is set to empty string.
-  final Object? data;
-
-  /// Rich-text attributes set by this operation, can be `null`.
-  Map<String, dynamic>? get attributes =>
-      _attributes == null ? null : Map<String, dynamic>.from(_attributes!);
-  final Map<String, dynamic>? _attributes;
-
-  /// Creates new [Operation] from JSON payload.
-  ///
-  /// If `dataDecoder` parameter is not null then it is used to additionally
-  /// decode the operation's data object. Only applied to insert operations.
-  static Operation fromJson(Map data, {DataDecoder? dataDecoder}) {
-    dataDecoder ??= _passThroughDataDecoder;
-    final map = Map<String, dynamic>.from(data);
-    if (map.containsKey(Operation.insertKey)) {
-      final data = dataDecoder(map[Operation.insertKey]);
-      final dataLength = data is String ? data.length : 1;
-      return Operation._(
-          Operation.insertKey, dataLength, data, map[Operation.attributesKey]);
-    } else if (map.containsKey(Operation.deleteKey)) {
-      final int? length = map[Operation.deleteKey];
-      return Operation._(Operation.deleteKey, length, '', null);
-    } else if (map.containsKey(Operation.retainKey)) {
-      final int? length = map[Operation.retainKey];
-      return Operation._(
-          Operation.retainKey, length, '', map[Operation.attributesKey]);
-    }
-    throw ArgumentError.value(data, 'Invalid data for Delta operation.');
-  }
-
-  /// Returns JSON-serializable representation of this operation.
-  Map<String, dynamic> toJson() {
-    final json = {key: value};
-    if (_attributes != null) json[Operation.attributesKey] = attributes;
-    return json;
-  }
-
-  /// Returns value of this operation.
-  ///
-  /// For insert operations this returns text, for delete and retain - length.
-  dynamic get value => (key == Operation.insertKey) ? data : length;
-
-  /// Returns `true` if this is a delete operation.
-  bool get isDelete => key == Operation.deleteKey;
-
-  /// Returns `true` if this is an insert operation.
-  bool get isInsert => key == Operation.insertKey;
-
-  /// Returns `true` if this is a retain operation.
-  bool get isRetain => key == Operation.retainKey;
-
-  /// Returns `true` if this operation has no attributes, e.g. is plain text.
-  bool get isPlain => _attributes == null || _attributes!.isEmpty;
-
-  /// Returns `true` if this operation sets at least one attribute.
-  bool get isNotPlain => !isPlain;
-
-  /// Returns `true` is this operation is empty.
-  ///
-  /// An operation is considered empty if its [length] is equal to `0`.
-  bool get isEmpty => length == 0;
-
-  /// Returns `true` is this operation is not empty.
-  bool get isNotEmpty => length! > 0;
-
-  @override
-  bool operator ==(other) {
-    if (identical(this, other)) return true;
-    if (other is! Operation) return false;
-    final typedOther = other;
-    return key == typedOther.key &&
-        length == typedOther.length &&
-        _valueEquality.equals(data, typedOther.data) &&
-        hasSameAttributes(typedOther);
-  }
-
-  /// Returns `true` if this operation has attribute specified by [name].
-  bool hasAttribute(String name) =>
-      isNotPlain && _attributes!.containsKey(name);
-
-  /// Returns `true` if [other] operation has the same attributes as this one.
-  bool hasSameAttributes(Operation other) {
-    return _attributeEquality.equals(_attributes, other._attributes);
-  }
-
-  @override
-  int get hashCode {
-    if (_attributes != null && _attributes!.isNotEmpty) {
-      final attrsHash =
-          hashObjects(_attributes!.entries.map((e) => hash2(e.key, e.value)));
-      return hash3(key, value, attrsHash);
-    }
-    return hash2(key, value);
-  }
-
-  @override
-  String toString() {
-    final attr = attributes == null ? '' : ' + $attributes';
-    final text = isInsert
-        ? (data is String
-            ? (data as String).replaceAll('\n', '⏎')
-            : data.toString())
-        : '$length';
-    return '$key⟨ $text ⟩$attr';
-  }
-}
-
-/// Delta represents a document or a modification of a document as a sequence of
-/// insert, delete and retain operations.
-///
-/// Delta consisting of only "insert" operations is usually referred to as
-/// "document delta". When delta includes also "retain" or "delete" operations
-/// it is a "change delta".
-class Delta {
-  /// Creates new empty [Delta].
-  factory Delta() => Delta._(<Operation>[]);
-
-  Delta._(List<Operation> operations) : _operations = operations;
-
-  /// Creates new [Delta] from [other].
-  factory Delta.from(Delta other) =>
-      Delta._(List<Operation>.from(other._operations));
-
-  // Placeholder char for embed in diff()
-  static final String _kNullCharacter = String.fromCharCode(0);
-
-  /// Transforms two attribute sets.
-  static Map<String, dynamic>? transformAttributes(
-      Map<String, dynamic>? a, Map<String, dynamic>? b, bool priority) {
-    if (a == null) return b;
-    if (b == null) return null;
-
-    if (!priority) return b;
-
-    final result = b.keys.fold<Map<String, dynamic>>({}, (attributes, key) {
-      if (!a.containsKey(key)) attributes[key] = b[key];
-      return attributes;
-    });
-
-    return result.isEmpty ? null : result;
-  }
-
-  /// Composes two attribute sets.
-  static Map<String, dynamic>? composeAttributes(
-      Map<String, dynamic>? a, Map<String, dynamic>? b,
-      {bool keepNull = false}) {
-    a ??= const {};
-    b ??= const {};
-
-    final result = Map<String, dynamic>.from(a)..addAll(b);
-    final keys = result.keys.toList(growable: false);
-
-    if (!keepNull) {
-      for (final key in keys) {
-        if (result[key] == null) result.remove(key);
-      }
-    }
-
-    return result.isEmpty ? null : result;
-  }
-
-  ///get anti-attr result base on base
-  static Map<String, dynamic> invertAttributes(
-      Map<String, dynamic>? attr, Map<String, dynamic>? base) {
-    attr ??= const {};
-    base ??= const {};
-
-    final baseInverted = base.keys.fold({}, (dynamic memo, key) {
-      if (base![key] != attr![key] && attr.containsKey(key)) {
-        memo[key] = base[key];
-      }
-      return memo;
-    });
-
-    final inverted =
-        Map<String, dynamic>.from(attr.keys.fold(baseInverted, (memo, key) {
-      if (base![key] != attr![key] && !base.containsKey(key)) {
-        memo[key] = null;
-      }
-      return memo;
-    }));
-    return inverted;
-  }
-
-  /// Returns diff between two attribute sets
-  static Map<String, dynamic>? diffAttributes(
-      Map<String, dynamic>? a, Map<String, dynamic>? b) {
-    a ??= const {};
-    b ??= const {};
-
-    final attributes = <String, dynamic>{};
-    (a.keys.toList()..addAll(b.keys)).forEach((key) {
-      if (a![key] != b![key]) {
-        attributes[key] = b.containsKey(key) ? b[key] : null;
-      }
-    });
-
-    return attributes.keys.isNotEmpty ? attributes : null;
-  }
-
-  final List<Operation> _operations;
-
-  int _modificationCount = 0;
-
-  /// Creates [Delta] from de-serialized JSON representation.
-  ///
-  /// If `dataDecoder` parameter is not null then it is used to additionally
-  /// decode the operation's data object. Only applied to insert operations.
-  static Delta fromJson(List data, {DataDecoder? dataDecoder}) {
-    return Delta._(data
-        .map((op) => Operation.fromJson(op, dataDecoder: dataDecoder))
-        .toList());
-  }
-
-  /// Returns list of operations in this delta.
-  List<Operation> toList() => List.from(_operations);
-
-  /// Returns JSON-serializable version of this delta.
-  List toJson() => toList().map((operation) => operation.toJson()).toList();
-
-  /// Returns `true` if this delta is empty.
-  bool get isEmpty => _operations.isEmpty;
-
-  /// Returns `true` if this delta is not empty.
-  bool get isNotEmpty => _operations.isNotEmpty;
-
-  /// Returns number of operations in this delta.
-  int get length => _operations.length;
-
-  /// Returns [Operation] at specified [index] in this delta.
-  Operation operator [](int index) => _operations[index];
-
-  /// Returns [Operation] at specified [index] in this delta.
-  Operation elementAt(int index) => _operations.elementAt(index);
-
-  /// Returns the first [Operation] in this delta.
-  Operation get first => _operations.first;
-
-  /// Returns the last [Operation] in this delta.
-  Operation get last => _operations.last;
-
-  @override
-  bool operator ==(dynamic other) {
-    if (identical(this, other)) return true;
-    if (other is! Delta) return false;
-    final typedOther = other;
-    const comparator = ListEquality<Operation>(DefaultEquality<Operation>());
-    return comparator.equals(_operations, typedOther._operations);
-  }
-
-  @override
-  int get hashCode => hashObjects(_operations);
-
-  /// Retain [count] of characters from current position.
-  void retain(int count, [Map<String, dynamic>? attributes]) {
-    assert(count >= 0);
-    if (count == 0) return; // no-op
-    push(Operation.retain(count, attributes));
-  }
-
-  /// Insert [data] at current position.
-  void insert(dynamic data, [Map<String, dynamic>? attributes]) {
-    if (data is String && data.isEmpty) return; // no-op
-    push(Operation.insert(data, attributes));
-  }
-
-  /// Delete [count] characters from current position.
-  void delete(int count) {
-    assert(count >= 0);
-    if (count == 0) return;
-    push(Operation.delete(count));
-  }
-
-  void _mergeWithTail(Operation operation) {
-    assert(isNotEmpty);
-    assert(last.key == operation.key);
-    assert(operation.data is String && last.data is String);
-
-    final length = operation.length! + last.length!;
-    final lastText = last.data as String;
-    final opText = operation.data as String;
-    final resultText = lastText + opText;
-    final index = _operations.length;
-    _operations.replaceRange(index - 1, index, [
-      Operation._(operation.key, length, resultText, operation.attributes),
-    ]);
-  }
-
-  /// Pushes new operation into this delta.
-  ///
-  /// Performs compaction by composing [operation] with current tail operation
-  /// of this delta, when possible. For instance, if current tail is
-  /// `insert('abc')` and pushed operation is `insert('123')` then existing
-  /// tail is replaced with `insert('abc123')` - a compound result of the two
-  /// operations.
-  void push(Operation operation) {
-    if (operation.isEmpty) return;
-
-    var index = _operations.length;
-    final lastOp = _operations.isNotEmpty ? _operations.last : null;
-    if (lastOp != null) {
-      if (lastOp.isDelete && operation.isDelete) {
-        _mergeWithTail(operation);
-        return;
-      }
-
-      if (lastOp.isDelete && operation.isInsert) {
-        index -= 1; // Always insert before deleting
-        final nLastOp = (index > 0) ? _operations.elementAt(index - 1) : null;
-        if (nLastOp == null) {
-          _operations.insert(0, operation);
-          return;
-        }
-      }
-
-      if (lastOp.isInsert && operation.isInsert) {
-        if (lastOp.hasSameAttributes(operation) &&
-            operation.data is String &&
-            lastOp.data is String) {
-          _mergeWithTail(operation);
-          return;
-        }
-      }
-
-      if (lastOp.isRetain && operation.isRetain) {
-        if (lastOp.hasSameAttributes(operation)) {
-          _mergeWithTail(operation);
-          return;
-        }
-      }
-    }
-    if (index == _operations.length) {
-      _operations.add(operation);
-    } else {
-      final opAtIndex = _operations.elementAt(index);
-      _operations.replaceRange(index, index + 1, [operation, opAtIndex]);
-    }
-    _modificationCount++;
-  }
-
-  /// Composes next operation from [thisIter] and [otherIter].
-  ///
-  /// Returns new operation or `null` if operations from [thisIter] and
-  /// [otherIter] nullify each other. For instance, for the pair `insert('abc')`
-  /// and `delete(3)` composition result would be empty string.
-  Operation? _composeOperation(
-      DeltaIterator thisIter, DeltaIterator otherIter) {
-    if (otherIter.isNextInsert) return otherIter.next();
-    if (thisIter.isNextDelete) return thisIter.next();
-
-    final length = math.min(thisIter.peekLength(), otherIter.peekLength());
-    final thisOp = thisIter.next(length);
-    final otherOp = otherIter.next(length);
-    assert(thisOp.length == otherOp.length);
-
-    if (otherOp.isRetain) {
-      final attributes = composeAttributes(
-        thisOp.attributes,
-        otherOp.attributes,
-        keepNull: thisOp.isRetain,
-      );
-      if (thisOp.isRetain) {
-        return Operation.retain(thisOp.length, attributes);
-      } else if (thisOp.isInsert) {
-        return Operation.insert(thisOp.data, attributes);
-      } else {
-        throw StateError('Unreachable');
-      }
-    } else {
-      // otherOp == delete && thisOp in [retain, insert]
-      assert(otherOp.isDelete);
-      if (thisOp.isRetain) return otherOp;
-      assert(thisOp.isInsert);
-      // otherOp(delete) + thisOp(insert) => null
-    }
-    return null;
-  }
-
-  /// Composes this delta with [other] and returns new [Delta].
-  ///
-  /// It is not required for this and [other] delta to represent a document
-  /// delta (consisting only of insert operations).
-  Delta compose(Delta other) {
-    final result = Delta();
-    final thisIter = DeltaIterator(this);
-    final otherIter = DeltaIterator(other);
-
-    while (thisIter.hasNext || otherIter.hasNext) {
-      final newOp = _composeOperation(thisIter, otherIter);
-      if (newOp != null) result.push(newOp);
-    }
-    return result..trim();
-  }
-
-  /// Returns a new lazy Iterable with elements that are created by calling
-  /// f on each element of this Iterable in iteration order.
-  ///
-  /// Convenience method
-  Iterable<T> map<T>(T Function(Operation) f) {
-    return _operations.map<T>(f);
-  }
-
-  /// Returns a [Delta] containing differences between 2 [Delta]s.
-  /// If [cleanupSemantic] is `true` (default), applies the following:
-  ///
-  /// The diff of "mouse" and "sofas" is
-  ///   [delete(1), insert("s"), retain(1),
-  ///   delete("u"), insert("fa"), retain(1), delete(1)].
-  /// While this is the optimum diff, it is difficult for humans to understand.
-  /// Semantic cleanup rewrites the diff,
-  /// expanding it into a more intelligible format.
-  /// The above example would become: [(-1, "mouse"), (1, "sofas")].
-  /// (source: https://github.com/google/diff-match-patch/wiki/API)
-  ///
-  /// Useful when one wishes to display difference between 2 documents
-  Delta diff(Delta other, {bool cleanupSemantic = true}) {
-    if (_operations.equals(other._operations)) {
-      return Delta();
-    }
-    final stringThis = map((op) {
-      if (op.isInsert) {
-        return op.data is String ? op.data : _kNullCharacter;
-      }
-      final prep = this == other ? 'on' : 'with';
-      throw ArgumentError('diff() call $prep non-document');
-    }).join();
-    final stringOther = other.map((op) {
-      if (op.isInsert) {
-        return op.data is String ? op.data : _kNullCharacter;
-      }
-      final prep = this == other ? 'on' : 'with';
-      throw ArgumentError('diff() call $prep non-document');
-    }).join();
-
-    final retDelta = Delta();
-    final diffResult = dmp.diff(stringThis, stringOther);
-    if (cleanupSemantic) {
-      dmp.DiffMatchPatch().diffCleanupSemantic(diffResult);
-    }
-
-    final thisIter = DeltaIterator(this);
-    final otherIter = DeltaIterator(other);
-
-    diffResult.forEach((component) {
-      var length = component.text.length;
-      while (length > 0) {
-        var opLength = 0;
-        switch (component.operation) {
-          case dmp.DIFF_INSERT:
-            opLength = math.min(otherIter.peekLength(), length);
-            retDelta.push(otherIter.next(opLength));
-            break;
-          case dmp.DIFF_DELETE:
-            opLength = math.min(length, thisIter.peekLength());
-            thisIter.next(opLength);
-            retDelta.delete(opLength);
-            break;
-          case dmp.DIFF_EQUAL:
-            opLength = math.min(
-              math.min(thisIter.peekLength(), otherIter.peekLength()),
-              length,
-            );
-            final thisOp = thisIter.next(opLength);
-            final otherOp = otherIter.next(opLength);
-            if (thisOp.data == otherOp.data) {
-              retDelta.retain(
-                opLength,
-                diffAttributes(thisOp.attributes, otherOp.attributes),
-              );
-            } else {
-              retDelta
-                ..push(otherOp)
-                ..delete(opLength);
-            }
-            break;
-        }
-        length -= opLength;
-      }
-    });
-    return retDelta..trim();
-  }
-
-  /// Transforms next operation from [otherIter] against next operation in
-  /// [thisIter].
-  ///
-  /// Returns `null` if both operations nullify each other.
-  Operation? _transformOperation(
-      DeltaIterator thisIter, DeltaIterator otherIter, bool priority) {
-    if (thisIter.isNextInsert && (priority || !otherIter.isNextInsert)) {
-      return Operation.retain(thisIter.next().length);
-    } else if (otherIter.isNextInsert) {
-      return otherIter.next();
-    }
-
-    final length = math.min(thisIter.peekLength(), otherIter.peekLength());
-    final thisOp = thisIter.next(length);
-    final otherOp = otherIter.next(length);
-    assert(thisOp.length == otherOp.length);
-
-    // At this point only delete and retain operations are possible.
-    if (thisOp.isDelete) {
-      // otherOp is either delete or retain, so they nullify each other.
-      return null;
-    } else if (otherOp.isDelete) {
-      return otherOp;
-    } else {
-      // Retain otherOp which is either retain or insert.
-      return Operation.retain(
-        length,
-        transformAttributes(thisOp.attributes, otherOp.attributes, priority),
-      );
-    }
-  }
-
-  /// Transforms [other] delta against operations in this delta.
-  Delta transform(Delta other, bool priority) {
-    final result = Delta();
-    final thisIter = DeltaIterator(this);
-    final otherIter = DeltaIterator(other);
-
-    while (thisIter.hasNext || otherIter.hasNext) {
-      final newOp = _transformOperation(thisIter, otherIter, priority);
-      if (newOp != null) result.push(newOp);
-    }
-    return result..trim();
-  }
-
-  /// Removes trailing retain operation with empty attributes, if present.
-  void trim() {
-    if (isNotEmpty) {
-      final last = _operations.last;
-      if (last.isRetain && last.isPlain) _operations.removeLast();
-    }
-  }
-
-  /// Concatenates [other] with this delta and returns the result.
-  Delta concat(Delta other) {
-    final result = Delta.from(this);
-    if (other.isNotEmpty) {
-      // In case first operation of other can be merged with last operation in
-      // our list.
-      result.push(other._operations.first);
-      result._operations.addAll(other._operations.sublist(1));
-    }
-    return result;
-  }
-
-  /// Inverts this delta against [base].
-  ///
-  /// Returns new delta which negates effect of this delta when applied to
-  /// [base]. This is an equivalent of "undo" operation on deltas.
-  Delta invert(Delta base) {
-    final inverted = Delta();
-    if (base.isEmpty) return inverted;
-
-    var baseIndex = 0;
-    for (final op in _operations) {
-      if (op.isInsert) {
-        inverted.delete(op.length!);
-      } else if (op.isRetain && op.isPlain) {
-        inverted.retain(op.length!);
-        baseIndex += op.length!;
-      } else if (op.isDelete || (op.isRetain && op.isNotPlain)) {
-        final length = op.length!;
-        final sliceDelta = base.slice(baseIndex, baseIndex + length);
-        sliceDelta.toList().forEach((baseOp) {
-          if (op.isDelete) {
-            inverted.push(baseOp);
-          } else if (op.isRetain && op.isNotPlain) {
-            final invertAttr =
-                invertAttributes(op.attributes, baseOp.attributes);
-            inverted.retain(
-                baseOp.length!, invertAttr.isEmpty ? null : invertAttr);
-          }
-        });
-        baseIndex += length;
-      } else {
-        throw StateError('Unreachable');
-      }
-    }
-    inverted.trim();
-    return inverted;
-  }
-
-  /// Returns slice of this delta from [start] index (inclusive) to [end]
-  /// (exclusive).
-  Delta slice(int start, [int? end]) {
-    final delta = Delta();
-    var index = 0;
-    final opIterator = DeltaIterator(this);
-
-    final actualEnd = end ?? DeltaIterator.maxLength;
-
-    while (index < actualEnd && opIterator.hasNext) {
-      Operation op;
-      if (index < start) {
-        op = opIterator.next(start - index);
-      } else {
-        op = opIterator.next(actualEnd - index);
-        delta.push(op);
-      }
-      index += op.length!;
-    }
-    return delta;
-  }
-
-  /// Transforms [index] against this delta.
-  ///
-  /// Any "delete" operation before specified [index] shifts it backward, as
-  /// well as any "insert" operation shifts it forward.
-  ///
-  /// The [force] argument is used to resolve scenarios when there is an
-  /// insert operation at the same position as [index]. If [force] is set to
-  /// `true` (default) then position is forced to shift forward, otherwise
-  /// position stays at the same index. In other words setting [force] to
-  /// `false` gives higher priority to the transformed position.
-  ///
-  /// Useful to adjust caret or selection positions.
-  int transformPosition(int index, {bool force = true}) {
-    final iter = DeltaIterator(this);
-    var offset = 0;
-    while (iter.hasNext && offset <= index) {
-      final op = iter.next();
-      if (op.isDelete) {
-        index -= math.min(op.length!, index - offset);
-        continue;
-      } else if (op.isInsert && (offset < index || force)) {
-        index += op.length!;
-      }
-      offset += op.length!;
-    }
-    return index;
-  }
-
-  @override
-  String toString() => _operations.join('\n');
-}
-
-/// Specialized iterator for [Delta]s.
-class DeltaIterator {
-  DeltaIterator(this.delta) : _modificationCount = delta._modificationCount;
-
-  static const int maxLength = 1073741824;
-
-  final Delta delta;
-  final int _modificationCount;
-  int _index = 0;
-  int _offset = 0;
-
-  bool get isNextInsert => nextOperationKey == Operation.insertKey;
-
-  bool get isNextDelete => nextOperationKey == Operation.deleteKey;
-
-  bool get isNextRetain => nextOperationKey == Operation.retainKey;
-
-  String? get nextOperationKey {
-    if (_index < delta.length) {
-      return delta.elementAt(_index).key;
-    } else {
-      return null;
-    }
-  }
-
-  bool get hasNext => peekLength() < maxLength;
-
-  /// Returns length of next operation without consuming it.
-  ///
-  /// Returns [maxLength] if there is no more operations left to iterate.
-  int peekLength() {
-    if (_index < delta.length) {
-      final operation = delta._operations[_index];
-      return operation.length! - _offset;
-    }
-    return maxLength;
-  }
-
-  /// Consumes and returns next operation.
-  ///
-  /// Optional [length] specifies maximum length of operation to return. Note
-  /// that actual length of returned operation may be less than specified value.
-  ///
-  /// If this iterator reached the end of the Delta then returns a retain
-  /// operation with its length set to [maxLength].
-  // TODO: Note that we used double.infinity as the default value
-  // for length here
-  //       but this can now cause a type error since operation length is
-  //       expected to be an int. Changing default length to [maxLength] is
-  //       a workaround to avoid breaking changes.
-  Operation next([int length = maxLength]) {
-    if (_modificationCount != delta._modificationCount) {
-      throw ConcurrentModificationError(delta);
-    }
-
-    if (_index < delta.length) {
-      final op = delta.elementAt(_index);
-      final opKey = op.key;
-      final opAttributes = op.attributes;
-      final _currentOffset = _offset;
-      final actualLength = math.min(op.length! - _currentOffset, length);
-      if (actualLength == op.length! - _currentOffset) {
-        _index++;
-        _offset = 0;
-      } else {
-        _offset += actualLength;
-      }
-      final opData = op.isInsert && op.data is String
-          ? (op.data as String)
-              .substring(_currentOffset, _currentOffset + actualLength)
-          : op.data;
-      final opIsNotEmpty =
-          opData is String ? opData.isNotEmpty : true; // embeds are never empty
-      final opLength = opData is String ? opData.length : 1;
-      final opActualLength = opIsNotEmpty ? opLength : actualLength;
-      return Operation._(opKey, opActualLength, opData, opAttributes);
-    }
-    return Operation.retain(length);
-  }
-
-  /// Skips [length] characters in source delta.
-  ///
-  /// Returns last skipped operation, or `null` if there was nothing to skip.
-  Operation? skip(int length) {
-    var skipped = 0;
-    Operation? op;
-    while (skipped < length && hasNext) {
-      final opLength = peekLength();
-      final skip = math.min(length - skipped, opLength);
-      op = next(skip);
-      skipped += op.length!;
-    }
-    return op;
-  }
-}

+ 0 - 126
app_flowy/packages/editor/lib/src/models/rules/delete.dart

@@ -1,126 +0,0 @@
-import '../documents/attribute.dart';
-import '../quill_delta.dart';
-import 'rule.dart';
-
-abstract class DeleteRule extends Rule {
-  const DeleteRule();
-
-  @override
-  RuleType get type => RuleType.DELETE;
-
-  @override
-  void validateArgs(int? len, Object? data, Attribute? attribute) {
-    assert(len != null);
-    assert(data == null);
-    assert(attribute == null);
-  }
-}
-
-class CatchAllDeleteRule extends DeleteRule {
-  const CatchAllDeleteRule();
-
-  @override
-  Delta applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    return Delta()
-      ..retain(index)
-      ..delete(len!);
-  }
-}
-
-class PreserveLineStyleOnMergeRule extends DeleteRule {
-  const PreserveLineStyleOnMergeRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    final itr = DeltaIterator(document)..skip(index);
-    var op = itr.next(1);
-    if (op.data != '\n') {
-      return null;
-    }
-
-    final isNotPlain = op.isNotPlain;
-    final attrs = op.attributes;
-
-    itr.skip(len! - 1);
-    final delta = Delta()
-      ..retain(index)
-      ..delete(len);
-
-    while (itr.hasNext) {
-      op = itr.next();
-      final text = op.data is String ? (op.data as String?)! : '';
-      final lineBreak = text.indexOf('\n');
-      if (lineBreak == -1) {
-        delta.retain(op.length!);
-        continue;
-      }
-
-      var attributes = op.attributes == null
-          ? null
-          : op.attributes!.map<String, dynamic>(
-              (key, dynamic value) => MapEntry<String, dynamic>(key, null));
-
-      if (isNotPlain) {
-        attributes ??= <String, dynamic>{};
-        attributes.addAll(attrs!);
-      }
-      delta
-        ..retain(lineBreak)
-        ..retain(1, attributes);
-      break;
-    }
-    return delta;
-  }
-}
-
-class EnsureEmbedLineRule extends DeleteRule {
-  const EnsureEmbedLineRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    final itr = DeltaIterator(document);
-
-    var op = itr.skip(index);
-    int? indexDelta = 0, lengthDelta = 0, remain = len;
-    var embedFound = op != null && op.data is! String;
-    final hasLineBreakBefore =
-        !embedFound && (op == null || (op.data as String).endsWith('\n'));
-    if (embedFound) {
-      var candidate = itr.next(1);
-      if (remain != null) {
-        remain--;
-        if (candidate.data == '\n') {
-          indexDelta++;
-          lengthDelta--;
-
-          candidate = itr.next(1);
-          remain--;
-          if (candidate.data == '\n') {
-            lengthDelta++;
-          }
-        }
-      }
-    }
-
-    op = itr.skip(remain!);
-    if (op != null &&
-        (op.data is String ? op.data as String? : '')!.endsWith('\n')) {
-      final candidate = itr.next(1);
-      if (candidate.data is! String && !hasLineBreakBefore) {
-        embedFound = true;
-        lengthDelta--;
-      }
-    }
-
-    if (!embedFound) {
-      return null;
-    }
-
-    return Delta()
-      ..retain(index + indexDelta)
-      ..delete(len! + lengthDelta);
-  }
-}

+ 0 - 161
app_flowy/packages/editor/lib/src/models/rules/format.dart

@@ -1,161 +0,0 @@
-import '../documents/attribute.dart';
-import '../quill_delta.dart';
-import 'rule.dart';
-
-abstract class FormatRule extends Rule {
-  const FormatRule();
-
-  @override
-  RuleType get type => RuleType.FORMAT;
-
-  @override
-  void validateArgs(int? len, Object? data, Attribute? attribute) {
-    assert(len != null);
-    assert(data == null);
-    assert(attribute != null);
-  }
-}
-
-class ResolveLineFormatRule extends FormatRule {
-  const ResolveLineFormatRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (attribute!.scope != AttributeScope.BLOCK) {
-      return null;
-    }
-
-    var delta = Delta()..retain(index);
-    final itr = DeltaIterator(document)..skip(index);
-    Operation op;
-    for (var cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
-      op = itr.next(len - cur);
-      if (op.data is! String || !(op.data as String).contains('\n')) {
-        delta.retain(op.length!);
-        continue;
-      }
-      final text = op.data as String;
-      final tmp = Delta();
-      var offset = 0;
-
-      // Enforce Block Format exclusivity by rule
-      final removedBlocks = Attribute.exclusiveBlockKeys.contains(attribute.key)
-          ? op.attributes?.keys
-                  .where((key) =>
-                      Attribute.exclusiveBlockKeys.contains(key) &&
-                      attribute.key != key &&
-                      attribute.value != null)
-                  .map((key) => MapEntry<String, dynamic>(key, null)) ??
-              []
-          : <MapEntry<String, dynamic>>[];
-
-      for (var lineBreak = text.indexOf('\n');
-          lineBreak >= 0;
-          lineBreak = text.indexOf('\n', offset)) {
-        tmp
-          ..retain(lineBreak - offset)
-          ..retain(1, attribute.toJson()..addEntries(removedBlocks));
-        offset = lineBreak + 1;
-      }
-      tmp.retain(text.length - offset);
-      delta = delta.concat(tmp);
-    }
-
-    while (itr.hasNext) {
-      op = itr.next();
-      final text = op.data is String ? (op.data as String?)! : '';
-      final lineBreak = text.indexOf('\n');
-      if (lineBreak < 0) {
-        delta.retain(op.length!);
-        continue;
-      }
-      // Enforce Block Format exclusivity by rule
-      final removedBlocks = Attribute.exclusiveBlockKeys.contains(attribute.key)
-          ? op.attributes?.keys
-                  .where((key) =>
-                      Attribute.exclusiveBlockKeys.contains(key) &&
-                      attribute.key != key &&
-                      attribute.value != null)
-                  .map((key) => MapEntry<String, dynamic>(key, null)) ??
-              []
-          : <MapEntry<String, dynamic>>[];
-      delta
-        ..retain(lineBreak)
-        ..retain(1, attribute.toJson()..addEntries(removedBlocks));
-      break;
-    }
-    return delta;
-  }
-}
-
-class FormatLinkAtCaretPositionRule extends FormatRule {
-  const FormatLinkAtCaretPositionRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (attribute!.key != Attribute.link.key || len! > 0) {
-      return null;
-    }
-
-    final delta = Delta();
-    final itr = DeltaIterator(document);
-    final before = itr.skip(index), after = itr.next();
-    int? beg = index, retain = 0;
-    if (before != null && before.hasAttribute(attribute.key)) {
-      beg -= before.length!;
-      retain = before.length;
-    }
-    if (after.hasAttribute(attribute.key)) {
-      if (retain != null) retain += after.length!;
-    }
-    if (retain == 0) {
-      return null;
-    }
-
-    delta
-      ..retain(beg)
-      ..retain(retain!, attribute.toJson());
-    return delta;
-  }
-}
-
-class ResolveInlineFormatRule extends FormatRule {
-  const ResolveInlineFormatRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (attribute!.scope != AttributeScope.INLINE) {
-      return null;
-    }
-
-    final delta = Delta()..retain(index);
-    final itr = DeltaIterator(document)..skip(index);
-
-    Operation op;
-    for (var cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
-      op = itr.next(len - cur);
-      final text = op.data is String ? (op.data as String?)! : '';
-      var lineBreak = text.indexOf('\n');
-      if (lineBreak < 0) {
-        delta.retain(op.length!, attribute.toJson());
-        continue;
-      }
-      var pos = 0;
-      while (lineBreak >= 0) {
-        delta
-          ..retain(lineBreak - pos, attribute.toJson())
-          ..retain(1);
-        pos = lineBreak + 1;
-        lineBreak = text.indexOf('\n', pos);
-      }
-      if (pos < op.length!) {
-        delta.retain(op.length! - pos, attribute.toJson());
-      }
-    }
-
-    return delta;
-  }
-}

+ 0 - 385
app_flowy/packages/editor/lib/src/models/rules/insert.dart

@@ -1,385 +0,0 @@
-import 'package:tuple/tuple.dart';
-
-import '../documents/attribute.dart';
-import '../documents/style.dart';
-import '../quill_delta.dart';
-import 'rule.dart';
-
-abstract class InsertRule extends Rule {
-  const InsertRule();
-
-  @override
-  RuleType get type => RuleType.INSERT;
-
-  @override
-  void validateArgs(int? len, Object? data, Attribute? attribute) {
-    assert(data != null);
-    assert(attribute == null);
-  }
-}
-
-class PreserveLineStyleOnSplitRule extends InsertRule {
-  const PreserveLineStyleOnSplitRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || data != '\n') {
-      return null;
-    }
-
-    final itr = DeltaIterator(document);
-    final before = itr.skip(index);
-    if (before == null ||
-        before.data is! String ||
-        (before.data as String).endsWith('\n')) {
-      return null;
-    }
-    final after = itr.next();
-    if (after.data is! String || (after.data as String).startsWith('\n')) {
-      return null;
-    }
-
-    final text = after.data as String;
-
-    final delta = Delta()..retain(index + (len ?? 0));
-    if (text.contains('\n')) {
-      assert(after.isPlain);
-      delta.insert('\n');
-      return delta;
-    }
-    final nextNewLine = _getNextNewLine(itr);
-    final attributes = nextNewLine.item1?.attributes;
-
-    return delta..insert('\n', attributes);
-  }
-}
-
-/// Preserves block style when user inserts text containing newlines.
-///
-/// This rule handles:
-///
-///   * inserting a new line in a block
-///   * pasting text containing multiple lines of text in a block
-///
-/// This rule may also be activated for changes triggered by auto-correct.
-class PreserveBlockStyleOnInsertRule extends InsertRule {
-  const PreserveBlockStyleOnInsertRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || !data.contains('\n')) {
-      // Only interested in text containing at least one newline character.
-      return null;
-    }
-
-    final itr = DeltaIterator(document)..skip(index);
-
-    // Look for the next newline.
-    final nextNewLine = _getNextNewLine(itr);
-    final lineStyle =
-        Style.fromJson(nextNewLine.item1?.attributes ?? <String, dynamic>{});
-
-    final blockStyle = lineStyle.getBlocksExceptHeader();
-    // Are we currently in a block? If not then ignore.
-    if (blockStyle.isEmpty) {
-      return null;
-    }
-
-    Map<String, dynamic>? resetStyle;
-    // If current line had heading style applied to it we'll need to move this
-    // style to the newly inserted line before it and reset style of the
-    // original line.
-    if (lineStyle.containsKey(Attribute.header.key)) {
-      resetStyle = Attribute.header.toJson();
-    }
-
-    // Go over each inserted line and ensure block style is applied.
-    final lines = data.split('\n');
-    final delta = Delta()..retain(index + (len ?? 0));
-    for (var i = 0; i < lines.length; i++) {
-      final line = lines[i];
-      if (line.isNotEmpty) {
-        delta.insert(line);
-      }
-      if (i == 0) {
-        // The first line should inherit the lineStyle entirely.
-        delta.insert('\n', lineStyle.toJson());
-      } else if (i < lines.length - 1) {
-        // we don't want to insert a newline after the last chunk of text, so -1
-        delta.insert('\n', blockStyle);
-      }
-    }
-
-    // Reset style of the original newline character if needed.
-    if (resetStyle != null) {
-      delta
-        ..retain(nextNewLine.item2!)
-        ..retain((nextNewLine.item1!.data as String).indexOf('\n'))
-        ..retain(1, resetStyle);
-    }
-
-    return delta;
-  }
-}
-
-/// Heuristic rule to exit current block when user inserts two consecutive
-/// newlines.
-///
-/// This rule is only applied when the cursor is on the last line of a block.
-/// When the cursor is in the middle of a block we allow adding empty lines
-/// and preserving the block's style.
-class AutoExitBlockRule extends InsertRule {
-  const AutoExitBlockRule();
-
-  bool _isEmptyLine(Operation? before, Operation? after) {
-    if (before == null) {
-      return true;
-    }
-    return before.data is String &&
-        (before.data as String).endsWith('\n') &&
-        after!.data is String &&
-        (after.data as String).startsWith('\n');
-  }
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || data != '\n') {
-      return null;
-    }
-
-    final itr = DeltaIterator(document);
-    final prev = itr.skip(index), cur = itr.next();
-    final blockStyle = Style.fromJson(cur.attributes).getBlockExceptHeader();
-    // We are not in a block, ignore.
-    if (cur.isPlain || blockStyle == null) {
-      return null;
-    }
-    // We are not on an empty line, ignore.
-    if (!_isEmptyLine(prev, cur)) {
-      return null;
-    }
-
-    // We are on an empty line. Now we need to determine if we are on the
-    // last line of a block.
-    // First check if `cur` length is greater than 1, this would indicate
-    // that it contains multiple newline characters which share the same style.
-    // This would mean we are not on the last line yet.
-    // `cur.value as String` is safe since we already called isEmptyLine and
-    // know it contains a newline
-    if ((cur.value as String).length > 1) {
-      // We are not on the last line of this block, ignore.
-      return null;
-    }
-
-    // Keep looking for the next newline character to see if it shares the same
-    // block style as `cur`.
-    final nextNewLine = _getNextNewLine(itr);
-    if (nextNewLine.item1 != null &&
-        nextNewLine.item1!.attributes != null &&
-        Style.fromJson(nextNewLine.item1!.attributes).getBlockExceptHeader() ==
-            blockStyle) {
-      // We are not at the end of this block, ignore.
-      return null;
-    }
-
-    // Here we now know that the line after `cur` is not in the same block
-    // therefore we can exit this block.
-    final attributes = cur.attributes ?? <String, dynamic>{};
-    final k =
-        attributes.keys.firstWhere(Attribute.blockKeysExceptHeader.contains);
-    attributes[k] = null;
-    // retain(1) should be '\n', set it with no attribute
-    return Delta()
-      ..retain(index + (len ?? 0))
-      ..retain(1, attributes);
-  }
-}
-
-class ResetLineFormatOnNewLineRule extends InsertRule {
-  const ResetLineFormatOnNewLineRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || data != '\n') {
-      return null;
-    }
-
-    final itr = DeltaIterator(document)..skip(index);
-    final cur = itr.next();
-    if (cur.data is! String || !(cur.data as String).startsWith('\n')) {
-      return null;
-    }
-
-    Map<String, dynamic>? resetStyle;
-    if (cur.attributes != null &&
-        cur.attributes!.containsKey(Attribute.header.key)) {
-      resetStyle = Attribute.header.toJson();
-    }
-    return Delta()
-      ..retain(index + (len ?? 0))
-      ..insert('\n', cur.attributes)
-      ..retain(1, resetStyle)
-      ..trim();
-  }
-}
-
-class InsertEmbedsRule extends InsertRule {
-  const InsertEmbedsRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is String) {
-      return null;
-    }
-
-    final delta = Delta()..retain(index + (len ?? 0));
-    final itr = DeltaIterator(document);
-    final prev = itr.skip(index), cur = itr.next();
-
-    final textBefore = prev?.data is String ? prev!.data as String? : '';
-    final textAfter = cur.data is String ? (cur.data as String?)! : '';
-
-    final isNewlineBefore = prev == null || textBefore!.endsWith('\n');
-    final isNewlineAfter = textAfter.startsWith('\n');
-
-    if (isNewlineBefore && isNewlineAfter) {
-      return delta..insert(data);
-    }
-
-    Map<String, dynamic>? lineStyle;
-    if (textAfter.contains('\n')) {
-      lineStyle = cur.attributes;
-    } else {
-      while (itr.hasNext) {
-        final op = itr.next();
-        if ((op.data is String ? op.data as String? : '')!.contains('\n')) {
-          lineStyle = op.attributes;
-          break;
-        }
-      }
-    }
-
-    if (!isNewlineBefore) {
-      delta.insert('\n', lineStyle);
-    }
-    delta.insert(data);
-    if (!isNewlineAfter) {
-      delta.insert('\n');
-    }
-    return delta;
-  }
-}
-
-class AutoFormatLinksRule extends InsertRule {
-  const AutoFormatLinksRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || data != ' ') {
-      return null;
-    }
-
-    final itr = DeltaIterator(document);
-    final prev = itr.skip(index);
-    if (prev == null || prev.data is! String) {
-      return null;
-    }
-
-    try {
-      final cand = (prev.data as String).split('\n').last.split(' ').last;
-      final link = Uri.parse(cand);
-      if (!['https', 'http'].contains(link.scheme)) {
-        return null;
-      }
-      final attributes = prev.attributes ?? <String, dynamic>{};
-
-      if (attributes.containsKey(Attribute.link.key)) {
-        return null;
-      }
-
-      attributes.addAll(LinkAttribute(link.toString()).toJson());
-      return Delta()
-        ..retain(index + (len ?? 0) - cand.length)
-        ..retain(cand.length, attributes)
-        ..insert(data, prev.attributes);
-    } on FormatException {
-      return null;
-    }
-  }
-}
-
-class PreserveInlineStylesRule extends InsertRule {
-  const PreserveInlineStylesRule();
-
-  @override
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    if (data is! String || data.contains('\n')) {
-      return null;
-    }
-
-    final itr = DeltaIterator(document);
-    final prev = itr.skip(index);
-    if (prev == null ||
-        prev.data is! String ||
-        (prev.data as String).contains('\n')) {
-      return null;
-    }
-
-    final attributes = prev.attributes;
-    final text = data;
-    if (attributes == null || !attributes.containsKey(Attribute.link.key)) {
-      return Delta()
-        ..retain(index + (len ?? 0))
-        ..insert(text, attributes);
-    }
-
-    attributes.remove(Attribute.link.key);
-    final delta = Delta()
-      ..retain(index + (len ?? 0))
-      ..insert(text, attributes.isEmpty ? null : attributes);
-    final next = itr.next();
-
-    final nextAttributes = next.attributes ?? const <String, dynamic>{};
-    if (!nextAttributes.containsKey(Attribute.link.key)) {
-      return delta;
-    }
-    if (attributes[Attribute.link.key] == nextAttributes[Attribute.link.key]) {
-      return Delta()
-        ..retain(index + (len ?? 0))
-        ..insert(text, attributes);
-    }
-    return delta;
-  }
-}
-
-class CatchAllInsertRule extends InsertRule {
-  const CatchAllInsertRule();
-
-  @override
-  Delta applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    return Delta()
-      ..retain(index + (len ?? 0))
-      ..insert(data);
-  }
-}
-
-Tuple2<Operation?, int?> _getNextNewLine(DeltaIterator iterator) {
-  Operation op;
-  for (var skipped = 0; iterator.hasNext; skipped += op.length!) {
-    op = iterator.next();
-    final lineBreak =
-        (op.data is String ? op.data as String? : '')!.indexOf('\n');
-    if (lineBreak >= 0) {
-      return Tuple2(op, skipped);
-    }
-  }
-  return const Tuple2(null, null);
-}

+ 0 - 76
app_flowy/packages/editor/lib/src/models/rules/rule.dart

@@ -1,76 +0,0 @@
-import '../documents/attribute.dart';
-import '../documents/document.dart';
-import '../quill_delta.dart';
-import 'delete.dart';
-import 'format.dart';
-import 'insert.dart';
-
-enum RuleType { INSERT, DELETE, FORMAT }
-
-abstract class Rule {
-  const Rule();
-
-  Delta? apply(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    validateArgs(len, data, attribute);
-    return applyRule(document, index,
-        len: len, data: data, attribute: attribute);
-  }
-
-  void validateArgs(int? len, Object? data, Attribute? attribute);
-
-  Delta? applyRule(Delta document, int index,
-      {int? len, Object? data, Attribute? attribute});
-
-  RuleType get type;
-}
-
-class Rules {
-  Rules(this._rules);
-
-  List<Rule> _customRules = [];
-
-  final List<Rule> _rules;
-  static final Rules _instance = Rules([
-    const FormatLinkAtCaretPositionRule(),
-    const ResolveLineFormatRule(),
-    const ResolveInlineFormatRule(),
-    const InsertEmbedsRule(),
-    const AutoExitBlockRule(),
-    const PreserveBlockStyleOnInsertRule(),
-    const PreserveLineStyleOnSplitRule(),
-    const ResetLineFormatOnNewLineRule(),
-    const AutoFormatLinksRule(),
-    const PreserveInlineStylesRule(),
-    const CatchAllInsertRule(),
-    const EnsureEmbedLineRule(),
-    const PreserveLineStyleOnMergeRule(),
-    const CatchAllDeleteRule(),
-  ]);
-
-  static Rules getInstance() => _instance;
-
-  void setCustomRules(List<Rule> customRules) {
-    _customRules = customRules;
-  }
-
-  Delta apply(RuleType ruleType, Document document, int index,
-      {int? len, Object? data, Attribute? attribute}) {
-    final delta = document.toDelta();
-    for (final rule in _customRules + _rules) {
-      if (rule.type != ruleType) {
-        continue;
-      }
-      try {
-        final result = rule.apply(delta, index,
-            len: len, data: data, attribute: attribute);
-        if (result != null) {
-          return result..trim();
-        }
-      } catch (e) {
-        rethrow;
-      }
-    }
-    throw 'Apply rules failed';
-  }
-}

+ 0 - 125
app_flowy/packages/editor/lib/src/utils/color.dart

@@ -1,125 +0,0 @@
-import 'dart:ui';
-
-import 'package:flutter/material.dart';
-
-Color stringToColor(String? s) {
-  switch (s) {
-    case 'transparent':
-      return Colors.transparent;
-    case 'black':
-      return Colors.black;
-    case 'black12':
-      return Colors.black12;
-    case 'black26':
-      return Colors.black26;
-    case 'black38':
-      return Colors.black38;
-    case 'black45':
-      return Colors.black45;
-    case 'black54':
-      return Colors.black54;
-    case 'black87':
-      return Colors.black87;
-    case 'white':
-      return Colors.white;
-    case 'white10':
-      return Colors.white10;
-    case 'white12':
-      return Colors.white12;
-    case 'white24':
-      return Colors.white24;
-    case 'white30':
-      return Colors.white30;
-    case 'white38':
-      return Colors.white38;
-    case 'white54':
-      return Colors.white54;
-    case 'white60':
-      return Colors.white60;
-    case 'white70':
-      return Colors.white70;
-    case 'red':
-      return Colors.red;
-    case 'redAccent':
-      return Colors.redAccent;
-    case 'amber':
-      return Colors.amber;
-    case 'amberAccent':
-      return Colors.amberAccent;
-    case 'yellow':
-      return Colors.yellow;
-    case 'yellowAccent':
-      return Colors.yellowAccent;
-    case 'teal':
-      return Colors.teal;
-    case 'tealAccent':
-      return Colors.tealAccent;
-    case 'purple':
-      return Colors.purple;
-    case 'purpleAccent':
-      return Colors.purpleAccent;
-    case 'pink':
-      return Colors.pink;
-    case 'pinkAccent':
-      return Colors.pinkAccent;
-    case 'orange':
-      return Colors.orange;
-    case 'orangeAccent':
-      return Colors.orangeAccent;
-    case 'deepOrange':
-      return Colors.deepOrange;
-    case 'deepOrangeAccent':
-      return Colors.deepOrangeAccent;
-    case 'indigo':
-      return Colors.indigo;
-    case 'indigoAccent':
-      return Colors.indigoAccent;
-    case 'lime':
-      return Colors.lime;
-    case 'limeAccent':
-      return Colors.limeAccent;
-    case 'grey':
-      return Colors.grey;
-    case 'blueGrey':
-      return Colors.blueGrey;
-    case 'green':
-      return Colors.green;
-    case 'greenAccent':
-      return Colors.greenAccent;
-    case 'lightGreen':
-      return Colors.lightGreen;
-    case 'lightGreenAccent':
-      return Colors.lightGreenAccent;
-    case 'blue':
-      return Colors.blue;
-    case 'blueAccent':
-      return Colors.blueAccent;
-    case 'lightBlue':
-      return Colors.lightBlue;
-    case 'lightBlueAccent':
-      return Colors.lightBlueAccent;
-    case 'cyan':
-      return Colors.cyan;
-    case 'cyanAccent':
-      return Colors.cyanAccent;
-    case 'brown':
-      return Colors.brown;
-  }
-
-  if (s!.startsWith('rgba')) {
-    s = s.substring(5); // trim left 'rgba('
-    s = s.substring(0, s.length - 1); // trim right ')'
-    final arr = s.split(',').map((e) => e.trim()).toList();
-    return Color.fromRGBO(int.parse(arr[0]), int.parse(arr[1]),
-        int.parse(arr[2]), double.parse(arr[3]));
-  }
-
-  if (!s.startsWith('#')) {
-    throw 'Color code not supported';
-  }
-
-  var hex = s.replaceFirst('#', '');
-  hex = hex.length == 6 ? 'ff$hex' : hex;
-  final val = int.parse(hex, radix: 16);
-  return Color(val);
-}

+ 0 - 103
app_flowy/packages/editor/lib/src/utils/diff_delta.dart

@@ -1,103 +0,0 @@
-import 'dart:math' as math;
-
-import '../models/quill_delta.dart';
-
-const Set<int> WHITE_SPACE = {
-  0x9,
-  0xA,
-  0xB,
-  0xC,
-  0xD,
-  0x1C,
-  0x1D,
-  0x1E,
-  0x1F,
-  0x20,
-  0xA0,
-  0x1680,
-  0x2000,
-  0x2001,
-  0x2002,
-  0x2003,
-  0x2004,
-  0x2005,
-  0x2006,
-  0x2007,
-  0x2008,
-  0x2009,
-  0x200A,
-  0x202F,
-  0x205F,
-  0x3000
-};
-
-// Diff between two texts - old text and new text
-class Diff {
-  Diff(this.start, this.deleted, this.inserted);
-
-  // Start index in old text at which changes begin.
-  final int start;
-
-  /// The deleted text
-  final String deleted;
-
-  // The inserted text
-  final String inserted;
-
-  @override
-  String toString() {
-    return 'Diff[$start, "$deleted", "$inserted"]';
-  }
-}
-
-/* Get diff operation between old text and new text */
-Diff getDiff(String oldText, String newText, int cursorPosition) {
-  var end = oldText.length;
-  final delta = newText.length - end;
-  for (final limit = math.max(0, cursorPosition - delta);
-      end > limit && oldText[end - 1] == newText[end + delta - 1];
-      end--) {}
-  var start = 0;
-  for (final startLimit = cursorPosition - math.max(0, delta);
-      start < startLimit && oldText[start] == newText[start];
-      start++) {}
-  final deleted = (start >= end) ? '' : oldText.substring(start, end);
-  final inserted = newText.substring(start, end + delta);
-  return Diff(start, deleted, inserted);
-}
-
-int getPositionDelta(Delta user, Delta actual) {
-  if (actual.isEmpty) {
-    return 0;
-  }
-
-  final userItr = DeltaIterator(user);
-  final actualItr = DeltaIterator(actual);
-  var diff = 0;
-  while (userItr.hasNext || actualItr.hasNext) {
-    final length = math.min(userItr.peekLength(), actualItr.peekLength());
-    final userOperation = userItr.next(length);
-    final actualOperation = actualItr.next(length);
-    if (userOperation.length != actualOperation.length) {
-      throw 'userOp ${userOperation.length} does not match actualOp '
-          '${actualOperation.length}';
-    }
-    if (userOperation.key == actualOperation.key) {
-      continue;
-    } else if (userOperation.isInsert && actualOperation.isRetain) {
-      diff -= userOperation.length!;
-    } else if (userOperation.isDelete && actualOperation.isRetain) {
-      diff += userOperation.length!;
-    } else if (userOperation.isRetain && actualOperation.isInsert) {
-      String? operationTxt = '';
-      if (actualOperation.data is String) {
-        operationTxt = actualOperation.data as String?;
-      }
-      if (operationTxt!.startsWith('\n')) {
-        continue;
-      }
-      diff += actualOperation.length!;
-    }
-  }
-  return diff;
-}

+ 0 - 4
app_flowy/packages/editor/lib/src/utils/media_pick_setting.dart

@@ -1,4 +0,0 @@
-enum MediaPickSetting {
-  Gallery,
-  Link,
-}

+ 0 - 16
app_flowy/packages/editor/lib/src/utils/string_helper.dart

@@ -1,16 +0,0 @@
-Map<String, String> parseKeyValuePairs(String s, Set<String> targetKeys) {
-  final result = <String, String>{};
-  final pairs = s.split(';');
-  for (final pair in pairs) {
-    final _index = pair.indexOf(':');
-    if (_index < 0) {
-      continue;
-    }
-    final _key = pair.substring(0, _index).trim();
-    if (targetKeys.contains(_key)) {
-      result[_key] = pair.substring(_index + 1).trim();
-    }
-  }
-
-  return result;
-}

+ 0 - 122
app_flowy/packages/editor/lib/src/widgets/box.dart

@@ -1,122 +0,0 @@
-import 'package:flutter/rendering.dart';
-
-import '../models/documents/nodes/container.dart';
-
-abstract class RenderContentProxyBox implements RenderBox {
-  double getPreferredLineHeight();
-
-  Offset getOffsetForCaret(TextPosition position, Rect? caretPrototype);
-
-  TextPosition getPositionForOffset(Offset offset);
-
-  double? getFullHeightForCaret(TextPosition position);
-
-  TextRange getWordBoundary(TextPosition position);
-
-  List<TextBox> getBoxesForSelection(TextSelection textSelection);
-}
-
-/// Base class for render boxes of editable content.
-///
-/// Implementations of this class usually work as a wrapper around
-/// regular (non-editable) render boxes which implement
-/// [RenderContentProxyBox].
-abstract class RenderEditableBox extends RenderBox {
-  /// The document node represented by this render box.
-  Container getContainer();
-
-  /// Returns preferred line height at specified `position` in text.
-  ///
-  /// The `position` parameter must be relative to the [node]'s content.
-  double preferredLineHeight(TextPosition position);
-
-  /// Returns the offset at which to paint the caret.
-  ///
-  /// The `position` parameter must be relative to the [node]'s content.
-  ///
-  /// Valid only after [layout].
-  Offset getOffsetForCaret(TextPosition position);
-
-  /// Returns the position within the text for the given pixel offset.
-  ///
-  /// The `offset` parameter must be local to this box coordinate system.
-  ///
-  /// Valid only after [layout].
-  TextPosition getPositionForOffset(Offset offset);
-
-  /// Returns the position relative to the [node] content
-  ///
-  /// The `position` must be within the [node] content
-  TextPosition globalToLocalPosition(TextPosition position);
-
-  /// Returns the position within the text which is on the line above the given
-  /// `position`.
-  ///
-  /// The `position` parameter must be relative to the [node] content.
-  ///
-  /// Primarily used with multi-line or soft-wrapping text.
-  ///
-  /// Can return `null` which indicates that the `position` is at the topmost
-  /// line in the text already.
-  TextPosition? getPositionAbove(TextPosition position);
-
-  /// Returns the position within the text which is on the line below the given
-  /// `position`.
-  ///
-  /// The `position` parameter must be relative to the [node] content.
-  ///
-  /// Primarily used with multi-line or soft-wrapping text.
-  ///
-  /// Can return `null` which indicates that the `position` is at the bottommost
-  /// line in the text already.
-  TextPosition? getPositionBelow(TextPosition position);
-
-  /// Returns the text range of the word at the given offset. Characters not
-  /// part of a word, such as spaces, symbols, and punctuation, have word breaks
-  /// on both sides. In such cases, this method will return a text range that
-  /// contains the given text position.
-  ///
-  /// Word boundaries are defined more precisely in Unicode Standard Annex #29
-  /// <http://www.unicode.org/reports/tr29/#Word_Boundaries>.
-  ///
-  /// The `position` parameter must be relative to the [node]'s content.
-  ///
-  /// Valid only after [layout].
-  TextRange getWordBoundary(TextPosition position);
-
-  /// Returns the text range of the line at the given offset.
-  ///
-  /// The newline, if any, is included in the range.
-  ///
-  /// The `position` parameter must be relative to the [node]'s content.
-  ///
-  /// Valid only after [layout].
-  TextRange getLineBoundary(TextPosition position);
-
-  /// Returns a list of rects that bound the given selection.
-  ///
-  /// A given selection might have more than one rect if this text painter
-  /// contains bidirectional text because logically contiguous text might not be
-  /// visually contiguous.
-  ///
-  /// Valid only after [layout].
-  // List<TextBox> getBoxesForSelection(TextSelection selection);
-
-  /// Returns a point for the base selection handle used on touch-oriented
-  /// devices.
-  ///
-  /// The `selection` parameter is expected to be in local offsets to this
-  /// render object's [node].
-  TextSelectionPoint getBaseEndpointForSelection(TextSelection textSelection);
-
-  /// Returns a point for the extent selection handle used on touch-oriented
-  /// devices.
-  ///
-  /// The `selection` parameter is expected to be in local offsets to this
-  /// render object's [node].
-  TextSelectionPoint getExtentEndpointForSelection(TextSelection textSelection);
-
-  /// Returns the [Rect] in local coordinates for the caret at the given text
-  /// position.
-  Rect getLocalRectForCaret(TextPosition position);
-}

+ 0 - 255
app_flowy/packages/editor/lib/src/widgets/controller.dart

@@ -1,255 +0,0 @@
-import 'dart:math' as math;
-
-import 'package:flutter/cupertino.dart';
-import 'package:tuple/tuple.dart';
-
-import '../models/documents/attribute.dart';
-import '../models/documents/document.dart';
-import '../models/documents/nodes/embed.dart';
-import '../models/documents/style.dart';
-import '../models/quill_delta.dart';
-import '../utils/diff_delta.dart';
-
-class QuillController extends ChangeNotifier {
-  QuillController({
-    required this.document,
-    required TextSelection selection,
-    bool keepStyleOnNewLine = false,
-  })  : _selection = selection,
-        _keepStyleOnNewLine = keepStyleOnNewLine;
-
-  factory QuillController.basic() {
-    return QuillController(
-      document: Document(),
-      selection: const TextSelection.collapsed(offset: 0),
-    );
-  }
-
-  /// Document managed by this controller.
-  final Document document;
-
-  /// Tells whether to keep or reset the [toggledStyle]
-  /// when user adds a new line.
-  final bool _keepStyleOnNewLine;
-
-  /// Currently selected text within the [document].
-  TextSelection get selection => _selection;
-  TextSelection _selection;
-
-  /// Store any styles attribute that got toggled by the tap of a button
-  /// and that has not been applied yet.
-  /// It gets reset after each format action within the [document].
-  Style toggledStyle = Style();
-
-  bool ignoreFocusOnTextChange = false;
-
-  /// True when this [QuillController] instance has been disposed.
-  ///
-  /// A safety mechanism to ensure that listeners don't crash when adding,
-  /// removing or listeners to this instance.
-  bool _isDisposed = false;
-
-  // item1: Document state before [change].
-  //
-  // item2: Change delta applied to the document.
-  //
-  // item3: The source of this change.
-  Stream<Tuple3<Delta, Delta, ChangeSource>> get changes => document.changes;
-
-  TextEditingValue get plainTextEditingValue => TextEditingValue(
-        text: document.toPlainText(),
-        selection: selection,
-      );
-
-  /// Only attributes applied to all characters within this range are
-  /// included in the result.
-  Style getSelectionStyle() {
-    return document
-        .collectStyle(selection.start, selection.end - selection.start)
-        .mergeAll(toggledStyle);
-  }
-
-  /// Returns all styles for any character within the specified text range.
-  List<Style> getAllSelectionStyles() {
-    final styles = document.collectAllStyles(
-        selection.start, selection.end - selection.start)
-      ..add(toggledStyle);
-    return styles;
-  }
-
-  void undo() {
-    final tup = document.undo();
-    if (tup.item1) {
-      _handleHistoryChange(tup.item2);
-    }
-  }
-
-  void _handleHistoryChange(int? len) {
-    if (len! != 0) {
-      // if (this.selection.extentOffset >= document.length) {
-      // // cursor exceeds the length of document, position it in the end
-      // updateSelection(
-      // TextSelection.collapsed(offset: document.length), ChangeSource.LOCAL);
-      updateSelection(
-          TextSelection.collapsed(offset: selection.baseOffset + len),
-          ChangeSource.LOCAL);
-    } else {
-      // no need to move cursor
-      notifyListeners();
-    }
-  }
-
-  void redo() {
-    final tup = document.redo();
-    if (tup.item1) {
-      _handleHistoryChange(tup.item2);
-    }
-  }
-
-  bool get hasUndo => document.hasUndo;
-
-  bool get hasRedo => document.hasRedo;
-
-  void replaceText(
-      int index, int len, Object? data, TextSelection? textSelection,
-      {bool ignoreFocus = false}) {
-    assert(data is String || data is Embeddable);
-
-    Delta? delta;
-    if (len > 0 || data is! String || data.isNotEmpty) {
-      delta = document.replace(index, len, data);
-      var shouldRetainDelta = toggledStyle.isNotEmpty &&
-          delta.isNotEmpty &&
-          delta.length <= 2 &&
-          delta.last.isInsert;
-      if (shouldRetainDelta &&
-          toggledStyle.isNotEmpty &&
-          delta.length == 2 &&
-          delta.last.data == '\n') {
-        // if all attributes are inline, shouldRetainDelta should be false
-        final anyAttributeNotInline =
-            toggledStyle.values.any((attr) => !attr.isInline);
-        if (!anyAttributeNotInline) {
-          shouldRetainDelta = false;
-        }
-      }
-      if (shouldRetainDelta) {
-        final retainDelta = Delta()
-          ..retain(index)
-          ..retain(data is String ? data.length : 1, toggledStyle.toJson());
-        document.compose(retainDelta, ChangeSource.LOCAL);
-      }
-    }
-
-    if (_keepStyleOnNewLine) {
-      final style = getSelectionStyle();
-      final notInlineStyle = style.attributes.values.where((s) => !s.isInline);
-      toggledStyle = style.removeAll(notInlineStyle.toSet());
-    } else {
-      toggledStyle = Style();
-    }
-
-    if (textSelection != null) {
-      if (delta == null || delta.isEmpty) {
-        _updateSelection(textSelection, ChangeSource.LOCAL);
-      } else {
-        final user = Delta()
-          ..retain(index)
-          ..insert(data)
-          ..delete(len);
-        final positionDelta = getPositionDelta(user, delta);
-        _updateSelection(
-          textSelection.copyWith(
-            baseOffset: textSelection.baseOffset + positionDelta,
-            extentOffset: textSelection.extentOffset + positionDelta,
-          ),
-          ChangeSource.LOCAL,
-        );
-      }
-    }
-
-    if (ignoreFocus) {
-      ignoreFocusOnTextChange = true;
-    }
-    notifyListeners();
-    ignoreFocusOnTextChange = false;
-  }
-
-  void formatText(int index, int len, Attribute? attribute) {
-    if (len == 0 &&
-        attribute!.isInline &&
-        attribute.key != Attribute.link.key) {
-      toggledStyle = toggledStyle.put(attribute);
-    }
-
-    final change = document.format(index, len, attribute);
-    final adjustedSelection = selection.copyWith(
-        baseOffset: change.transformPosition(selection.baseOffset),
-        extentOffset: change.transformPosition(selection.extentOffset));
-    if (selection != adjustedSelection) {
-      _updateSelection(adjustedSelection, ChangeSource.LOCAL);
-    }
-    notifyListeners();
-  }
-
-  void formatSelection(Attribute? attribute) {
-    formatText(selection.start, selection.end - selection.start, attribute);
-  }
-
-  void updateSelection(TextSelection textSelection, ChangeSource source) {
-    _updateSelection(textSelection, source);
-    notifyListeners();
-  }
-
-  void compose(Delta delta, TextSelection textSelection, ChangeSource source) {
-    if (delta.isNotEmpty) {
-      document.compose(delta, source);
-    }
-
-    textSelection = selection.copyWith(
-        baseOffset: delta.transformPosition(selection.baseOffset, force: false),
-        extentOffset:
-            delta.transformPosition(selection.extentOffset, force: false));
-    if (selection != textSelection) {
-      _updateSelection(textSelection, source);
-    }
-
-    notifyListeners();
-  }
-
-  @override
-  void addListener(VoidCallback listener) {
-    // By using `_isDisposed`, make sure that `addListener` won't be called on a
-    // disposed `ChangeListener`
-    if (!_isDisposed) {
-      super.addListener(listener);
-    }
-  }
-
-  @override
-  void removeListener(VoidCallback listener) {
-    // By using `_isDisposed`, make sure that `removeListener` won't be called
-    // on a disposed `ChangeListener`
-    if (!_isDisposed) {
-      super.removeListener(listener);
-    }
-  }
-
-  @override
-  void dispose() {
-    if (!_isDisposed) {
-      document.close();
-    }
-
-    _isDisposed = true;
-    super.dispose();
-  }
-
-  void _updateSelection(TextSelection textSelection, ChangeSource source) {
-    _selection = textSelection;
-    final end = document.length - 1;
-    _selection = selection.copyWith(
-        baseOffset: math.min(selection.baseOffset, end),
-        extentOffset: math.min(selection.extentOffset, end));
-  }
-}

+ 0 - 341
app_flowy/packages/editor/lib/src/widgets/cursor.dart

@@ -1,341 +0,0 @@
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
-
-import 'box.dart';
-
-/// Style properties of editing cursor.
-class CursorStyle {
-  const CursorStyle({
-    required this.color,
-    required this.backgroundColor,
-    this.width = 1.0,
-    this.height,
-    this.radius,
-    this.offset,
-    this.opacityAnimates = false,
-    this.paintAboveText = false,
-  });
-
-  /// The color to use when painting the cursor.
-  final Color color;
-
-  /// The color to use when painting the background cursor aligned with the text
-  /// while rendering the floating cursor.
-  final Color backgroundColor;
-
-  /// How thick the cursor will be.
-  ///
-  /// The cursor will draw under the text. The cursor width will extend
-  /// to the right of the boundary between characters for left-to-right text
-  /// and to the left for right-to-left text. This corresponds to extending
-  /// downstream relative to the selected position. Negative values may be used
-  /// to reverse this behavior.
-  final double width;
-
-  /// How tall the cursor will be.
-  ///
-  /// By default, the cursor height is set to the preferred line height of the
-  /// text.
-  final double? height;
-
-  /// How rounded the corners of the cursor should be.
-  ///
-  /// By default, the cursor has no radius.
-  final Radius? radius;
-
-  /// The offset that is used, in pixels, when painting the cursor on screen.
-  ///
-  /// By default, the cursor position should be set to an offset of
-  /// (-[cursorWidth] * 0.5, 0.0) on iOS platforms and (0, 0) on Android
-  /// platforms. The origin from where the offset is applied to is the arbitrary
-  /// location where the cursor ends up being rendered from by default.
-  final Offset? offset;
-
-  /// Whether the cursor will animate from fully transparent to fully opaque
-  /// during each cursor blink.
-  ///
-  /// By default, the cursor opacity will animate on iOS platforms and will not
-  /// animate on Android platforms.
-  final bool opacityAnimates;
-
-  /// If the cursor should be painted on top of the text or underneath it.
-  ///
-  /// By default, the cursor should be painted on top for iOS platforms and
-  /// underneath for Android platforms.
-  final bool paintAboveText;
-
-  @override
-  bool operator ==(Object other) =>
-      identical(this, other) ||
-      other is CursorStyle &&
-          runtimeType == other.runtimeType &&
-          color == other.color &&
-          backgroundColor == other.backgroundColor &&
-          width == other.width &&
-          height == other.height &&
-          radius == other.radius &&
-          offset == other.offset &&
-          opacityAnimates == other.opacityAnimates &&
-          paintAboveText == other.paintAboveText;
-
-  @override
-  int get hashCode =>
-      color.hashCode ^
-      backgroundColor.hashCode ^
-      width.hashCode ^
-      height.hashCode ^
-      radius.hashCode ^
-      offset.hashCode ^
-      opacityAnimates.hashCode ^
-      paintAboveText.hashCode;
-}
-
-/// Controls the cursor of an editable widget.
-///
-/// This class is a [ChangeNotifier] and allows to listen for updates on the
-/// cursor [style].
-class CursorCont extends ChangeNotifier {
-  CursorCont({
-    required this.show,
-    required CursorStyle style,
-    required TickerProvider tickerProvider,
-  })  : _style = style,
-        blink = ValueNotifier(false),
-        color = ValueNotifier(style.color) {
-    _blinkOpacityController =
-        AnimationController(vsync: tickerProvider, duration: _fadeDuration);
-    _blinkOpacityController.addListener(_onColorTick);
-  }
-
-  // The time it takes for the cursor to fade from fully opaque to fully
-  // transparent and vice versa. A full cursor blink, from transparent to opaque
-  // to transparent, is twice this duration.
-  static const Duration _blinkHalfPeriod = Duration(milliseconds: 500);
-
-  // The time the cursor is static in opacity before animating to become
-  // transparent.
-  static const Duration _blinkWaitForStart = Duration(milliseconds: 150);
-
-  // This value is an eyeball estimation of the time it takes for the iOS cursor
-  // to ease in and out.
-  static const Duration _fadeDuration = Duration(milliseconds: 250);
-
-  final ValueNotifier<bool> show;
-  final ValueNotifier<Color> color;
-  final ValueNotifier<bool> blink;
-
-  late final AnimationController _blinkOpacityController;
-
-  Timer? _cursorTimer;
-  bool _targetCursorVisibility = false;
-
-  CursorStyle _style;
-  CursorStyle get style => _style;
-  set style(CursorStyle value) {
-    if (_style == value) return;
-    _style = value;
-    notifyListeners();
-  }
-
-  /// True when this [CursorCont] instance has been disposed.
-  ///
-  /// A safety mechanism to prevent the value of a disposed controller from
-  /// getting set.
-  bool _isDisposed = false;
-
-  @override
-  void dispose() {
-    _blinkOpacityController.removeListener(_onColorTick);
-    stopCursorTimer();
-
-    _isDisposed = true;
-    _blinkOpacityController.dispose();
-    show.dispose();
-    blink.dispose();
-    color.dispose();
-    assert(_cursorTimer == null);
-    super.dispose();
-  }
-
-  void _cursorTick(Timer timer) {
-    _targetCursorVisibility = !_targetCursorVisibility;
-    final targetOpacity = _targetCursorVisibility ? 1.0 : 0.0;
-    if (style.opacityAnimates) {
-      // If we want to show the cursor, we will animate the opacity to the value
-      // of 1.0, and likewise if we want to make it disappear, to 0.0. An easing
-      // curve is used for the animation to mimic the aesthetics of the native
-      // iOS cursor.
-      //
-      // These values and curves have been obtained through eyeballing, so are
-      // likely not exactly the same as the values for native iOS.
-      _blinkOpacityController.animateTo(targetOpacity, curve: Curves.easeOut);
-    } else {
-      _blinkOpacityController.value = targetOpacity;
-    }
-  }
-
-  void _waitForStart(Timer timer) {
-    _cursorTimer?.cancel();
-    _cursorTimer = Timer.periodic(_blinkHalfPeriod, _cursorTick);
-  }
-
-  void startCursorTimer() {
-    if (_isDisposed) {
-      return;
-    }
-
-    _targetCursorVisibility = true;
-    _blinkOpacityController.value = 1.0;
-
-    if (style.opacityAnimates) {
-      _cursorTimer = Timer.periodic(_blinkWaitForStart, _waitForStart);
-    } else {
-      _cursorTimer = Timer.periodic(_blinkHalfPeriod, _cursorTick);
-    }
-  }
-
-  void stopCursorTimer({bool resetCharTicks = true}) {
-    _cursorTimer?.cancel();
-    _cursorTimer = null;
-    _targetCursorVisibility = false;
-    _blinkOpacityController.value = 0.0;
-
-    if (style.opacityAnimates) {
-      _blinkOpacityController
-        ..stop()
-        ..value = 0.0;
-    }
-  }
-
-  void startOrStopCursorTimerIfNeeded(bool hasFocus, TextSelection selection) {
-    if (show.value &&
-        _cursorTimer == null &&
-        hasFocus &&
-        selection.isCollapsed) {
-      startCursorTimer();
-    } else if (_cursorTimer != null && (!hasFocus || !selection.isCollapsed)) {
-      stopCursorTimer();
-    }
-  }
-
-  void _onColorTick() {
-    color.value = _style.color.withOpacity(_blinkOpacityController.value);
-    blink.value = show.value && _blinkOpacityController.value > 0;
-  }
-}
-
-/// Paints the editing cursor.
-class CursorPainter {
-  CursorPainter(
-    this.editable,
-    this.style,
-    this.prototype,
-    this.color,
-    this.devicePixelRatio,
-  );
-
-  final RenderContentProxyBox? editable;
-  final CursorStyle style;
-  final Rect prototype;
-  final Color color;
-  final double devicePixelRatio;
-
-  /// Paints cursor on [canvas] at specified [position].
-  /// [offset] is global top left (x, y) of text line
-  /// [position] is relative (x) in text line
-  void paint(
-      Canvas canvas, Offset offset, TextPosition position, bool lineHasEmbed) {
-    // relative (x, y) to global offset
-    var relativeCaretOffset = editable!.getOffsetForCaret(position, prototype);
-    if (lineHasEmbed && relativeCaretOffset == Offset.zero) {
-      relativeCaretOffset = editable!.getOffsetForCaret(
-          TextPosition(
-              offset: position.offset - 1, affinity: position.affinity),
-          prototype);
-      // Hardcoded 6 as estimate of the width of a character
-      relativeCaretOffset =
-          Offset(relativeCaretOffset.dx + 6, relativeCaretOffset.dy);
-    }
-
-    final caretOffset = relativeCaretOffset + offset;
-    var caretRect = prototype.shift(caretOffset);
-    if (style.offset != null) {
-      caretRect = caretRect.shift(style.offset!);
-    }
-
-    if (caretRect.left < 0.0) {
-      // For iOS the cursor may get clipped by the scroll view when
-      // it's located at a beginning of a line. We ensure that this
-      // does not happen here. This may result in the cursor being painted
-      // closer to the character on the right, but it's arguably better
-      // then painting clipped cursor (or even cursor completely hidden).
-      caretRect = caretRect.shift(Offset(-caretRect.left, 0));
-    }
-
-    final caretHeight = editable!.getFullHeightForCaret(position);
-    if (caretHeight != null) {
-      switch (defaultTargetPlatform) {
-        case TargetPlatform.android:
-        case TargetPlatform.fuchsia:
-        case TargetPlatform.linux:
-        case TargetPlatform.windows:
-          // Override the height to take the full height of the glyph at the
-          // TextPosition when not on iOS. iOS has special handling that
-          // creates a taller caret.
-          caretRect = Rect.fromLTWH(
-            caretRect.left,
-            caretRect.top - 2.0,
-            caretRect.width,
-            caretHeight,
-          );
-          break;
-        case TargetPlatform.iOS:
-        case TargetPlatform.macOS:
-          // Center the caret vertically along the text.
-          caretRect = Rect.fromLTWH(
-            caretRect.left,
-            caretRect.top + (caretHeight - caretRect.height) / 2,
-            caretRect.width,
-            caretRect.height,
-          );
-          break;
-        default:
-          throw UnimplementedError();
-      }
-    }
-
-    final pixelPerfectOffset = _getPixelPerfectCursorOffset(caretRect);
-    if (!pixelPerfectOffset.isFinite) {
-      return;
-    }
-    caretRect = caretRect.shift(pixelPerfectOffset);
-
-    final paint = Paint()..color = color;
-    if (style.radius == null) {
-      canvas.drawRect(caretRect, paint);
-    } else {
-      final caretRRect = RRect.fromRectAndRadius(caretRect, style.radius!);
-      canvas.drawRRect(caretRRect, paint);
-    }
-  }
-
-  Offset _getPixelPerfectCursorOffset(
-    Rect caretRect,
-  ) {
-    final caretPosition = editable!.localToGlobal(caretRect.topLeft);
-    final pixelMultiple = 1.0 / devicePixelRatio;
-
-    final pixelPerfectOffsetX = caretPosition.dx.isFinite
-        ? (caretPosition.dx / pixelMultiple).round() * pixelMultiple -
-            caretPosition.dx
-        : caretPosition.dx;
-    final pixelPerfectOffsetY = caretPosition.dy.isFinite
-        ? (caretPosition.dy / pixelMultiple).round() * pixelMultiple -
-            caretPosition.dy
-        : caretPosition.dy;
-
-    return Offset(pixelPerfectOffsetX, pixelPerfectOffsetY);
-  }
-}

+ 0 - 235
app_flowy/packages/editor/lib/src/widgets/default_styles.dart

@@ -1,235 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-import 'package:tuple/tuple.dart';
-
-class QuillStyles extends InheritedWidget {
-  const QuillStyles({
-    required this.data,
-    required Widget child,
-    Key? key,
-  }) : super(key: key, child: child);
-
-  final DefaultStyles data;
-
-  @override
-  bool updateShouldNotify(QuillStyles oldWidget) {
-    return data != oldWidget.data;
-  }
-
-  static DefaultStyles? getStyles(BuildContext context, bool nullOk) {
-    final widget = context.dependOnInheritedWidgetOfExactType<QuillStyles>();
-    if (widget == null && nullOk) {
-      return null;
-    }
-    assert(widget != null);
-    return widget!.data;
-  }
-}
-
-class DefaultTextBlockStyle {
-  DefaultTextBlockStyle(
-    this.style,
-    this.verticalSpacing,
-    this.lineSpacing,
-    this.decoration,
-  );
-
-  final TextStyle style;
-
-  final Tuple2<double, double> verticalSpacing;
-
-  final Tuple2<double, double> lineSpacing;
-
-  final BoxDecoration? decoration;
-}
-
-class DefaultStyles {
-  DefaultStyles({
-    this.h1,
-    this.h2,
-    this.h3,
-    this.paragraph,
-    this.bold,
-    this.italic,
-    this.small,
-    this.underline,
-    this.strikeThrough,
-    this.inlineCode,
-    this.link,
-    this.color,
-    this.placeHolder,
-    this.lists,
-    this.quote,
-    this.code,
-    this.indent,
-    this.align,
-    this.leading,
-    this.sizeSmall,
-    this.sizeLarge,
-    this.sizeHuge,
-  });
-
-  final DefaultTextBlockStyle? h1;
-  final DefaultTextBlockStyle? h2;
-  final DefaultTextBlockStyle? h3;
-  final DefaultTextBlockStyle? paragraph;
-  final TextStyle? bold;
-  final TextStyle? italic;
-  final TextStyle? small;
-  final TextStyle? underline;
-  final TextStyle? strikeThrough;
-  final TextStyle? inlineCode;
-  final TextStyle? sizeSmall; // 'small'
-  final TextStyle? sizeLarge; // 'large'
-  final TextStyle? sizeHuge; // 'huge'
-  final TextStyle? link;
-  final Color? color;
-  final DefaultTextBlockStyle? placeHolder;
-  final DefaultTextBlockStyle? lists;
-  final DefaultTextBlockStyle? quote;
-  final DefaultTextBlockStyle? code;
-  final DefaultTextBlockStyle? indent;
-  final DefaultTextBlockStyle? align;
-  final DefaultTextBlockStyle? leading;
-
-  static DefaultStyles getInstance(BuildContext context) {
-    final themeData = Theme.of(context);
-    final defaultTextStyle = DefaultTextStyle.of(context);
-    final baseStyle = defaultTextStyle.style.copyWith(
-      fontSize: 16,
-      height: 1.3,
-    );
-    const baseSpacing = Tuple2<double, double>(6, 0);
-    String fontFamily;
-    switch (themeData.platform) {
-      case TargetPlatform.iOS:
-      case TargetPlatform.macOS:
-        fontFamily = 'Menlo';
-        break;
-      case TargetPlatform.android:
-      case TargetPlatform.fuchsia:
-      case TargetPlatform.windows:
-      case TargetPlatform.linux:
-        fontFamily = 'Roboto Mono';
-        break;
-      default:
-        throw UnimplementedError();
-    }
-
-    return DefaultStyles(
-        h1: DefaultTextBlockStyle(
-            defaultTextStyle.style.copyWith(
-              fontSize: 34,
-              color: defaultTextStyle.style.color!.withOpacity(0.70),
-              height: 1.15,
-              fontWeight: FontWeight.w300,
-            ),
-            const Tuple2(16, 0),
-            const Tuple2(0, 0),
-            null),
-        h2: DefaultTextBlockStyle(
-            defaultTextStyle.style.copyWith(
-              fontSize: 24,
-              color: defaultTextStyle.style.color!.withOpacity(0.70),
-              height: 1.15,
-              fontWeight: FontWeight.normal,
-            ),
-            const Tuple2(8, 0),
-            const Tuple2(0, 0),
-            null),
-        h3: DefaultTextBlockStyle(
-            defaultTextStyle.style.copyWith(
-              fontSize: 20,
-              color: defaultTextStyle.style.color!.withOpacity(0.70),
-              height: 1.25,
-              fontWeight: FontWeight.w500,
-            ),
-            const Tuple2(8, 0),
-            const Tuple2(0, 0),
-            null),
-        paragraph: DefaultTextBlockStyle(
-            baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
-        bold: const TextStyle(fontWeight: FontWeight.bold),
-        italic: const TextStyle(fontStyle: FontStyle.italic),
-        small: const TextStyle(fontSize: 12, color: Colors.black45),
-        underline: const TextStyle(decoration: TextDecoration.underline),
-        strikeThrough: const TextStyle(decoration: TextDecoration.lineThrough),
-        inlineCode: TextStyle(
-          color: Colors.blue.shade900.withOpacity(0.9),
-          fontFamily: fontFamily,
-          fontSize: 13,
-        ),
-        link: TextStyle(
-          color: themeData.colorScheme.secondary,
-          decoration: TextDecoration.underline,
-        ),
-        placeHolder: DefaultTextBlockStyle(
-            defaultTextStyle.style.copyWith(
-              fontSize: 20,
-              height: 1.5,
-              color: Colors.grey.withOpacity(0.6),
-            ),
-            const Tuple2(0, 0),
-            const Tuple2(0, 0),
-            null),
-        lists: DefaultTextBlockStyle(
-            baseStyle, baseSpacing, const Tuple2(0, 6), null),
-        quote: DefaultTextBlockStyle(
-            TextStyle(color: baseStyle.color!.withOpacity(0.6)),
-            baseSpacing,
-            const Tuple2(6, 2),
-            BoxDecoration(
-              border: Border(
-                left: BorderSide(width: 4, color: Colors.grey.shade300),
-              ),
-            )),
-        code: DefaultTextBlockStyle(
-            TextStyle(
-              color: Colors.blue.shade900.withOpacity(0.9),
-              fontFamily: fontFamily,
-              fontSize: 13,
-              height: 1.15,
-            ),
-            baseSpacing,
-            const Tuple2(0, 0),
-            BoxDecoration(
-              color: Colors.grey.shade50,
-              borderRadius: BorderRadius.circular(2),
-            )),
-        indent: DefaultTextBlockStyle(
-            baseStyle, baseSpacing, const Tuple2(0, 6), null),
-        align: DefaultTextBlockStyle(
-            baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
-        leading: DefaultTextBlockStyle(
-            baseStyle, const Tuple2(0, 0), const Tuple2(0, 0), null),
-        sizeSmall: const TextStyle(fontSize: 10),
-        sizeLarge: const TextStyle(fontSize: 18),
-        sizeHuge: const TextStyle(fontSize: 22));
-  }
-
-  DefaultStyles merge(DefaultStyles other) {
-    return DefaultStyles(
-        h1: other.h1 ?? h1,
-        h2: other.h2 ?? h2,
-        h3: other.h3 ?? h3,
-        paragraph: other.paragraph ?? paragraph,
-        bold: other.bold ?? bold,
-        italic: other.italic ?? italic,
-        small: other.small ?? small,
-        underline: other.underline ?? underline,
-        strikeThrough: other.strikeThrough ?? strikeThrough,
-        inlineCode: other.inlineCode ?? inlineCode,
-        link: other.link ?? link,
-        color: other.color ?? color,
-        placeHolder: other.placeHolder ?? placeHolder,
-        lists: other.lists ?? lists,
-        quote: other.quote ?? quote,
-        code: other.code ?? code,
-        indent: other.indent ?? indent,
-        align: other.align ?? align,
-        leading: other.leading ?? leading,
-        sizeSmall: other.sizeSmall ?? sizeSmall,
-        sizeLarge: other.sizeLarge ?? sizeLarge,
-        sizeHuge: other.sizeHuge ?? sizeHuge);
-  }
-}

+ 0 - 152
app_flowy/packages/editor/lib/src/widgets/delegate.dart

@@ -1,152 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/rendering.dart';
-import '../../flutter_quill.dart';
-
-import '../models/documents/nodes/leaf.dart';
-import 'editor.dart';
-import 'text_selection.dart';
-
-typedef EmbedBuilder = Widget Function(
-    BuildContext context, Embed node, bool readOnly);
-
-typedef CustomStyleBuilder = TextStyle Function(Attribute attribute);
-
-abstract class EditorTextSelectionGestureDetectorBuilderDelegate {
-  GlobalKey<EditorState> getEditableTextKey();
-
-  bool getForcePressEnabled();
-
-  bool getSelectionEnabled();
-}
-
-class EditorTextSelectionGestureDetectorBuilder {
-  EditorTextSelectionGestureDetectorBuilder(this.delegate);
-
-  final EditorTextSelectionGestureDetectorBuilderDelegate delegate;
-  bool shouldShowSelectionToolbar = true;
-
-  EditorState? getEditor() {
-    return delegate.getEditableTextKey().currentState;
-  }
-
-  RenderEditor? getRenderEditor() {
-    return getEditor()!.getRenderEditor();
-  }
-
-  void onTapDown(TapDownDetails details) {
-    getRenderEditor()!.handleTapDown(details);
-
-    final kind = details.kind;
-    shouldShowSelectionToolbar = kind == null ||
-        kind == PointerDeviceKind.touch ||
-        kind == PointerDeviceKind.stylus;
-  }
-
-  void onForcePressStart(ForcePressDetails details) {
-    assert(delegate.getForcePressEnabled());
-    shouldShowSelectionToolbar = true;
-    if (delegate.getSelectionEnabled()) {
-      getRenderEditor()!.selectWordsInRange(
-        details.globalPosition,
-        null,
-        SelectionChangedCause.forcePress,
-      );
-    }
-  }
-
-  void onForcePressEnd(ForcePressDetails details) {
-    assert(delegate.getForcePressEnabled());
-    getRenderEditor()!.selectWordsInRange(
-      details.globalPosition,
-      null,
-      SelectionChangedCause.forcePress,
-    );
-    if (shouldShowSelectionToolbar) {
-      getEditor()!.showToolbar();
-    }
-  }
-
-  void onSingleTapUp(TapUpDetails details) {
-    if (delegate.getSelectionEnabled()) {
-      getRenderEditor()!.selectWordEdge(SelectionChangedCause.tap);
-    }
-  }
-
-  void onSingleTapCancel() {}
-
-  void onSingleLongTapStart(LongPressStartDetails details) {
-    if (delegate.getSelectionEnabled()) {
-      getRenderEditor()!.selectPositionAt(
-        details.globalPosition,
-        null,
-        SelectionChangedCause.longPress,
-      );
-    }
-  }
-
-  void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
-    if (delegate.getSelectionEnabled()) {
-      getRenderEditor()!.selectPositionAt(
-        details.globalPosition,
-        null,
-        SelectionChangedCause.longPress,
-      );
-    }
-  }
-
-  void onSingleLongTapEnd(LongPressEndDetails details) {
-    if (shouldShowSelectionToolbar) {
-      getEditor()!.showToolbar();
-    }
-  }
-
-  void onDoubleTapDown(TapDownDetails details) {
-    if (delegate.getSelectionEnabled()) {
-      getRenderEditor()!.selectWord(SelectionChangedCause.tap);
-      if (shouldShowSelectionToolbar) {
-        getEditor()!.showToolbar();
-      }
-    }
-  }
-
-  void onDragSelectionStart(DragStartDetails details) {
-    getRenderEditor()!.selectPositionAt(
-      details.globalPosition,
-      null,
-      SelectionChangedCause.drag,
-    );
-  }
-
-  void onDragSelectionUpdate(
-      DragStartDetails startDetails, DragUpdateDetails updateDetails) {
-    getRenderEditor()!.selectPositionAt(
-      startDetails.globalPosition,
-      updateDetails.globalPosition,
-      SelectionChangedCause.drag,
-    );
-  }
-
-  void onDragSelectionEnd(DragEndDetails details) {}
-
-  Widget build(HitTestBehavior behavior, Widget child) {
-    return EditorTextSelectionGestureDetector(
-      onTapDown: onTapDown,
-      onForcePressStart:
-          delegate.getForcePressEnabled() ? onForcePressStart : null,
-      onForcePressEnd: delegate.getForcePressEnabled() ? onForcePressEnd : null,
-      onSingleTapUp: onSingleTapUp,
-      onSingleTapCancel: onSingleTapCancel,
-      onSingleLongTapStart: onSingleLongTapStart,
-      onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate,
-      onSingleLongTapEnd: onSingleLongTapEnd,
-      onDoubleTapDown: onDoubleTapDown,
-      onDragSelectionStart: onDragSelectionStart,
-      onDragSelectionUpdate: onDragSelectionUpdate,
-      onDragSelectionEnd: onDragSelectionEnd,
-      behavior: behavior,
-      child: child,
-    );
-  }
-}

+ 0 - 1289
app_flowy/packages/editor/lib/src/widgets/editor.dart

@@ -1,1289 +0,0 @@
-import 'dart:convert';
-import 'dart:io' as io;
-import 'dart:math' as math;
-
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/rendering.dart';
-import 'package:flutter/services.dart';
-import 'package:string_validator/string_validator.dart';
-import 'package:url_launcher/url_launcher.dart';
-
-import '../models/documents/attribute.dart';
-import '../models/documents/document.dart';
-import '../models/documents/nodes/container.dart' as container_node;
-import '../models/documents/nodes/embed.dart';
-import '../models/documents/nodes/leaf.dart' as leaf;
-import '../models/documents/nodes/line.dart';
-import '../utils/string_helper.dart';
-import 'box.dart';
-import 'controller.dart';
-import 'cursor.dart';
-import 'default_styles.dart';
-import 'delegate.dart';
-import 'image.dart';
-import 'raw_editor.dart';
-import 'text_selection.dart';
-import 'video_app.dart';
-import 'youtube_video_app.dart';
-
-const linkPrefixes = [
-  'mailto:', // email
-  'tel:', // telephone
-  'sms:', // SMS
-  'callto:',
-  'wtai:',
-  'market:',
-  'geopoint:',
-  'ymsgr:',
-  'msnim:',
-  'gtalk:', // Google Talk
-  'skype:',
-  'sip:', // Lync
-  'whatsapp:',
-  'http'
-];
-
-abstract class EditorState extends State<RawEditor> {
-  ScrollController get scrollController;
-
-  TextEditingValue getTextEditingValue();
-
-  void setTextEditingValue(TextEditingValue value);
-
-  RenderEditor? getRenderEditor();
-
-  EditorTextSelectionOverlay? getSelectionOverlay();
-
-  bool showToolbar();
-
-  void hideToolbar();
-
-  void requestKeyboard();
-}
-
-/// Base interface for editable render objects.
-abstract class RenderAbstractEditor {
-  TextSelection selectWordAtPosition(TextPosition position);
-
-  TextSelection selectLineAtPosition(TextPosition position);
-
-  /// Returns preferred line height at specified `position` in text.
-  double preferredLineHeight(TextPosition position);
-
-  /// Returns [Rect] for caret in local coordinates
-  ///
-  /// Useful to enforce visibility of full caret at given position
-  Rect getLocalRectForCaret(TextPosition position);
-
-  /// Returns the local coordinates of the endpoints of the given selection.
-  ///
-  /// If the selection is collapsed (and therefore occupies a single point), the
-  /// returned list is of length one. Otherwise, the selection is not collapsed
-  /// and the returned list is of length two. In this case, however, the two
-  /// points might actually be co-located (e.g., because of a bidirectional
-  /// selection that contains some text but whose ends meet in the middle).
-  TextPosition getPositionForOffset(Offset offset);
-
-  List<TextSelectionPoint> getEndpointsForSelection(
-      TextSelection textSelection);
-
-  /// If [ignorePointer] is false (the default) then this method is called by
-  /// the internal gesture recognizer's [TapGestureRecognizer.onTapDown]
-  /// callback.
-  ///
-  /// When [ignorePointer] is true, an ancestor widget must respond to tap
-  /// down events by calling this method.
-  void handleTapDown(TapDownDetails details);
-
-  /// Selects the set words of a paragraph in a given range of global positions.
-  ///
-  /// The first and last endpoints of the selection will always be at the
-  /// beginning and end of a word respectively.
-  ///
-  /// {@macro flutter.rendering.editable.select}
-  void selectWordsInRange(
-    Offset from,
-    Offset to,
-    SelectionChangedCause cause,
-  );
-
-  /// Move the selection to the beginning or end of a word.
-  ///
-  /// {@macro flutter.rendering.editable.select}
-  void selectWordEdge(SelectionChangedCause cause);
-
-  /// Select text between the global positions [from] and [to].
-  void selectPositionAt(Offset from, Offset to, SelectionChangedCause cause);
-
-  /// Select a word around the location of the last tap down.
-  ///
-  /// {@macro flutter.rendering.editable.select}
-  void selectWord(SelectionChangedCause cause);
-
-  /// Move selection to the location of the last tap down.
-  ///
-  /// {@template flutter.rendering.editable.select}
-  /// This method is mainly used to translate user inputs in global positions
-  /// into a [TextSelection]. When used in conjunction with a [EditableText],
-  /// the selection change is fed back into [TextEditingController.selection].
-  ///
-  /// If you have a [TextEditingController], it's generally easier to
-  /// programmatically manipulate its `value` or `selection` directly.
-  /// {@endtemplate}
-  void selectPosition(SelectionChangedCause cause);
-}
-
-String _standardizeImageUrl(String url) {
-  if (url.contains('base64')) {
-    return url.split(',')[1];
-  }
-  return url;
-}
-
-bool _isMobile() => io.Platform.isAndroid || io.Platform.isIOS;
-
-Widget _defaultEmbedBuilder(
-    BuildContext context, leaf.Embed node, bool readOnly) {
-  assert(!kIsWeb, 'Please provide EmbedBuilder for Web');
-  switch (node.value.type) {
-    case 'image':
-      final imageUrl = _standardizeImageUrl(node.value.data);
-
-      final style = node.style.attributes['style'];
-      if (_isMobile() && style != null) {
-        final _attrs = parseKeyValuePairs(style.value.toString(),
-            {'mobileWidth', 'mobileHeight', 'mobileMargin', 'mobileAlignment'});
-        if (_attrs.isNotEmpty) {
-          assert(
-              _attrs['mobileWidth'] != null && _attrs['mobileHeight'] != null,
-              'mobileWidth and mobileHeight must be specified');
-          final w = double.parse(_attrs['mobileWidth']!);
-          final h = double.parse(_attrs['mobileHeight']!);
-          final m = _attrs['mobileMargin'] == null
-              ? 0.0
-              : double.parse(_attrs['mobileMargin']!);
-          var a = Alignment.center;
-          if (_attrs['mobileAlignment'] != null) {
-            final _index = [
-              'topLeft',
-              'topCenter',
-              'topRight',
-              'centerLeft',
-              'center',
-              'centerRight',
-              'bottomLeft',
-              'bottomCenter',
-              'bottomRight'
-            ].indexOf(_attrs['mobileAlignment']!);
-            if (_index >= 0) {
-              a = [
-                Alignment.topLeft,
-                Alignment.topCenter,
-                Alignment.topRight,
-                Alignment.centerLeft,
-                Alignment.center,
-                Alignment.centerRight,
-                Alignment.bottomLeft,
-                Alignment.bottomCenter,
-                Alignment.bottomRight
-              ][_index];
-            }
-          }
-          return Padding(
-              padding: EdgeInsets.all(m),
-              child: imageUrl.startsWith('http')
-                  ? Image.network(imageUrl, width: w, height: h, alignment: a)
-                  : isBase64(imageUrl)
-                      ? Image.memory(base64.decode(imageUrl),
-                          width: w, height: h, alignment: a)
-                      : Image.file(io.File(imageUrl),
-                          width: w, height: h, alignment: a));
-        }
-      }
-      return imageUrl.startsWith('http')
-          ? Image.network(imageUrl)
-          : isBase64(imageUrl)
-              ? Image.memory(base64.decode(imageUrl))
-              : Image.file(io.File(imageUrl));
-    case 'video':
-      final videoUrl = node.value.data;
-      if (videoUrl.contains('youtube.com') || videoUrl.contains('youtu.be')) {
-        return YoutubeVideoApp(
-            videoUrl: videoUrl, context: context, readOnly: readOnly);
-      }
-      return VideoApp(videoUrl: videoUrl, context: context, readOnly: readOnly);
-    default:
-      throw UnimplementedError(
-        'Embeddable type "${node.value.type}" is not supported by default '
-        'embed builder of QuillEditor. You must pass your own builder function '
-        'to embedBuilder property of QuillEditor or QuillField widgets.',
-      );
-  }
-}
-
-class QuillEditor extends StatefulWidget {
-  const QuillEditor(
-      {required this.controller,
-      required this.focusNode,
-      required this.scrollController,
-      required this.scrollable,
-      required this.padding,
-      required this.autoFocus,
-      required this.readOnly,
-      required this.expands,
-      this.showCursor,
-      this.paintCursorAboveText,
-      this.placeholder,
-      this.enableInteractiveSelection = true,
-      this.scrollBottomInset = 0,
-      this.minHeight,
-      this.maxHeight,
-      this.customStyles,
-      this.textCapitalization = TextCapitalization.sentences,
-      this.keyboardAppearance = Brightness.light,
-      this.scrollPhysics,
-      this.onLaunchUrl,
-      this.onTapDown,
-      this.onTapUp,
-      this.onSingleLongTapStart,
-      this.onSingleLongTapMoveUpdate,
-      this.onSingleLongTapEnd,
-      this.embedBuilder = _defaultEmbedBuilder,
-      this.customStyleBuilder,
-      Key? key});
-
-  factory QuillEditor.basic({
-    required QuillController controller,
-    required bool readOnly,
-  }) {
-    return QuillEditor(
-        controller: controller,
-        scrollController: ScrollController(),
-        scrollable: true,
-        focusNode: FocusNode(),
-        autoFocus: true,
-        readOnly: readOnly,
-        expands: false,
-        padding: EdgeInsets.zero);
-  }
-
-  final QuillController controller;
-  final FocusNode focusNode;
-  final ScrollController scrollController;
-  final bool scrollable;
-  final double scrollBottomInset;
-  final EdgeInsetsGeometry padding;
-  final bool autoFocus;
-  final bool? showCursor;
-  final bool? paintCursorAboveText;
-  final bool readOnly;
-  final String? placeholder;
-  final bool enableInteractiveSelection;
-  final double? minHeight;
-  final double? maxHeight;
-  final DefaultStyles? customStyles;
-  final bool expands;
-  final TextCapitalization textCapitalization;
-  final Brightness keyboardAppearance;
-  final ScrollPhysics? scrollPhysics;
-  final ValueChanged<String>? onLaunchUrl;
-  // Returns whether gesture is handled
-  final bool Function(
-      TapDownDetails details, TextPosition Function(Offset offset))? onTapDown;
-
-  // Returns whether gesture is handled
-  final bool Function(
-      TapUpDetails details, TextPosition Function(Offset offset))? onTapUp;
-
-  // Returns whether gesture is handled
-  final bool Function(
-          LongPressStartDetails details, TextPosition Function(Offset offset))?
-      onSingleLongTapStart;
-
-  // Returns whether gesture is handled
-  final bool Function(LongPressMoveUpdateDetails details,
-      TextPosition Function(Offset offset))? onSingleLongTapMoveUpdate;
-  // Returns whether gesture is handled
-  final bool Function(
-          LongPressEndDetails details, TextPosition Function(Offset offset))?
-      onSingleLongTapEnd;
-
-  final EmbedBuilder embedBuilder;
-  final CustomStyleBuilder? customStyleBuilder;
-
-  @override
-  _QuillEditorState createState() => _QuillEditorState();
-}
-
-class _QuillEditorState extends State<QuillEditor>
-    implements EditorTextSelectionGestureDetectorBuilderDelegate {
-  final GlobalKey<EditorState> _editorKey = GlobalKey<EditorState>();
-  late EditorTextSelectionGestureDetectorBuilder
-      _selectionGestureDetectorBuilder;
-
-  @override
-  void initState() {
-    super.initState();
-    _selectionGestureDetectorBuilder =
-        _QuillEditorSelectionGestureDetectorBuilder(this);
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    final selectionTheme = TextSelectionTheme.of(context);
-
-    TextSelectionControls textSelectionControls;
-    bool paintCursorAboveText;
-    bool cursorOpacityAnimates;
-    Offset? cursorOffset;
-    Color? cursorColor;
-    Color selectionColor;
-    Radius? cursorRadius;
-
-    switch (theme.platform) {
-      case TargetPlatform.android:
-      case TargetPlatform.fuchsia:
-      case TargetPlatform.linux:
-      case TargetPlatform.windows:
-        textSelectionControls = materialTextSelectionControls;
-        paintCursorAboveText = false;
-        cursorOpacityAnimates = false;
-        cursorColor ??= selectionTheme.cursorColor ?? theme.colorScheme.primary;
-        selectionColor = selectionTheme.selectionColor ??
-            theme.colorScheme.primary.withOpacity(0.40);
-        break;
-      case TargetPlatform.iOS:
-      case TargetPlatform.macOS:
-        final cupertinoTheme = CupertinoTheme.of(context);
-        textSelectionControls = cupertinoTextSelectionControls;
-        paintCursorAboveText = true;
-        cursorOpacityAnimates = true;
-        cursorColor ??=
-            selectionTheme.cursorColor ?? cupertinoTheme.primaryColor;
-        selectionColor = selectionTheme.selectionColor ??
-            cupertinoTheme.primaryColor.withOpacity(0.40);
-        cursorRadius ??= const Radius.circular(2);
-        cursorOffset = Offset(
-            iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0);
-        break;
-      default:
-        throw UnimplementedError();
-    }
-
-    return _selectionGestureDetectorBuilder.build(
-      HitTestBehavior.translucent,
-      RawEditor(
-        _editorKey,
-        widget.controller,
-        widget.focusNode,
-        widget.scrollController,
-        widget.scrollable,
-        widget.scrollBottomInset,
-        widget.padding,
-        widget.readOnly,
-        widget.placeholder,
-        widget.onLaunchUrl,
-        ToolbarOptions(
-          copy: widget.enableInteractiveSelection,
-          cut: widget.enableInteractiveSelection,
-          paste: widget.enableInteractiveSelection,
-          selectAll: widget.enableInteractiveSelection,
-        ),
-        theme.platform == TargetPlatform.iOS ||
-            theme.platform == TargetPlatform.android,
-        widget.showCursor,
-        CursorStyle(
-          color: cursorColor,
-          backgroundColor: Colors.grey,
-          width: 2,
-          radius: cursorRadius,
-          offset: cursorOffset,
-          paintAboveText: widget.paintCursorAboveText ?? paintCursorAboveText,
-          opacityAnimates: cursorOpacityAnimates,
-        ),
-        widget.textCapitalization,
-        widget.maxHeight,
-        widget.minHeight,
-        widget.customStyles,
-        widget.expands,
-        widget.autoFocus,
-        selectionColor,
-        textSelectionControls,
-        widget.keyboardAppearance,
-        widget.enableInteractiveSelection,
-        widget.scrollPhysics,
-        widget.embedBuilder,
-        widget.customStyleBuilder,
-      ),
-    );
-  }
-
-  @override
-  GlobalKey<EditorState> getEditableTextKey() {
-    return _editorKey;
-  }
-
-  @override
-  bool getForcePressEnabled() {
-    return false;
-  }
-
-  @override
-  bool getSelectionEnabled() {
-    return widget.enableInteractiveSelection;
-  }
-
-  void _requestKeyboard() {
-    _editorKey.currentState!.requestKeyboard();
-  }
-}
-
-class _QuillEditorSelectionGestureDetectorBuilder
-    extends EditorTextSelectionGestureDetectorBuilder {
-  _QuillEditorSelectionGestureDetectorBuilder(this._state) : super(_state);
-
-  final _QuillEditorState _state;
-
-  @override
-  void onForcePressStart(ForcePressDetails details) {
-    super.onForcePressStart(details);
-    if (delegate.getSelectionEnabled() && shouldShowSelectionToolbar) {
-      getEditor()!.showToolbar();
-    }
-  }
-
-  @override
-  void onForcePressEnd(ForcePressDetails details) {}
-
-  @override
-  void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
-    if (_state.widget.onSingleLongTapMoveUpdate != null) {
-      final renderEditor = getRenderEditor();
-      if (renderEditor != null) {
-        if (_state.widget.onSingleLongTapMoveUpdate!(
-            details, renderEditor.getPositionForOffset)) {
-          return;
-        }
-      }
-    }
-    if (!delegate.getSelectionEnabled()) {
-      return;
-    }
-    switch (Theme.of(_state.context).platform) {
-      case TargetPlatform.iOS:
-      case TargetPlatform.macOS:
-        getRenderEditor()!.selectPositionAt(
-          details.globalPosition,
-          null,
-          SelectionChangedCause.longPress,
-        );
-        break;
-      case TargetPlatform.android:
-      case TargetPlatform.fuchsia:
-      case TargetPlatform.linux:
-      case TargetPlatform.windows:
-        getRenderEditor()!.selectWordsInRange(
-          details.globalPosition - details.offsetFromOrigin,
-          details.globalPosition,
-          SelectionChangedCause.longPress,
-        );
-        break;
-      default:
-        throw 'Invalid platform';
-    }
-  }
-
-  bool _onTapping(TapUpDetails details) {
-    if (_state.widget.controller.document.isEmpty()) {
-      return false;
-    }
-    final pos = getRenderEditor()!.getPositionForOffset(details.globalPosition);
-    final result =
-        getEditor()!.widget.controller.document.queryChild(pos.offset);
-    if (result.node == null) {
-      return false;
-    }
-    final line = result.node as Line;
-    final segmentResult = line.queryChild(result.offset, false);
-    if (segmentResult.node == null) {
-      if (line.length == 1) {
-        getEditor()!.widget.controller.updateSelection(
-            TextSelection.collapsed(offset: pos.offset), ChangeSource.LOCAL);
-        return true;
-      }
-      return false;
-    }
-    final segment = segmentResult.node as leaf.Leaf;
-    if (segment.style.containsKey(Attribute.link.key)) {
-      var launchUrl = getEditor()!.widget.onLaunchUrl;
-      launchUrl ??= _launchUrl;
-      String? link = segment.style.attributes[Attribute.link.key]!.value;
-      if (getEditor()!.widget.readOnly && link != null) {
-        link = link.trim();
-        if (!linkPrefixes
-            .any((linkPrefix) => link!.toLowerCase().startsWith(linkPrefix))) {
-          link = 'https://$link';
-        }
-        launchUrl(link);
-      }
-      return false;
-    }
-    if (getEditor()!.widget.readOnly && segment.value is BlockEmbed) {
-      final blockEmbed = segment.value as BlockEmbed;
-      if (blockEmbed.type == 'image') {
-        final imageUrl = _standardizeImageUrl(blockEmbed.data);
-        Navigator.push(
-          getEditor()!.context,
-          MaterialPageRoute(
-            builder: (context) => ImageTapWrapper(
-              imageProvider: imageUrl.startsWith('http')
-                  ? NetworkImage(imageUrl)
-                  : isBase64(imageUrl)
-                      ? Image.memory(base64.decode(imageUrl))
-                          as ImageProvider<Object>?
-                      : FileImage(io.File(imageUrl)),
-            ),
-          ),
-        );
-      }
-    }
-
-    return false;
-  }
-
-  Future<void> _launchUrl(String url) async {
-    await launch(url);
-  }
-
-  @override
-  void onTapDown(TapDownDetails details) {
-    if (_state.widget.onTapDown != null) {
-      final renderEditor = getRenderEditor();
-      if (renderEditor != null) {
-        if (_state.widget.onTapDown!(
-            details, renderEditor.getPositionForOffset)) {
-          return;
-        }
-      }
-    }
-    super.onTapDown(details);
-  }
-
-  @override
-  void onSingleTapUp(TapUpDetails details) {
-    if (_state.widget.onTapUp != null) {
-      final renderEditor = getRenderEditor();
-      if (renderEditor != null) {
-        if (_state.widget.onTapUp!(
-            details, renderEditor.getPositionForOffset)) {
-          return;
-        }
-      }
-    }
-
-    getEditor()!.hideToolbar();
-
-    final positionSelected = _onTapping(details);
-
-    if (delegate.getSelectionEnabled() && !positionSelected) {
-      switch (Theme.of(_state.context).platform) {
-        case TargetPlatform.iOS:
-        case TargetPlatform.macOS:
-          switch (details.kind) {
-            case PointerDeviceKind.mouse:
-            case PointerDeviceKind.stylus:
-            case PointerDeviceKind.invertedStylus:
-              getRenderEditor()!.selectPosition(SelectionChangedCause.tap);
-              break;
-            case PointerDeviceKind.touch:
-            case PointerDeviceKind.unknown:
-              getRenderEditor()!.selectWordEdge(SelectionChangedCause.tap);
-              break;
-          }
-          break;
-        case TargetPlatform.android:
-        case TargetPlatform.fuchsia:
-        case TargetPlatform.linux:
-        case TargetPlatform.windows:
-          getRenderEditor()!.selectPosition(SelectionChangedCause.tap);
-          break;
-      }
-    }
-    _state._requestKeyboard();
-  }
-
-  @override
-  void onSingleLongTapStart(LongPressStartDetails details) {
-    if (_state.widget.onSingleLongTapStart != null) {
-      final renderEditor = getRenderEditor();
-      if (renderEditor != null) {
-        if (_state.widget.onSingleLongTapStart!(
-            details, renderEditor.getPositionForOffset)) {
-          return;
-        }
-      }
-    }
-
-    if (delegate.getSelectionEnabled()) {
-      switch (Theme.of(_state.context).platform) {
-        case TargetPlatform.iOS:
-        case TargetPlatform.macOS:
-          getRenderEditor()!.selectPositionAt(
-            details.globalPosition,
-            null,
-            SelectionChangedCause.longPress,
-          );
-          break;
-        case TargetPlatform.android:
-        case TargetPlatform.fuchsia:
-        case TargetPlatform.linux:
-        case TargetPlatform.windows:
-          getRenderEditor()!.selectWord(SelectionChangedCause.longPress);
-          Feedback.forLongPress(_state.context);
-          break;
-        default:
-          throw 'Invalid platform';
-      }
-    }
-  }
-
-  @override
-  void onSingleLongTapEnd(LongPressEndDetails details) {
-    if (_state.widget.onSingleLongTapEnd != null) {
-      final renderEditor = getRenderEditor();
-      if (renderEditor != null) {
-        if (_state.widget.onSingleLongTapEnd!(
-            details, renderEditor.getPositionForOffset)) {
-          return;
-        }
-      }
-    }
-    super.onSingleLongTapEnd(details);
-  }
-}
-
-typedef TextSelectionChangedHandler = void Function(
-    TextSelection selection, SelectionChangedCause cause);
-
-class RenderEditor extends RenderEditableContainerBox
-    implements RenderAbstractEditor {
-  RenderEditor(
-    List<RenderEditableBox>? children,
-    TextDirection textDirection,
-    double scrollBottomInset,
-    EdgeInsetsGeometry padding,
-    this.document,
-    this.selection,
-    this._hasFocus,
-    this.onSelectionChanged,
-    this._startHandleLayerLink,
-    this._endHandleLayerLink,
-    EdgeInsets floatingCursorAddedMargin,
-  ) : super(
-          children,
-          document.root,
-          textDirection,
-          scrollBottomInset,
-          padding,
-        );
-
-  Document document;
-  TextSelection selection;
-  bool _hasFocus = false;
-  LayerLink _startHandleLayerLink;
-  LayerLink _endHandleLayerLink;
-  TextSelectionChangedHandler onSelectionChanged;
-  final ValueNotifier<bool> _selectionStartInViewport =
-      ValueNotifier<bool>(true);
-
-  ValueListenable<bool> get selectionStartInViewport =>
-      _selectionStartInViewport;
-
-  ValueListenable<bool> get selectionEndInViewport => _selectionEndInViewport;
-  final ValueNotifier<bool> _selectionEndInViewport = ValueNotifier<bool>(true);
-
-  void setDocument(Document doc) {
-    if (document == doc) {
-      return;
-    }
-    document = doc;
-    markNeedsLayout();
-  }
-
-  void setHasFocus(bool h) {
-    if (_hasFocus == h) {
-      return;
-    }
-    _hasFocus = h;
-    markNeedsSemanticsUpdate();
-  }
-
-  void setSelection(TextSelection t) {
-    if (selection == t) {
-      return;
-    }
-    selection = t;
-    markNeedsPaint();
-  }
-
-  void setStartHandleLayerLink(LayerLink value) {
-    if (_startHandleLayerLink == value) {
-      return;
-    }
-    _startHandleLayerLink = value;
-    markNeedsPaint();
-  }
-
-  void setEndHandleLayerLink(LayerLink value) {
-    if (_endHandleLayerLink == value) {
-      return;
-    }
-    _endHandleLayerLink = value;
-    markNeedsPaint();
-  }
-
-  void setScrollBottomInset(double value) {
-    if (scrollBottomInset == value) {
-      return;
-    }
-    scrollBottomInset = value;
-    markNeedsPaint();
-  }
-
-  @override
-  List<TextSelectionPoint> getEndpointsForSelection(
-      TextSelection textSelection) {
-    if (textSelection.isCollapsed) {
-      final child = childAtPosition(textSelection.extent);
-      final localPosition = TextPosition(
-          offset: textSelection.extentOffset - child.getContainer().offset);
-      final localOffset = child.getOffsetForCaret(localPosition);
-      final parentData = child.parentData as BoxParentData;
-      return <TextSelectionPoint>[
-        TextSelectionPoint(
-            Offset(0, child.preferredLineHeight(localPosition)) +
-                localOffset +
-                parentData.offset,
-            null)
-      ];
-    }
-
-    final baseNode = _container.queryChild(textSelection.start, false).node;
-
-    var baseChild = firstChild;
-    while (baseChild != null) {
-      if (baseChild.getContainer() == baseNode) {
-        break;
-      }
-      baseChild = childAfter(baseChild);
-    }
-    assert(baseChild != null);
-
-    final baseParentData = baseChild!.parentData as BoxParentData;
-    final baseSelection =
-        localSelection(baseChild.getContainer(), textSelection, true);
-    var basePoint = baseChild.getBaseEndpointForSelection(baseSelection);
-    basePoint = TextSelectionPoint(
-        basePoint.point + baseParentData.offset, basePoint.direction);
-
-    final extentNode = _container.queryChild(textSelection.end, false).node;
-    RenderEditableBox? extentChild = baseChild;
-    while (extentChild != null) {
-      if (extentChild.getContainer() == extentNode) {
-        break;
-      }
-      extentChild = childAfter(extentChild);
-    }
-    assert(extentChild != null);
-
-    final extentParentData = extentChild!.parentData as BoxParentData;
-    final extentSelection =
-        localSelection(extentChild.getContainer(), textSelection, true);
-    var extentPoint =
-        extentChild.getExtentEndpointForSelection(extentSelection);
-    extentPoint = TextSelectionPoint(
-        extentPoint.point + extentParentData.offset, extentPoint.direction);
-
-    return <TextSelectionPoint>[basePoint, extentPoint];
-  }
-
-  Offset? _lastTapDownPosition;
-
-  @override
-  void handleTapDown(TapDownDetails details) {
-    _lastTapDownPosition = details.globalPosition;
-  }
-
-  @override
-  void selectWordsInRange(
-    Offset from,
-    Offset? to,
-    SelectionChangedCause cause,
-  ) {
-    final firstPosition = getPositionForOffset(from);
-    final firstWord = selectWordAtPosition(firstPosition);
-    final lastWord =
-        to == null ? firstWord : selectWordAtPosition(getPositionForOffset(to));
-
-    _handleSelectionChange(
-      TextSelection(
-        baseOffset: firstWord.base.offset,
-        extentOffset: lastWord.extent.offset,
-        affinity: firstWord.affinity,
-      ),
-      cause,
-    );
-  }
-
-  void _handleSelectionChange(
-    TextSelection nextSelection,
-    SelectionChangedCause cause,
-  ) {
-    final focusingEmpty = nextSelection.baseOffset == 0 &&
-        nextSelection.extentOffset == 0 &&
-        !_hasFocus;
-    if (nextSelection == selection &&
-        cause != SelectionChangedCause.keyboard &&
-        !focusingEmpty) {
-      return;
-    }
-    onSelectionChanged(nextSelection, cause);
-  }
-
-  @override
-  void selectWordEdge(SelectionChangedCause cause) {
-    assert(_lastTapDownPosition != null);
-    final position = getPositionForOffset(_lastTapDownPosition!);
-    final child = childAtPosition(position);
-    final nodeOffset = child.getContainer().offset;
-    final localPosition = TextPosition(
-      offset: position.offset - nodeOffset,
-      affinity: position.affinity,
-    );
-    final localWord = child.getWordBoundary(localPosition);
-    final word = TextRange(
-      start: localWord.start + nodeOffset,
-      end: localWord.end + nodeOffset,
-    );
-    if (position.offset - word.start <= 1) {
-      _handleSelectionChange(
-        TextSelection.collapsed(offset: word.start),
-        cause,
-      );
-    } else {
-      _handleSelectionChange(
-        TextSelection.collapsed(
-            offset: word.end, affinity: TextAffinity.upstream),
-        cause,
-      );
-    }
-  }
-
-  @override
-  void selectPositionAt(
-    Offset from,
-    Offset? to,
-    SelectionChangedCause cause,
-  ) {
-    final fromPosition = getPositionForOffset(from);
-    final toPosition = to == null ? null : getPositionForOffset(to);
-
-    var baseOffset = fromPosition.offset;
-    var extentOffset = fromPosition.offset;
-    if (toPosition != null) {
-      baseOffset = math.min(fromPosition.offset, toPosition.offset);
-      extentOffset = math.max(fromPosition.offset, toPosition.offset);
-    }
-
-    final newSelection = TextSelection(
-      baseOffset: baseOffset,
-      extentOffset: extentOffset,
-      affinity: fromPosition.affinity,
-    );
-    _handleSelectionChange(newSelection, cause);
-  }
-
-  @override
-  void selectWord(SelectionChangedCause cause) {
-    selectWordsInRange(_lastTapDownPosition!, null, cause);
-  }
-
-  @override
-  void selectPosition(SelectionChangedCause cause) {
-    selectPositionAt(_lastTapDownPosition!, null, cause);
-  }
-
-  @override
-  TextSelection selectWordAtPosition(TextPosition position) {
-    final child = childAtPosition(position);
-    final nodeOffset = child.getContainer().offset;
-    final localPosition = TextPosition(
-        offset: position.offset - nodeOffset, affinity: position.affinity);
-    final localWord = child.getWordBoundary(localPosition);
-    final word = TextRange(
-      start: localWord.start + nodeOffset,
-      end: localWord.end + nodeOffset,
-    );
-    if (position.offset >= word.end) {
-      return TextSelection.fromPosition(position);
-    }
-    return TextSelection(baseOffset: word.start, extentOffset: word.end);
-  }
-
-  @override
-  TextSelection selectLineAtPosition(TextPosition position) {
-    final child = childAtPosition(position);
-    final nodeOffset = child.getContainer().offset;
-    final localPosition = TextPosition(
-        offset: position.offset - nodeOffset, affinity: position.affinity);
-    final localLineRange = child.getLineBoundary(localPosition);
-    final line = TextRange(
-      start: localLineRange.start + nodeOffset,
-      end: localLineRange.end + nodeOffset,
-    );
-
-    if (position.offset >= line.end) {
-      return TextSelection.fromPosition(position);
-    }
-    return TextSelection(baseOffset: line.start, extentOffset: line.end);
-  }
-
-  @override
-  void paint(PaintingContext context, Offset offset) {
-    defaultPaint(context, offset);
-    _paintHandleLayers(context, getEndpointsForSelection(selection));
-  }
-
-  @override
-  bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
-    return defaultHitTestChildren(result, position: position);
-  }
-
-  void _paintHandleLayers(
-      PaintingContext context, List<TextSelectionPoint> endpoints) {
-    var startPoint = endpoints[0].point;
-    startPoint = Offset(
-      startPoint.dx.clamp(0.0, size.width),
-      startPoint.dy.clamp(0.0, size.height),
-    );
-    context.pushLayer(
-      LeaderLayer(link: _startHandleLayerLink, offset: startPoint),
-      super.paint,
-      Offset.zero,
-    );
-    if (endpoints.length == 2) {
-      var endPoint = endpoints[1].point;
-      endPoint = Offset(
-        endPoint.dx.clamp(0.0, size.width),
-        endPoint.dy.clamp(0.0, size.height),
-      );
-      context.pushLayer(
-        LeaderLayer(link: _endHandleLayerLink, offset: endPoint),
-        super.paint,
-        Offset.zero,
-      );
-    }
-  }
-
-  @override
-  double preferredLineHeight(TextPosition position) {
-    final child = childAtPosition(position);
-    return child.preferredLineHeight(
-        TextPosition(offset: position.offset - child.getContainer().offset));
-  }
-
-  @override
-  TextPosition getPositionForOffset(Offset offset) {
-    final local = globalToLocal(offset);
-    final child = childAtOffset(local)!;
-
-    final parentData = child.parentData as BoxParentData;
-    final localOffset = local - parentData.offset;
-    final localPosition = child.getPositionForOffset(localOffset);
-    return TextPosition(
-      offset: localPosition.offset + child.getContainer().offset,
-      affinity: localPosition.affinity,
-    );
-  }
-
-  /// Returns the y-offset of the editor at which [selection] is visible.
-  ///
-  /// The offset is the distance from the top of the editor and is the minimum
-  /// from the current scroll position until [selection] becomes visible.
-  /// Returns null if [selection] is already visible.
-  double? getOffsetToRevealCursor(
-      double viewportHeight, double scrollOffset, double offsetInViewport) {
-    final endpoints = getEndpointsForSelection(selection);
-
-    // when we drag the right handle, we should get the last point
-    TextSelectionPoint endpoint;
-    if (selection.isCollapsed) {
-      endpoint = endpoints.first;
-    } else {
-      if (selection is DragTextSelection) {
-        endpoint = (selection as DragTextSelection).first
-            ? endpoints.first
-            : endpoints.last;
-      } else {
-        endpoint = endpoints.first;
-      }
-    }
-
-    final child = childAtPosition(selection.extent);
-    const kMargin = 8.0;
-
-    final caretTop = endpoint.point.dy -
-        child.preferredLineHeight(TextPosition(
-            offset:
-                selection.extentOffset - child.getContainer().documentOffset)) -
-        kMargin +
-        offsetInViewport +
-        scrollBottomInset;
-    final caretBottom =
-        endpoint.point.dy + kMargin + offsetInViewport + scrollBottomInset;
-    double? dy;
-    if (caretTop < scrollOffset) {
-      dy = caretTop;
-    } else if (caretBottom > scrollOffset + viewportHeight) {
-      dy = caretBottom - viewportHeight;
-    }
-    if (dy == null) {
-      return null;
-    }
-    return math.max(dy, 0);
-  }
-
-  @override
-  Rect getLocalRectForCaret(TextPosition position) {
-    final targetChild = childAtPosition(position);
-    final localPosition = targetChild.globalToLocalPosition(position);
-
-    final childLocalRect = targetChild.getLocalRectForCaret(localPosition);
-
-    final boxParentData = targetChild.parentData as BoxParentData;
-    return childLocalRect.shift(Offset(0, boxParentData.offset.dy));
-  }
-}
-
-class EditableContainerParentData
-    extends ContainerBoxParentData<RenderEditableBox> {}
-
-class RenderEditableContainerBox extends RenderBox
-    with
-        ContainerRenderObjectMixin<RenderEditableBox,
-            EditableContainerParentData>,
-        RenderBoxContainerDefaultsMixin<RenderEditableBox,
-            EditableContainerParentData> {
-  RenderEditableContainerBox(
-    List<RenderEditableBox>? children,
-    this._container,
-    this.textDirection,
-    this.scrollBottomInset,
-    this._padding,
-  ) : assert(_padding.isNonNegative) {
-    addAll(children);
-  }
-
-  container_node.Container _container;
-  TextDirection textDirection;
-  EdgeInsetsGeometry _padding;
-  double scrollBottomInset;
-  EdgeInsets? _resolvedPadding;
-
-  container_node.Container getContainer() {
-    return _container;
-  }
-
-  void setContainer(container_node.Container c) {
-    if (_container == c) {
-      return;
-    }
-    _container = c;
-    markNeedsLayout();
-  }
-
-  EdgeInsetsGeometry getPadding() => _padding;
-
-  void setPadding(EdgeInsetsGeometry value) {
-    assert(value.isNonNegative);
-    if (_padding == value) {
-      return;
-    }
-    _padding = value;
-    _markNeedsPaddingResolution();
-  }
-
-  EdgeInsets? get resolvedPadding => _resolvedPadding;
-
-  void _resolvePadding() {
-    if (_resolvedPadding != null) {
-      return;
-    }
-    _resolvedPadding = _padding.resolve(textDirection);
-    _resolvedPadding = _resolvedPadding!.copyWith(left: _resolvedPadding!.left);
-
-    assert(_resolvedPadding!.isNonNegative);
-  }
-
-  RenderEditableBox childAtPosition(TextPosition position) {
-    assert(firstChild != null);
-
-    final targetNode = _container.queryChild(position.offset, false).node;
-
-    var targetChild = firstChild;
-    while (targetChild != null) {
-      if (targetChild.getContainer() == targetNode) {
-        break;
-      }
-      targetChild = childAfter(targetChild);
-    }
-    if (targetChild == null) {
-      throw 'targetChild should not be null';
-    }
-    return targetChild;
-  }
-
-  void _markNeedsPaddingResolution() {
-    _resolvedPadding = null;
-    markNeedsLayout();
-  }
-
-  RenderEditableBox? childAtOffset(Offset offset) {
-    assert(firstChild != null);
-    _resolvePadding();
-
-    if (offset.dy <= _resolvedPadding!.top) {
-      return firstChild;
-    }
-    if (offset.dy >= size.height - _resolvedPadding!.bottom) {
-      return lastChild;
-    }
-
-    var child = firstChild;
-    final dx = -offset.dx;
-    var dy = _resolvedPadding!.top;
-    while (child != null) {
-      if (child.size.contains(offset.translate(dx, -dy))) {
-        return child;
-      }
-      dy += child.size.height;
-      child = childAfter(child);
-    }
-    throw 'No child';
-  }
-
-  @override
-  void setupParentData(RenderBox child) {
-    if (child.parentData is EditableContainerParentData) {
-      return;
-    }
-
-    child.parentData = EditableContainerParentData();
-  }
-
-  @override
-  void performLayout() {
-    assert(constraints.hasBoundedWidth);
-    _resolvePadding();
-    assert(_resolvedPadding != null);
-
-    var mainAxisExtent = _resolvedPadding!.top;
-    var child = firstChild;
-    final innerConstraints =
-        BoxConstraints.tightFor(width: constraints.maxWidth)
-            .deflate(_resolvedPadding!);
-    while (child != null) {
-      child.layout(innerConstraints, parentUsesSize: true);
-      final childParentData = (child.parentData as EditableContainerParentData)
-        ..offset = Offset(_resolvedPadding!.left, mainAxisExtent);
-      mainAxisExtent += child.size.height;
-      assert(child.parentData == childParentData);
-      child = childParentData.nextSibling;
-    }
-    mainAxisExtent += _resolvedPadding!.bottom;
-    size = constraints.constrain(Size(constraints.maxWidth, mainAxisExtent));
-
-    assert(size.isFinite);
-  }
-
-  double _getIntrinsicCrossAxis(double Function(RenderBox child) childSize) {
-    var extent = 0.0;
-    var child = firstChild;
-    while (child != null) {
-      extent = math.max(extent, childSize(child));
-      final childParentData = child.parentData as EditableContainerParentData;
-      child = childParentData.nextSibling;
-    }
-    return extent;
-  }
-
-  double _getIntrinsicMainAxis(double Function(RenderBox child) childSize) {
-    var extent = 0.0;
-    var child = firstChild;
-    while (child != null) {
-      extent += childSize(child);
-      final childParentData = child.parentData as EditableContainerParentData;
-      child = childParentData.nextSibling;
-    }
-    return extent;
-  }
-
-  @override
-  double computeMinIntrinsicWidth(double height) {
-    _resolvePadding();
-    return _getIntrinsicCrossAxis((child) {
-      final childHeight = math.max<double>(
-          0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
-      return child.getMinIntrinsicWidth(childHeight) +
-          _resolvedPadding!.left +
-          _resolvedPadding!.right;
-    });
-  }
-
-  @override
-  double computeMaxIntrinsicWidth(double height) {
-    _resolvePadding();
-    return _getIntrinsicCrossAxis((child) {
-      final childHeight = math.max<double>(
-          0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
-      return child.getMaxIntrinsicWidth(childHeight) +
-          _resolvedPadding!.left +
-          _resolvedPadding!.right;
-    });
-  }
-
-  @override
-  double computeMinIntrinsicHeight(double width) {
-    _resolvePadding();
-    return _getIntrinsicMainAxis((child) {
-      final childWidth = math.max<double>(
-          0, width - _resolvedPadding!.left + _resolvedPadding!.right);
-      return child.getMinIntrinsicHeight(childWidth) +
-          _resolvedPadding!.top +
-          _resolvedPadding!.bottom;
-    });
-  }
-
-  @override
-  double computeMaxIntrinsicHeight(double width) {
-    _resolvePadding();
-    return _getIntrinsicMainAxis((child) {
-      final childWidth = math.max<double>(
-          0, width - _resolvedPadding!.left + _resolvedPadding!.right);
-      return child.getMaxIntrinsicHeight(childWidth) +
-          _resolvedPadding!.top +
-          _resolvedPadding!.bottom;
-    });
-  }
-
-  @override
-  double computeDistanceToActualBaseline(TextBaseline baseline) {
-    _resolvePadding();
-    return defaultComputeDistanceToFirstActualBaseline(baseline)! +
-        _resolvedPadding!.top;
-  }
-}

+ 0 - 31
app_flowy/packages/editor/lib/src/widgets/image.dart

@@ -1,31 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/rendering.dart';
-import 'package:photo_view/photo_view.dart';
-
-class ImageTapWrapper extends StatelessWidget {
-  const ImageTapWrapper({
-    this.imageProvider,
-  });
-
-  final ImageProvider? imageProvider;
-
-  @override
-  Widget build(BuildContext context) {
-    return Scaffold(
-      body: Container(
-        constraints: BoxConstraints.expand(
-          height: MediaQuery.of(context).size.height,
-        ),
-        child: GestureDetector(
-          onTapDown: (_) {
-            Navigator.pop(context);
-          },
-          child: PhotoView(
-            imageProvider: imageProvider,
-          ),
-        ),
-      ),
-    );
-  }
-}

+ 0 - 129
app_flowy/packages/editor/lib/src/widgets/keyboard_listener.dart

@@ -1,129 +0,0 @@
-import 'package:flutter/foundation.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
-
-//fixme workaround flutter MacOS issue https://github.com/flutter/flutter/issues/75595
-extension _LogicalKeyboardKeyCaseExt on LogicalKeyboardKey {
-  static const _kUpperToLowerDist = 0x20;
-  static final _kLowerCaseA = LogicalKeyboardKey.keyA.keyId;
-  static final _kLowerCaseZ = LogicalKeyboardKey.keyZ.keyId;
-
-  LogicalKeyboardKey toUpperCase() {
-    if (keyId < _kLowerCaseA || keyId > _kLowerCaseZ) return this;
-    return LogicalKeyboardKey(keyId - _kUpperToLowerDist);
-  }
-}
-
-enum InputShortcut { CUT, COPY, PASTE, SELECT_ALL, UNDO, REDO }
-
-typedef CursorMoveCallback = void Function(
-    LogicalKeyboardKey key, bool wordModifier, bool lineModifier, bool shift);
-typedef InputShortcutCallback = void Function(InputShortcut? shortcut);
-typedef OnDeleteCallback = void Function(bool forward);
-
-class KeyboardEventHandler {
-  KeyboardEventHandler(this.onCursorMove, this.onShortcut, this.onDelete);
-
-  final CursorMoveCallback onCursorMove;
-  final InputShortcutCallback onShortcut;
-  final OnDeleteCallback onDelete;
-
-  static final Set<LogicalKeyboardKey> _moveKeys = <LogicalKeyboardKey>{
-    LogicalKeyboardKey.arrowRight,
-    LogicalKeyboardKey.arrowLeft,
-    LogicalKeyboardKey.arrowUp,
-    LogicalKeyboardKey.arrowDown,
-  };
-
-  static final Set<LogicalKeyboardKey> _shortcutKeys = <LogicalKeyboardKey>{
-    LogicalKeyboardKey.keyA,
-    LogicalKeyboardKey.keyC,
-    LogicalKeyboardKey.keyV,
-    LogicalKeyboardKey.keyX,
-    LogicalKeyboardKey.keyZ.toUpperCase(),
-    LogicalKeyboardKey.keyZ,
-    LogicalKeyboardKey.delete,
-    LogicalKeyboardKey.backspace,
-  };
-
-  static final Set<LogicalKeyboardKey> _nonModifierKeys = <LogicalKeyboardKey>{
-    ..._shortcutKeys,
-    ..._moveKeys,
-  };
-
-  static final Set<LogicalKeyboardKey> _modifierKeys = <LogicalKeyboardKey>{
-    LogicalKeyboardKey.shift,
-    LogicalKeyboardKey.control,
-    LogicalKeyboardKey.alt,
-  };
-
-  static final Set<LogicalKeyboardKey> _macOsModifierKeys =
-      <LogicalKeyboardKey>{
-    LogicalKeyboardKey.shift,
-    LogicalKeyboardKey.meta,
-    LogicalKeyboardKey.alt,
-  };
-
-  static final Set<LogicalKeyboardKey> _interestingKeys = <LogicalKeyboardKey>{
-    ..._modifierKeys,
-    ..._macOsModifierKeys,
-    ..._nonModifierKeys,
-  };
-
-  static final Map<LogicalKeyboardKey, InputShortcut> _keyToShortcut = {
-    LogicalKeyboardKey.keyX: InputShortcut.CUT,
-    LogicalKeyboardKey.keyC: InputShortcut.COPY,
-    LogicalKeyboardKey.keyV: InputShortcut.PASTE,
-    LogicalKeyboardKey.keyA: InputShortcut.SELECT_ALL,
-  };
-
-  KeyEventResult handleRawKeyEvent(RawKeyEvent event) {
-    if (kIsWeb) {
-      // On web platform, we ignore the key because it's already processed.
-      return KeyEventResult.ignored;
-    }
-
-    if (event is! RawKeyDownEvent) {
-      return KeyEventResult.ignored;
-    }
-
-    final keysPressed =
-        LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed);
-    final key = event.logicalKey;
-    final isMacOS = event.data is RawKeyEventDataMacOs;
-    if (!_nonModifierKeys.contains(key) ||
-        keysPressed
-                .difference(isMacOS ? _macOsModifierKeys : _modifierKeys)
-                .length >
-            1 ||
-        keysPressed.difference(_interestingKeys).isNotEmpty) {
-      return KeyEventResult.ignored;
-    }
-
-    final isShortcutModifierPressed =
-        isMacOS ? event.isMetaPressed : event.isControlPressed;
-
-    if (_moveKeys.contains(key)) {
-      onCursorMove(
-          key,
-          isMacOS ? event.isAltPressed : event.isControlPressed,
-          isMacOS ? event.isMetaPressed : event.isAltPressed,
-          event.isShiftPressed);
-    } else if (isShortcutModifierPressed && (_shortcutKeys.contains(key))) {
-      if (key == LogicalKeyboardKey.keyZ ||
-          key == LogicalKeyboardKey.keyZ.toUpperCase()) {
-        onShortcut(
-            event.isShiftPressed ? InputShortcut.REDO : InputShortcut.UNDO);
-      } else {
-        onShortcut(_keyToShortcut[key]);
-      }
-    } else if (key == LogicalKeyboardKey.delete) {
-      onDelete(true);
-    } else if (key == LogicalKeyboardKey.backspace) {
-      onDelete(false);
-    } else {
-      return KeyEventResult.ignored;
-    }
-    return KeyEventResult.handled;
-  }
-}

Some files were not shown because too many files changed in this diff