Skip to content

Commit fc1efeb

Browse files
committed
feat: support prompt and suffix
1 parent 494e319 commit fc1efeb

File tree

9 files changed

+221
-120
lines changed

9 files changed

+221
-120
lines changed

frontend/app_flowy/packages/appflowy_editor/example/lib/home_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import 'dart:io';
44

55
import 'package:appflowy_editor/appflowy_editor.dart';
66
import 'package:example/pages/simple_editor.dart';
7-
import 'package:example/plugin/text_robot.dart';
7+
import 'package:example/plugin/AI/text_robot.dart';
88
import 'package:file_picker/file_picker.dart';
99
import 'package:flutter/foundation.dart';
1010
import 'package:flutter/material.dart';

frontend/app_flowy/packages/appflowy_editor/example/lib/pages/simple_editor.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import 'dart:convert';
22

33
import 'package:appflowy_editor/appflowy_editor.dart';
44
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
5-
import 'package:example/plugin/text_robot.dart';
5+
import 'package:example/plugin/AI/continue_to_write.dart';
6+
import 'package:example/plugin/AI/auto_completion.dart';
7+
import 'package:example/plugin/AI/getgpt3completions.dart';
68
import 'package:flutter/material.dart';
79

810
class SimpleEditor extends StatelessWidget {
@@ -65,8 +67,11 @@ class SimpleEditor extends StatelessWidget {
6567
codeBlockMenuItem,
6668
// Emoji
6769
emojiMenuItem,
68-
// Text Robot
69-
textRobotMenuItem,
70+
// Open AI
71+
if (apiKey.isNotEmpty) ...[
72+
autoCompletionMenuItem,
73+
continueToWriteMenuItem,
74+
]
7075
],
7176
);
7277
} else {
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import 'package:appflowy_editor/appflowy_editor.dart';
22
import 'package:example/plugin/AI/getgpt3completions.dart';
3+
import 'package:example/plugin/AI/text_robot.dart';
34
import 'package:flutter/material.dart';
45
import 'package:flutter/services.dart';
56

6-
SelectionMenuItem textRobotMenuItem = SelectionMenuItem(
7-
name: () => 'Open AI',
7+
SelectionMenuItem autoCompletionMenuItem = SelectionMenuItem(
8+
name: () => 'Auto generate content',
89
icon: (editorState, onSelected) => Icon(
910
Icons.rocket,
1011
size: 18.0,
1112
color: onSelected
1213
? editorState.editorStyle.selectionMenuItemSelectedIconColor
1314
: editorState.editorStyle.selectionMenuItemIconColor,
1415
),
15-
keywords: ['open ai', 'gpt3', 'ai'],
16+
keywords: ['auto generate content', 'open ai', 'gpt3', 'ai'],
1617
handler: ((editorState, menuService, context) async {
1718
showDialog(
1819
context: context,
@@ -35,11 +36,11 @@ SelectionMenuItem textRobotMenuItem = SelectionMenuItem(
3536
if (key.logicalKey == LogicalKeyboardKey.enter) {
3637
Navigator.of(context).pop();
3738
// fetch the result and insert it
38-
// Please fill in your own API key
39-
getGPT3Completion('', controller.text, '', 200, .3,
40-
(result) async {
41-
await editorState.insertTextAtCurrentSelection(
39+
final textRobot = TextRobot(editorState: editorState);
40+
getGPT3Completion(apiKey, controller.text, '', (result) async {
41+
await textRobot.insertText(
4242
result,
43+
inputType: TextRobotInputType.character,
4344
);
4445
});
4546
} else if (key.logicalKey == LogicalKeyboardKey.escape) {
@@ -52,44 +53,3 @@ SelectionMenuItem textRobotMenuItem = SelectionMenuItem(
5253
);
5354
}),
5455
);
55-
56-
enum TextRobotInputType {
57-
character,
58-
word,
59-
}
60-
61-
class TextRobot {
62-
const TextRobot({
63-
required this.editorState,
64-
this.delay = const Duration(milliseconds: 30),
65-
});
66-
67-
final EditorState editorState;
68-
final Duration delay;
69-
70-
Future<void> insertText(
71-
String text, {
72-
TextRobotInputType inputType = TextRobotInputType.character,
73-
}) async {
74-
final lines = text.split('\n');
75-
for (final line in lines) {
76-
switch (inputType) {
77-
case TextRobotInputType.character:
78-
final iterator = line.runes.iterator;
79-
while (iterator.moveNext()) {
80-
await editorState.insertTextAtCurrentSelection(
81-
iterator.currentAsString,
82-
);
83-
await Future.delayed(delay);
84-
}
85-
break;
86-
default:
87-
}
88-
89-
// insert new line
90-
if (lines.length > 1) {
91-
await editorState.insertNewLine(editorState);
92-
}
93-
}
94-
}
95-
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart';
2+
import 'package:example/plugin/AI/getgpt3completions.dart';
3+
import 'package:example/plugin/AI/text_robot.dart';
4+
import 'package:flutter/material.dart';
5+
6+
SelectionMenuItem continueToWriteMenuItem = SelectionMenuItem(
7+
name: () => 'Continue To Write',
8+
icon: (editorState, onSelected) => Icon(
9+
Icons.print,
10+
size: 18.0,
11+
color: onSelected
12+
? editorState.editorStyle.selectionMenuItemSelectedIconColor
13+
: editorState.editorStyle.selectionMenuItemIconColor,
14+
),
15+
keywords: ['continue to write'],
16+
handler: ((editorState, menuService, context) async {
17+
// get the current text
18+
final selection =
19+
editorState.service.selectionService.currentSelection.value;
20+
final textNodes = editorState.service.selectionService.currentSelectedNodes;
21+
if (selection == null || !selection.isCollapsed || textNodes.length != 1) {
22+
return;
23+
}
24+
final textNode = textNodes.first as TextNode;
25+
final prompt = textNode.delta.slice(0, selection.startIndex).toPlainText();
26+
final suffix = textNode.delta
27+
.slice(
28+
selection.endIndex,
29+
textNode.toPlainText().length,
30+
)
31+
.toPlainText();
32+
debugPrint('AI: prompt = $prompt, suffix = $suffix');
33+
final textRobot = TextRobot(editorState: editorState);
34+
getGPT3Completion(
35+
apiKey,
36+
prompt,
37+
suffix,
38+
(result) async {
39+
if (result == '\\n') {
40+
await editorState.insertNewLineAtCurrentSelection();
41+
} else {
42+
await textRobot.insertText(
43+
result,
44+
inputType: TextRobotInputType.word,
45+
);
46+
}
47+
},
48+
);
49+
}),
50+
);

frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/AI/getgpt3completions.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import 'package:http/http.dart' as http;
22
import 'dart:async';
33
import 'dart:convert';
44

5+
// Please fill in your own API key
6+
const apiKey = '';
7+
58
Future<void> getGPT3Completion(
69
String apiKey,
710
String prompt,
811
String suffix,
9-
int maxTokens,
10-
double temperature,
11-
Function(String) onData, // callback function to handle streaming data
12-
) async {
12+
Future<void> Function(String)
13+
onData, // callback function to handle streaming data
14+
{
15+
int maxTokens = 200,
16+
double temperature = .3,
17+
}) async {
1318
final data = {
1419
'prompt': prompt,
1520
'suffix': suffix,
@@ -43,7 +48,6 @@ Future<void> getGPT3Completion(
4348
}
4449

4550
final processedText = text
46-
.replaceAll('\\n', '\n')
4751
.replaceAll('\\r', '\r')
4852
.replaceAll('\\t', '\t')
4953
.replaceAll('\\b', '\b')
@@ -62,7 +66,7 @@ Future<void> getGPT3Completion(
6266
.replaceAll('\\8', '8')
6367
.replaceAll('\\9', '9');
6468

65-
onData(processedText);
69+
await onData(processedText);
6670
}
6771
}
6872
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart';
2+
3+
enum TextRobotInputType {
4+
character,
5+
word,
6+
}
7+
8+
class TextRobot {
9+
const TextRobot({
10+
required this.editorState,
11+
this.delay = const Duration(milliseconds: 30),
12+
});
13+
14+
final EditorState editorState;
15+
final Duration delay;
16+
17+
Future<void> insertText(
18+
String text, {
19+
TextRobotInputType inputType = TextRobotInputType.character,
20+
}) async {
21+
final lines = text.split('\\n');
22+
for (final line in lines) {
23+
if (line.isEmpty) continue;
24+
switch (inputType) {
25+
case TextRobotInputType.character:
26+
final iterator = line.runes.iterator;
27+
while (iterator.moveNext()) {
28+
await editorState.insertTextAtCurrentSelection(
29+
iterator.currentAsString,
30+
);
31+
await Future.delayed(delay, () {});
32+
}
33+
break;
34+
case TextRobotInputType.word:
35+
await editorState.insertTextAtCurrentSelection(
36+
line,
37+
);
38+
await Future.delayed(delay, () {});
39+
break;
40+
}
41+
42+
// insert new line
43+
if (lines.length > 1) {
44+
await editorState.insertNewLineAtCurrentSelection();
45+
}
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)