Переглянути джерело

Fix/1936 (#1939)

* fix: [Bug] Code block jump skip blank line

* fix: auto completion interaction

* Revert "fix: [Bug] Code block jump skip blank line"

This reverts commit 5a252bcb182b78898bf40be11b7bc2a84dcf4d4f.

* fix: [Bug] Code block jump skip blank line

* fix: number list parse error
Lucas.Xu 2 роки тому
батько
коміт
21199c04ac

+ 21 - 20
frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart

@@ -2,7 +2,6 @@ import 'dart:convert';
 
 
 import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_edit.dart';
 import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_edit.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
-import 'package:flutter/material.dart';
 
 
 import 'text_completion.dart';
 import 'text_completion.dart';
 import 'package:dartz/dartz.dart';
 import 'package:dartz/dartz.dart';
@@ -39,7 +38,7 @@ abstract class OpenAIRepository {
   Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({
   Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({
     required String prompt,
     required String prompt,
     String? suffix,
     String? suffix,
-    int maxTokens = 500,
+    int maxTokens = 2048,
     double temperature = .3,
     double temperature = .3,
   });
   });
 
 
@@ -47,10 +46,10 @@ abstract class OpenAIRepository {
     required String prompt,
     required String prompt,
     required Future<void> Function() onStart,
     required Future<void> Function() onStart,
     required Future<void> Function(TextCompletionResponse response) onProcess,
     required Future<void> Function(TextCompletionResponse response) onProcess,
-    required VoidCallback onEnd,
+    required Future<void> Function() onEnd,
     required void Function(OpenAIError error) onError,
     required void Function(OpenAIError error) onError,
     String? suffix,
     String? suffix,
-    int maxTokens = 500,
+    int maxTokens = 2048,
     double temperature = 0.3,
     double temperature = 0.3,
   });
   });
 
 
@@ -85,7 +84,7 @@ class HttpOpenAIRepository implements OpenAIRepository {
   Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({
   Future<Either<OpenAIError, TextCompletionResponse>> getCompletions({
     required String prompt,
     required String prompt,
     String? suffix,
     String? suffix,
-    int maxTokens = 500,
+    int maxTokens = 2048,
     double temperature = 0.3,
     double temperature = 0.3,
   }) async {
   }) async {
     final parameters = {
     final parameters = {
@@ -121,10 +120,10 @@ class HttpOpenAIRepository implements OpenAIRepository {
     required String prompt,
     required String prompt,
     required Future<void> Function() onStart,
     required Future<void> Function() onStart,
     required Future<void> Function(TextCompletionResponse response) onProcess,
     required Future<void> Function(TextCompletionResponse response) onProcess,
-    required VoidCallback onEnd,
+    required Future<void> Function() onEnd,
     required void Function(OpenAIError error) onError,
     required void Function(OpenAIError error) onError,
     String? suffix,
     String? suffix,
-    int maxTokens = 500,
+    int maxTokens = 2048,
     double temperature = 0.3,
     double temperature = 0.3,
   }) async {
   }) async {
     final parameters = {
     final parameters = {
@@ -159,21 +158,23 @@ class HttpOpenAIRepository implements OpenAIRepository {
           continue;
           continue;
         }
         }
         final data = chunk.trim().split('data: ');
         final data = chunk.trim().split('data: ');
-        if (data.length > 1 && data[1] != '[DONE]') {
-          final response = TextCompletionResponse.fromJson(
-            json.decode(data[1]),
-          );
-          if (response.choices.isNotEmpty) {
-            final text = response.choices.first.text;
-            if (text == previousSyntax && text == '\n') {
-              continue;
+        Log.editor.info(data.toString());
+        if (data.length > 1) {
+          if (data[1] != '[DONE]') {
+            final response = TextCompletionResponse.fromJson(
+              json.decode(data[1]),
+            );
+            if (response.choices.isNotEmpty) {
+              final text = response.choices.first.text;
+              if (text == previousSyntax && text == '\n') {
+                continue;
+              }
+              await onProcess(response);
+              previousSyntax = response.choices.first.text;
             }
             }
-            await onProcess(response);
-            previousSyntax = response.choices.first.text;
-            Log.editor.info(response.choices.first.text);
+          } else {
+            onEnd();
           }
           }
-        } else {
-          onEnd();
         }
         }
       }
       }
     } else {
     } else {

+ 13 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/auto_completion_node_widget.dart

@@ -69,6 +69,8 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
   void dispose() {
   void dispose() {
     controller.dispose();
     controller.dispose();
     textFieldFocusNode.removeListener(_onFocusChanged);
     textFieldFocusNode.removeListener(_onFocusChanged);
+    widget.editorState.service.selectionService.currentSelection
+        .removeListener(_onCancelWhenSelectionChanged);
 
 
     super.dispose();
     super.dispose();
   }
   }
@@ -239,6 +241,7 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
     final result = await UserBackendService.getCurrentUserProfile();
     final result = await UserBackendService.getCurrentUserProfile();
 
 
     result.fold((userProfile) async {
     result.fold((userProfile) async {
+      BarrierDialog? barrierDialog;
       final openAIRepository = HttpOpenAIRepository(
       final openAIRepository = HttpOpenAIRepository(
         client: http.Client(),
         client: http.Client(),
         apiKey: userProfile.openaiKey,
         apiKey: userProfile.openaiKey,
@@ -247,6 +250,8 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
         prompt: controller.text,
         prompt: controller.text,
         onStart: () async {
         onStart: () async {
           loading.stop();
           loading.stop();
+          barrierDialog = BarrierDialog(context);
+          barrierDialog?.show();
           await _makeSurePreviousNodeIsEmptyTextNode();
           await _makeSurePreviousNodeIsEmptyTextNode();
         },
         },
         onProcess: (response) async {
         onProcess: (response) async {
@@ -255,10 +260,15 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
             await widget.editorState.autoInsertText(
             await widget.editorState.autoInsertText(
               text,
               text,
               inputType: TextRobotInputType.word,
               inputType: TextRobotInputType.word,
+              delay: Duration.zero,
             );
             );
           }
           }
         },
         },
-        onEnd: () {},
+        onEnd: () async {
+          await barrierDialog?.dismiss();
+          widget.editorState.service.selectionService.currentSelection
+              .addListener(_onCancelWhenSelectionChanged);
+        },
         onError: (error) async {
         onError: (error) async {
           loading.stop();
           loading.stop();
           await _showError(error.message);
           await _showError(error.message);
@@ -353,4 +363,6 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
       widget.editorState.service.keyboardService?.enable();
       widget.editorState.service.keyboardService?.enable();
     }
     }
   }
   }
+
+  void _onCancelWhenSelectionChanged() {}
 }
 }

+ 25 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/loading.dart

@@ -32,3 +32,28 @@ class Loading {
     return Navigator.of(loadingContext).pop();
     return Navigator.of(loadingContext).pop();
   }
   }
 }
 }
+
+class BarrierDialog {
+  BarrierDialog(
+    this.context,
+  );
+
+  late BuildContext loadingContext;
+  final BuildContext context;
+
+  Future<void> show() async {
+    return showDialog<void>(
+      context: context,
+      barrierDismissible: false,
+      barrierColor: Colors.transparent,
+      builder: (BuildContext context) {
+        loadingContext = context;
+        return Container();
+      },
+    );
+  }
+
+  Future<void> dismiss() async {
+    return Navigator.of(loadingContext).pop();
+  }
+}

+ 3 - 3
frontend/appflowy_flutter/packages/appflowy_editor/example/macos/Podfile.lock

@@ -38,10 +38,10 @@ EXTERNAL SOURCES:
 SPEC CHECKSUMS:
 SPEC CHECKSUMS:
   flowy_infra_ui: c34d49d615ed9fe552cd47f90d7850815a74e9e9
   flowy_infra_ui: c34d49d615ed9fe552cd47f90d7850815a74e9e9
   FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
   FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
-  path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
+  path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
   rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
   rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
-  shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
-  url_launcher_macos: c04e4fa86382d4f94f6b38f14625708be3ae52e2
+  shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
+  url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
 
 
 PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
 PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
 
 

+ 8 - 1
frontend/appflowy_flutter/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart

@@ -95,11 +95,18 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
               textPosition, Rect.zero) ??
               textPosition, Rect.zero) ??
           Offset.zero;
           Offset.zero;
     }
     }
+    if (widget.cursorHeight != null && cursorHeight != null) {
+      cursorOffset = Offset(
+        cursorOffset.dx,
+        cursorOffset.dy + (cursorHeight - widget.cursorHeight!) / 2,
+      );
+      cursorHeight = widget.cursorHeight;
+    }
     final rect = Rect.fromLTWH(
     final rect = Rect.fromLTWH(
       cursorOffset.dx - (widget.cursorWidth / 2.0),
       cursorOffset.dx - (widget.cursorWidth / 2.0),
       cursorOffset.dy,
       cursorOffset.dy,
       widget.cursorWidth,
       widget.cursorWidth,
-      widget.cursorHeight ?? cursorHeight ?? 16.0,
+      cursorHeight ?? 16.0,
     );
     );
     return rect;
     return rect;
   }
   }

+ 3 - 1
frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart

@@ -56,7 +56,9 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
   } else if (numberMatch != null) {
   } else if (numberMatch != null) {
     final matchText = numberMatch.group(0);
     final matchText = numberMatch.group(0);
     final numText = numberMatch.group(1);
     final numText = numberMatch.group(1);
-    if (matchText != null && numText != null) {
+    if (matchText != null &&
+        numText != null &&
+        matchText.length == selection.startIndex) {
       return _toNumberList(editorState, textNode, matchText, numText);
       return _toNumberList(editorState, textNode, matchText, numText);
     }
     }
   }
   }

+ 2 - 0
frontend/appflowy_flutter/packages/appflowy_editor_plugins/lib/src/code_block/code_block_node_widget.dart

@@ -102,6 +102,8 @@ class __CodeBlockNodeWidgeState extends State<_CodeBlockNodeWidge>
         key: _richTextKey,
         key: _richTextKey,
         textNode: widget.textNode,
         textNode: widget.textNode,
         editorState: widget.editorState,
         editorState: widget.editorState,
+        lineHeight: 1.0,
+        cursorHeight: 15.0,
         textSpanDecorator: (textSpan) => TextSpan(
         textSpanDecorator: (textSpan) => TextSpan(
           style: widget.editorState.editorStyle.textStyle,
           style: widget.editorState.editorStyle.textStyle,
           children: codeTextSpan,
           children: codeTextSpan,