Przeglądaj źródła

feat: added support for markdown to link an image (#1277)

* feat: added support for markdown to link an image

#1064

* fix: fixed failing FlowyEditor test

#1064

* chore: add documentation

* chore: add documentation

* chore: update locale

* Refactor/rename crate (#1275)

* chore: addressed review comments

#1064

* chore: removed unused import

* fix: compile error and wrong image attributes

Co-authored-by: appflowy <[email protected]>
Co-authored-by: Lucas.Xu <[email protected]>
Co-authored-by: Nathan.fooo <[email protected]>
Jed Aureus Gonzales 2 lat temu
rodzic
commit
8a94bbd602

+ 60 - 39
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart

@@ -188,9 +188,7 @@ ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) {
   return KeyEventResult.handled;
 };
 
-/// To create a link, enclose the link text in brackets (e.g., [link text]).
-/// Then, immediately follow it with the URL in parentheses (e.g., (https://example.com)).
-ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) {
+ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) {
   final selectionService = editorState.service.selectionService;
   final selection = selectionService.currentSelection.value;
   final textNodes = selectionService.currentSelectedNodes.whereType<TextNode>();
@@ -198,48 +196,72 @@ ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) {
     return KeyEventResult.ignored;
   }
 
-  // find all of the indexs for important characters
+  // Find all of the indexes of the relevant characters
   final textNode = textNodes.first;
   final text = textNode.toPlainText();
+  final firstExclamation = text.indexOf('!');
   final firstOpeningBracket = text.indexOf('[');
   final firstClosingBracket = text.indexOf(']');
 
-  // use regex to validate the format of the link
-  // note: this enforces that the link has http or https
-  final regexp = RegExp(r'\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d./?=#]+)$');
-  final match = regexp.firstMatch(text);
-  if (match == null) {
+  // Use RegEx to determine whether it's an image or a link
+  // Difference between image and link syntax is that image
+  // has an exclamation point at the beginning.
+  // Note: The RegEx enforces that the URL has http or https
+  final imgRegEx =
+      RegExp(r'\!\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d-./?=#%&]+)$');
+  final lnkRegEx =
+      RegExp(r'\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d-./?=#%&]+)$');
+
+  if (imgRegEx.firstMatch(text) != null) {
+    // Extract the alt text and the URL of the image
+    final match = lnkRegEx.firstMatch(text);
+    final imgUrl = match?.group(2);
+
+    // Delete the text and replace it with the image pointed to by the URL
+    final transaction = editorState.transaction
+      ..deleteText(textNode, firstExclamation, text.length)
+      ..insertNode(
+          textNode.path,
+          Node.fromJson({
+            'type': 'image',
+            'attributes': {
+              'image_src': imgUrl,
+              'align': 'center',
+            }
+          }));
+    editorState.apply(transaction);
+  } else if (lnkRegEx.firstMatch(text) != null) {
+    // Extract the text and the URL of the link
+    final match = lnkRegEx.firstMatch(text);
+    final linkText = match?.group(1);
+    final linkUrl = match?.group(2);
+
+    // Delete the initial opening bracket,
+    // update the href attribute of the text surrounded by [ ] to the url,
+    // delete everything after the text,
+    // and update the cursor position.
+    final transaction = editorState.transaction
+      ..deleteText(textNode, firstOpeningBracket, 1)
+      ..formatText(
+        textNode,
+        firstOpeningBracket,
+        firstClosingBracket - firstOpeningBracket - 1,
+        {
+          BuiltInAttributeKey.href: linkUrl,
+        },
+      )
+      ..deleteText(textNode, firstClosingBracket - 1,
+          selection.end.offset - firstClosingBracket)
+      ..afterSelection = Selection.collapsed(
+        Position(
+          path: textNode.path,
+          offset: firstOpeningBracket + linkText!.length,
+        ),
+      );
+    editorState.apply(transaction);
+  } else {
     return KeyEventResult.ignored;
   }
-
-  // extract the text and the url of the link
-  final linkText = match.group(1);
-  final linkUrl = match.group(2);
-
-  // Delete the initial opening bracket,
-  // update the href attribute of the text surrounded by [ ] to the url,
-  // delete everything after the text,
-  // and update the cursor position.
-  final transaction = editorState.transaction
-    ..deleteText(textNode, firstOpeningBracket, 1)
-    ..formatText(
-      textNode,
-      firstOpeningBracket,
-      firstClosingBracket - firstOpeningBracket - 1,
-      {
-        BuiltInAttributeKey.href: linkUrl,
-      },
-    )
-    ..deleteText(textNode, firstClosingBracket - 1,
-        selection.end.offset - firstClosingBracket)
-    ..afterSelection = Selection.collapsed(
-      Position(
-        path: textNode.path,
-        offset: firstOpeningBracket + linkText!.length,
-      ),
-    );
-  editorState.apply(transaction);
-
   return KeyEventResult.handled;
 };
 
@@ -369,6 +391,5 @@ ShortcutEventHandler doubleUnderscoresToBold = (editorState, event) {
       ),
     );
   editorState.apply(transaction);
-
   return KeyEventResult.handled;
 };

+ 2 - 3
frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart

@@ -16,7 +16,6 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespa
 import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event.dart';
 import 'package:flutter/foundation.dart';
 
-//
 List<ShortcutEvent> builtInShortcutEvents = [
   ShortcutEvent(
     key: 'Move cursor up',
@@ -273,9 +272,9 @@ List<ShortcutEvent> builtInShortcutEvents = [
     handler: doubleTildeToStrikethrough,
   ),
   ShortcutEvent(
-    key: 'Markdown link to link',
+    key: 'Markdown link or image',
     command: 'shift+parenthesis right',
-    handler: markdownLinkToLinkHandler,
+    handler: markdownLinkOrImageHandler,
   ),
   // https://github.com/flutter/flutter/issues/104944
   // Workaround: Using space editing on the web platform often results in errors,