Lucas.Xu пре 2 година
родитељ
комит
494e31993b

+ 9 - 0
frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md

@@ -1,3 +1,12 @@
+## 0.0.9
+* Support customize the text color and text background color.
+* Fix some bugs.
+
+## 0.0.8
+* Fix the toolbar display issue.
+* Fix the copy/paste issue on Windows.
+* Minor Updates.
+
 ## 0.0.7
 * Refactor theme customizer, and support dark mode.
 * Support export and import markdown.

+ 3 - 0
frontend/app_flowy/packages/appflowy_editor/example/lib/pages/simple_editor.dart

@@ -2,6 +2,7 @@ import 'dart:convert';
 
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
+import 'package:example/plugin/text_robot.dart';
 import 'package:flutter/material.dart';
 
 class SimpleEditor extends StatelessWidget {
@@ -64,6 +65,8 @@ class SimpleEditor extends StatelessWidget {
               codeBlockMenuItem,
               // Emoji
               emojiMenuItem,
+              // Text Robot
+              textRobotMenuItem,
             ],
           );
         } else {

+ 68 - 0
frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/AI/getgpt3completions.dart

@@ -0,0 +1,68 @@
+import 'package:http/http.dart' as http;
+import 'dart:async';
+import 'dart:convert';
+
+Future<void> getGPT3Completion(
+  String apiKey,
+  String prompt,
+  String suffix,
+  int maxTokens,
+  double temperature,
+  Function(String) onData, // callback function to handle streaming data
+) async {
+  final data = {
+    'prompt': prompt,
+    'suffix': suffix,
+    'max_tokens': maxTokens,
+    'temperature': temperature,
+    'stream': true, // set stream parameter to true
+  };
+
+  final headers = {
+    'Authorization': apiKey,
+    'Content-Type': 'application/json',
+  };
+  final request = http.Request(
+    'POST',
+    Uri.parse('https://api.openai.com/v1/engines/text-davinci-003/completions'),
+  );
+  request.body = json.encode(data);
+  request.headers.addAll(headers);
+
+  final httpResponse = await request.send();
+
+  if (httpResponse.statusCode == 200) {
+    await for (final chunk in httpResponse.stream) {
+      var result = utf8.decode(chunk).split('text": "');
+      var text = '';
+      if (result.length > 1) {
+        result = result[1].split('",');
+        if (result.isNotEmpty) {
+          text = result.first;
+        }
+      }
+
+      final processedText = text
+          .replaceAll('\\n', '\n')
+          .replaceAll('\\r', '\r')
+          .replaceAll('\\t', '\t')
+          .replaceAll('\\b', '\b')
+          .replaceAll('\\f', '\f')
+          .replaceAll('\\v', '\v')
+          .replaceAll('\\\'', '\'')
+          .replaceAll('"', '"')
+          .replaceAll('\\0', '0')
+          .replaceAll('\\1', '1')
+          .replaceAll('\\2', '2')
+          .replaceAll('\\3', '3')
+          .replaceAll('\\4', '4')
+          .replaceAll('\\5', '5')
+          .replaceAll('\\6', '6')
+          .replaceAll('\\7', '7')
+          .replaceAll('\\8', '8')
+          .replaceAll('\\9', '9');
+
+      onData(processedText);
+    }
+  }
+}

+ 56 - 10
frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/text_robot.dart

@@ -1,4 +1,57 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:example/plugin/AI/getgpt3completions.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+SelectionMenuItem textRobotMenuItem = SelectionMenuItem(
+  name: () => 'Open AI',
+  icon: (editorState, onSelected) => Icon(
+    Icons.rocket,
+    size: 18.0,
+    color: onSelected
+        ? editorState.editorStyle.selectionMenuItemSelectedIconColor
+        : editorState.editorStyle.selectionMenuItemIconColor,
+  ),
+  keywords: ['open ai', 'gpt3', 'ai'],
+  handler: ((editorState, menuService, context) async {
+    showDialog(
+      context: context,
+      builder: (context) {
+        final controller = TextEditingController(text: '');
+        return AlertDialog(
+          content: RawKeyboardListener(
+            focusNode: FocusNode(),
+            child: TextField(
+              autofocus: true,
+              controller: controller,
+              maxLines: null,
+              decoration: const InputDecoration(
+                border: OutlineInputBorder(),
+                hintText: 'Please input something...',
+              ),
+            ),
+            onKey: (key) {
+              if (key is! RawKeyDownEvent) return;
+              if (key.logicalKey == LogicalKeyboardKey.enter) {
+                Navigator.of(context).pop();
+                // fetch the result and insert it
+                // Please fill in your own API key
+                getGPT3Completion('', controller.text, '', 200, .3,
+                    (result) async {
+                  await editorState.insertTextAtCurrentSelection(
+                    result,
+                  );
+                });
+              } else if (key.logicalKey == LogicalKeyboardKey.escape) {
+                Navigator.of(context).pop();
+              }
+            },
+          ),
+        );
+      },
+    );
+  }),
+);
 
 enum TextRobotInputType {
   character,
@@ -19,31 +72,24 @@ class TextRobot {
     TextRobotInputType inputType = TextRobotInputType.character,
   }) async {
     final lines = text.split('\n');
-    var path = 0;
     for (final line in lines) {
       switch (inputType) {
         case TextRobotInputType.character:
-          var index = 0;
           final iterator = line.runes.iterator;
           while (iterator.moveNext()) {
-            // await editorState.insertText(
-            //   index,
-            //   iterator.currentAsString,
-            //   path: [path],
-            // );
             await editorState.insertTextAtCurrentSelection(
               iterator.currentAsString,
             );
-            index += iterator.currentSize;
             await Future.delayed(delay);
           }
-          path += 1;
           break;
         default:
       }
 
       // insert new line
-      await editorState.insertNewLine(editorState, [path]);
+      if (lines.length > 1) {
+        await editorState.insertNewLine(editorState);
+      }
     }
   }
 }

+ 1 - 0
frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml

@@ -47,6 +47,7 @@ dependencies:
   flutter_math_fork: ^0.6.3+1
   appflowy_editor_plugins:
     path: ../../../packages/appflowy_editor_plugins
+  http: ^0.13.5
 
 dev_dependencies:
   flutter_test:

+ 6 - 5
frontend/app_flowy/packages/appflowy_editor/lib/src/commands/text/text_commands.dart

@@ -110,14 +110,15 @@ extension TextCommands on EditorState {
   }
 
   Future<void> insertNewLine(
-    EditorState editorState,
-    Path path,
-  ) async {
+    EditorState editorState, {
+    Path? path,
+  }) async {
     return futureCommand(() async {
+      final p = path ?? getSelection(null).start.path.next;
       final transaction = editorState.transaction;
-      transaction.insertNode(path, TextNode.empty());
+      transaction.insertNode(p, TextNode.empty());
       transaction.afterSelection = Selection.single(
-        path: path,
+        path: p,
         startOffset: 0,
       );
       apply(transaction);

+ 0 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart

@@ -1,5 +1,4 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
-import 'package:appflowy_editor/src/commands/text/text_commands.dart';
 import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
 
 import 'package:appflowy_editor/src/extensions/text_style_extension.dart';

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart

@@ -96,7 +96,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
     final items = widget.items
         .where(
           (item) => item.keywords.any((keyword) {
-            final value = keyword.contains(newKeyword);
+            final value = keyword.contains(newKeyword.toLowerCase());
             if (value) {
               maxKeywordLength = max(maxKeywordLength, keyword.length);
             }

+ 0 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart

@@ -1,5 +1,4 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
-import 'package:appflowy_editor/src/commands/text/text_commands.dart';
 import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
 import 'package:appflowy_editor/src/flutter/overlay.dart';
 import 'package:appflowy_editor/src/infra/clipboard.dart';

+ 0 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart

@@ -1,5 +1,4 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
-import 'package:appflowy_editor/src/commands/text/text_commands.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/pubspec.yaml

@@ -1,6 +1,6 @@
 name: appflowy_editor
 description: A highly customizable rich-text editor for Flutter
-version: 0.0.7
+version: 0.0.9
 homepage: https://github.com/AppFlowy-IO/AppFlowy
 
 platforms: