Skip to content

Commit 494e319

Browse files
committed
feat: add openAI plugin
1 parent 34d4ea3 commit 494e319

File tree

11 files changed

+145
-20
lines changed

11 files changed

+145
-20
lines changed

frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 0.0.9
2+
* Support customize the text color and text background color.
3+
* Fix some bugs.
4+
5+
## 0.0.8
6+
* Fix the toolbar display issue.
7+
* Fix the copy/paste issue on Windows.
8+
* Minor Updates.
9+
110
## 0.0.7
211
* Refactor theme customizer, and support dark mode.
312
* Support export and import markdown.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ 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';
56
import 'package:flutter/material.dart';
67

78
class SimpleEditor extends StatelessWidget {
@@ -64,6 +65,8 @@ class SimpleEditor extends StatelessWidget {
6465
codeBlockMenuItem,
6566
// Emoji
6667
emojiMenuItem,
68+
// Text Robot
69+
textRobotMenuItem,
6770
],
6871
);
6972
} else {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import 'package:http/http.dart' as http;
2+
import 'dart:async';
3+
import 'dart:convert';
4+
5+
Future<void> getGPT3Completion(
6+
String apiKey,
7+
String prompt,
8+
String suffix,
9+
int maxTokens,
10+
double temperature,
11+
Function(String) onData, // callback function to handle streaming data
12+
) async {
13+
final data = {
14+
'prompt': prompt,
15+
'suffix': suffix,
16+
'max_tokens': maxTokens,
17+
'temperature': temperature,
18+
'stream': true, // set stream parameter to true
19+
};
20+
21+
final headers = {
22+
'Authorization': apiKey,
23+
'Content-Type': 'application/json',
24+
};
25+
final request = http.Request(
26+
'POST',
27+
Uri.parse('https://api.openai.com/v1/engines/text-davinci-003/completions'),
28+
);
29+
request.body = json.encode(data);
30+
request.headers.addAll(headers);
31+
32+
final httpResponse = await request.send();
33+
34+
if (httpResponse.statusCode == 200) {
35+
await for (final chunk in httpResponse.stream) {
36+
var result = utf8.decode(chunk).split('text": "');
37+
var text = '';
38+
if (result.length > 1) {
39+
result = result[1].split('",');
40+
if (result.isNotEmpty) {
41+
text = result.first;
42+
}
43+
}
44+
45+
final processedText = text
46+
.replaceAll('\\n', '\n')
47+
.replaceAll('\\r', '\r')
48+
.replaceAll('\\t', '\t')
49+
.replaceAll('\\b', '\b')
50+
.replaceAll('\\f', '\f')
51+
.replaceAll('\\v', '\v')
52+
.replaceAll('\\\'', '\'')
53+
.replaceAll('"', '"')
54+
.replaceAll('\\0', '0')
55+
.replaceAll('\\1', '1')
56+
.replaceAll('\\2', '2')
57+
.replaceAll('\\3', '3')
58+
.replaceAll('\\4', '4')
59+
.replaceAll('\\5', '5')
60+
.replaceAll('\\6', '6')
61+
.replaceAll('\\7', '7')
62+
.replaceAll('\\8', '8')
63+
.replaceAll('\\9', '9');
64+
65+
onData(processedText);
66+
}
67+
}
68+
}

frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/text_robot.dart

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,57 @@
11
import 'package:appflowy_editor/appflowy_editor.dart';
2+
import 'package:example/plugin/AI/getgpt3completions.dart';
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/services.dart';
5+
6+
SelectionMenuItem textRobotMenuItem = SelectionMenuItem(
7+
name: () => 'Open AI',
8+
icon: (editorState, onSelected) => Icon(
9+
Icons.rocket,
10+
size: 18.0,
11+
color: onSelected
12+
? editorState.editorStyle.selectionMenuItemSelectedIconColor
13+
: editorState.editorStyle.selectionMenuItemIconColor,
14+
),
15+
keywords: ['open ai', 'gpt3', 'ai'],
16+
handler: ((editorState, menuService, context) async {
17+
showDialog(
18+
context: context,
19+
builder: (context) {
20+
final controller = TextEditingController(text: '');
21+
return AlertDialog(
22+
content: RawKeyboardListener(
23+
focusNode: FocusNode(),
24+
child: TextField(
25+
autofocus: true,
26+
controller: controller,
27+
maxLines: null,
28+
decoration: const InputDecoration(
29+
border: OutlineInputBorder(),
30+
hintText: 'Please input something...',
31+
),
32+
),
33+
onKey: (key) {
34+
if (key is! RawKeyDownEvent) return;
35+
if (key.logicalKey == LogicalKeyboardKey.enter) {
36+
Navigator.of(context).pop();
37+
// 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(
42+
result,
43+
);
44+
});
45+
} else if (key.logicalKey == LogicalKeyboardKey.escape) {
46+
Navigator.of(context).pop();
47+
}
48+
},
49+
),
50+
);
51+
},
52+
);
53+
}),
54+
);
255

356
enum TextRobotInputType {
457
character,
@@ -19,31 +72,24 @@ class TextRobot {
1972
TextRobotInputType inputType = TextRobotInputType.character,
2073
}) async {
2174
final lines = text.split('\n');
22-
var path = 0;
2375
for (final line in lines) {
2476
switch (inputType) {
2577
case TextRobotInputType.character:
26-
var index = 0;
2778
final iterator = line.runes.iterator;
2879
while (iterator.moveNext()) {
29-
// await editorState.insertText(
30-
// index,
31-
// iterator.currentAsString,
32-
// path: [path],
33-
// );
3480
await editorState.insertTextAtCurrentSelection(
3581
iterator.currentAsString,
3682
);
37-
index += iterator.currentSize;
3883
await Future.delayed(delay);
3984
}
40-
path += 1;
4185
break;
4286
default:
4387
}
4488

4589
// insert new line
46-
await editorState.insertNewLine(editorState, [path]);
90+
if (lines.length > 1) {
91+
await editorState.insertNewLine(editorState);
92+
}
4793
}
4894
}
4995
}

frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ dependencies:
4747
flutter_math_fork: ^0.6.3+1
4848
appflowy_editor_plugins:
4949
path: ../../../packages/appflowy_editor_plugins
50+
http: ^0.13.5
5051

5152
dev_dependencies:
5253
flutter_test:

frontend/app_flowy/packages/appflowy_editor/lib/src/commands/text/text_commands.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,15 @@ extension TextCommands on EditorState {
110110
}
111111

112112
Future<void> insertNewLine(
113-
EditorState editorState,
114-
Path path,
115-
) async {
113+
EditorState editorState, {
114+
Path? path,
115+
}) async {
116116
return futureCommand(() async {
117+
final p = path ?? getSelection(null).start.path.next;
117118
final transaction = editorState.transaction;
118-
transaction.insertNode(path, TextNode.empty());
119+
transaction.insertNode(p, TextNode.empty());
119120
transaction.afterSelection = Selection.single(
120-
path: path,
121+
path: p,
121122
startOffset: 0,
122123
);
123124
apply(transaction);

frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:appflowy_editor/appflowy_editor.dart';
2-
import 'package:appflowy_editor/src/commands/text/text_commands.dart';
32
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
43

54
import 'package:appflowy_editor/src/extensions/text_style_extension.dart';

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
9696
final items = widget.items
9797
.where(
9898
(item) => item.keywords.any((keyword) {
99-
final value = keyword.contains(newKeyword);
99+
final value = keyword.contains(newKeyword.toLowerCase());
100100
if (value) {
101101
maxKeywordLength = max(maxKeywordLength, keyword.length);
102102
}

frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:appflowy_editor/appflowy_editor.dart';
2-
import 'package:appflowy_editor/src/commands/text/text_commands.dart';
32
import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
43
import 'package:appflowy_editor/src/flutter/overlay.dart';
54
import 'package:appflowy_editor/src/infra/clipboard.dart';

frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:appflowy_editor/appflowy_editor.dart';
2-
import 'package:appflowy_editor/src/commands/text/text_commands.dart';
32
import 'package:flutter/foundation.dart';
43
import 'package:flutter/material.dart';
54

0 commit comments

Comments
 (0)