1- import 'package:appflowy/plugins/document/presentation/plugins/openai/service/error.dart' ;
21import 'package:appflowy/plugins/document/presentation/plugins/openai/service/openai_client.dart' ;
3- import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_edit.dart' ;
42import 'package:appflowy/plugins/document/presentation/plugins/openai/util/learn_more_action.dart' ;
53import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart' ;
64import 'package:appflowy/user/application/user_service.dart' ;
@@ -12,8 +10,6 @@ import 'package:flutter/material.dart';
1210import 'package:appflowy/generated/locale_keys.g.dart' ;
1311import 'package:easy_localization/easy_localization.dart' ;
1412import 'package:http/http.dart' as http;
15- import 'package:dartz/dartz.dart' as dartz;
16- import 'package:appflowy/util/either_extension.dart' ;
1713
1814const String kSmartEditType = 'smart_edit_input' ;
1915const String kSmartEditInstructionType = 'smart_edit_instruction' ;
@@ -22,9 +18,9 @@ const String kSmartEditInputType = 'smart_edit_input';
2218class SmartEditInputBuilder extends NodeWidgetBuilder <Node > {
2319 @override
2420 NodeValidator <Node > get nodeValidator => (node) {
25- return SmartEditAction .values. map ((e) => e.toInstruction). contains (
26- node.attributes[kSmartEditInstructionType],
27- ) &&
21+ return SmartEditAction .values
22+ . map ((e) => e.index)
23+ . contains (node.attributes[kSmartEditInstructionType] ) &&
2824 node.attributes[kSmartEditInputType] is String ;
2925 };
3026
@@ -53,13 +49,14 @@ class _SmartEditInput extends StatefulWidget {
5349}
5450
5551class _SmartEditInputState extends State <_SmartEditInput > {
56- String get instruction => widget.node.attributes[kSmartEditInstructionType];
52+ SmartEditAction get action =>
53+ SmartEditAction .from (widget.node.attributes[kSmartEditInstructionType]);
5754 String get input => widget.node.attributes[kSmartEditInputType];
5855
5956 final focusNode = FocusNode ();
6057 final client = http.Client ();
61- dartz.Either <OpenAIError , TextEditResponse >? result;
6258 bool loading = true ;
59+ String result = '' ;
6360
6461 @override
6562 void initState () {
@@ -72,12 +69,7 @@ class _SmartEditInputState extends State<_SmartEditInput> {
7269 widget.editorState.service.keyboardService? .enable ();
7370 }
7471 });
75- _requestEdits ().then (
76- (value) => setState (() {
77- result = value;
78- loading = false ;
79- }),
80- );
72+ _requestCompletions ();
8173 }
8274
8375 @override
@@ -141,25 +133,14 @@ class _SmartEditInputState extends State<_SmartEditInput> {
141133 child: const CircularProgressIndicator (),
142134 ),
143135 );
144- if (result == null ) {
136+ if (result.isEmpty ) {
145137 return loading;
146138 }
147- return result! .fold ((error) {
148- return Flexible (
149- child: Text (
150- error.message,
151- style: Theme .of (context).textTheme.bodyMedium? .copyWith (
152- color: Colors .red,
153- ),
154- ),
155- );
156- }, (response) {
157- return Flexible (
158- child: Text (
159- response.choices.map ((e) => e.text).join ('\n ' ),
160- ),
161- );
162- });
139+ return Flexible (
140+ child: Text (
141+ result,
142+ ),
143+ );
163144 }
164145
165146 Widget _buildInputFooterWidget (BuildContext context) {
@@ -174,8 +155,23 @@ class _SmartEditInputState extends State<_SmartEditInput> {
174155 ),
175156 ],
176157 ),
177- onPressed: () {
178- _onReplace ();
158+ onPressed: () async {
159+ await _onReplace ();
160+ _onExit ();
161+ },
162+ ),
163+ const Space (10 , 0 ),
164+ FlowyRichTextButton (
165+ TextSpan (
166+ children: [
167+ TextSpan (
168+ text: LocaleKeys .button_insertBelow.tr (),
169+ style: Theme .of (context).textTheme.bodyMedium,
170+ ),
171+ ],
172+ ),
173+ onPressed: () async {
174+ await _onInsertBelow ();
179175 _onExit ();
180176 },
181177 ),
@@ -201,12 +197,11 @@ class _SmartEditInputState extends State<_SmartEditInput> {
201197 final selectedNodes = widget
202198 .editorState.service.selectionService.currentSelectedNodes.normalized
203199 .whereType <TextNode >();
204- if (selection == null || result == null || result ! . isLeft () ) {
200+ if (selection == null || result.isEmpty ) {
205201 return ;
206202 }
207203
208- final texts = result! .asRight ().choices.first.text.split ('\n ' )
209- ..removeWhere ((element) => element.isEmpty);
204+ final texts = result.split ('\n ' )..removeWhere ((element) => element.isEmpty);
210205 final transaction = widget.editorState.transaction;
211206 transaction.replaceTexts (
212207 selectedNodes.toList (growable: false ),
@@ -216,6 +211,25 @@ class _SmartEditInputState extends State<_SmartEditInput> {
216211 return widget.editorState.apply (transaction);
217212 }
218213
214+ Future <void > _onInsertBelow () async {
215+ final selection = widget.editorState.service.selectionService
216+ .currentSelection.value? .normalized;
217+ if (selection == null || result.isEmpty) {
218+ return ;
219+ }
220+ final texts = result.split ('\n ' )..removeWhere ((element) => element.isEmpty);
221+ final transaction = widget.editorState.transaction;
222+ transaction.insertNodes (
223+ selection.normalized.end.path.next,
224+ texts.map (
225+ (e) => TextNode (
226+ delta: Delta ()..insert (e),
227+ ),
228+ ),
229+ );
230+ return widget.editorState.apply (transaction);
231+ }
232+
219233 Future <void > _onExit () async {
220234 final transaction = widget.editorState.transaction;
221235 transaction.deleteNode (widget.node);
@@ -228,35 +242,62 @@ class _SmartEditInputState extends State<_SmartEditInput> {
228242 );
229243 }
230244
231- Future <dartz. Either < OpenAIError , TextEditResponse >> _requestEdits () async {
245+ Future <void > _requestCompletions () async {
232246 final result = await UserBackendService .getCurrentUserProfile ();
233- return result.fold ((userProfile ) async {
247+ return result.fold ((l ) async {
234248 final openAIRepository = HttpOpenAIRepository (
235249 client: client,
236- apiKey: userProfile.openaiKey,
237- );
238- final edits = await openAIRepository.getEdits (
239- input: input,
240- instruction: instruction,
241- n: 1 ,
250+ apiKey: l.openaiKey,
242251 );
243- return edits.fold ((error) async {
244- return dartz.Left (
245- OpenAIError (
246- message:
247- LocaleKeys .document_plugins_smartEditCouldNotFetchResult.tr (),
248- ),
252+ var lines = input.split ('\n\n ' );
253+ if (action == SmartEditAction .summarize) {
254+ lines = [lines.join ('\n ' )];
255+ }
256+ for (var i = 0 ; i < lines.length; i++ ) {
257+ final element = lines[i];
258+ await openAIRepository.getStreamedCompletions (
259+ useAction: true ,
260+ prompt: action.prompt (element),
261+ onStart: () async {
262+ setState (() {
263+ loading = false ;
264+ });
265+ },
266+ onProcess: (response) async {
267+ setState (() {
268+ this .result += response.choices.first.text;
269+ });
270+ },
271+ onEnd: () async {
272+ setState (() {
273+ if (i != lines.length - 1 ) {
274+ this .result += '\n ' ;
275+ }
276+ });
277+ },
278+ onError: (error) async {
279+ await _showError (error.message);
280+ await _onExit ();
281+ },
249282 );
250- }, (textEdit) async {
251- return dartz.Right (textEdit);
252- });
253- }, (error) async {
254- // error
255- return dartz.Left (
256- OpenAIError (
257- message: LocaleKeys .document_plugins_smartEditCouldNotFetchKey.tr (),
258- ),
259- );
283+ }
284+ }, (r) async {
285+ await _showError (r.msg);
286+ await _onExit ();
260287 });
261288 }
289+
290+ Future <void > _showError (String message) async {
291+ ScaffoldMessenger .of (context).showSnackBar (
292+ SnackBar (
293+ action: SnackBarAction (
294+ label: LocaleKeys .button_Cancel.tr (),
295+ onPressed: () {
296+ ScaffoldMessenger .of (context).hideCurrentSnackBar ();
297+ },
298+ ),
299+ content: FlowyText (message),
300+ ),
301+ );
302+ }
262303}
0 commit comments