mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: sync release 0.1.1 (#2075)
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_edit.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
import 'text_completion.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@ -125,6 +124,7 @@ class HttpOpenAIRepository implements OpenAIRepository {
|
||||
String? suffix,
|
||||
int maxTokens = 2048,
|
||||
double temperature = 0.3,
|
||||
bool useAction = false,
|
||||
}) async {
|
||||
final parameters = {
|
||||
'model': 'text-davinci-003',
|
||||
@ -151,14 +151,22 @@ class HttpOpenAIRepository implements OpenAIRepository {
|
||||
.transform(const Utf8Decoder())
|
||||
.transform(const LineSplitter())) {
|
||||
syntax += 1;
|
||||
if (syntax == 3) {
|
||||
await onStart();
|
||||
continue;
|
||||
} else if (syntax < 3) {
|
||||
continue;
|
||||
if (!useAction) {
|
||||
if (syntax == 3) {
|
||||
await onStart();
|
||||
continue;
|
||||
} else if (syntax < 3) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (syntax == 2) {
|
||||
await onStart();
|
||||
continue;
|
||||
} else if (syntax < 2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
final data = chunk.trim().split('data: ');
|
||||
Log.editor.info(data.toString());
|
||||
if (data.length > 1) {
|
||||
if (data[1] != '[DONE]') {
|
||||
final response = TextCompletionResponse.fromJson(
|
||||
@ -173,7 +181,7 @@ class HttpOpenAIRepository implements OpenAIRepository {
|
||||
previousSyntax = response.choices.first.text;
|
||||
}
|
||||
} else {
|
||||
onEnd();
|
||||
await onEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,6 +191,7 @@ class HttpOpenAIRepository implements OpenAIRepository {
|
||||
OpenAIError.fromJson(json.decode(body)['error']),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -0,0 +1,9 @@
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
Future<void> openLearnMorePage() async {
|
||||
final uri = Uri.parse(
|
||||
'https://appflowy.gitbook.io/docs/essential-documentation/appflowy-x-openai');
|
||||
if (await canLaunchUrl(uri)) {
|
||||
await launchUrl(uri);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/util/learn_more_action.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/discard_dialog.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/loading.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
@ -9,7 +11,7 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -56,6 +58,7 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
final controller = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
final textFieldFocusNode = FocusNode();
|
||||
final interceptor = SelectionInterceptor();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -63,6 +66,34 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
|
||||
textFieldFocusNode.addListener(_onFocusChanged);
|
||||
textFieldFocusNode.requestFocus();
|
||||
widget.editorState.service.selectionService.register(interceptor
|
||||
..canTap = (details) {
|
||||
final renderBox = context.findRenderObject() as RenderBox?;
|
||||
if (renderBox != null) {
|
||||
if (!isTapDownDetailsInRenderBox(details, renderBox)) {
|
||||
if (text.isNotEmpty || controller.text.isNotEmpty) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DiscardDialog(
|
||||
onConfirm: () => _onDiscard(),
|
||||
onCancel: () {},
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (controller.text.isEmpty) {
|
||||
_onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool isTapDownDetailsInRenderBox(TapDownDetails details, RenderBox box) {
|
||||
var result = BoxHitTestResult();
|
||||
box.hitTest(result, position: box.globalToLocal(details.globalPosition));
|
||||
return result.path.any((entry) => entry.target == box);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -71,6 +102,7 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
textFieldFocusNode.removeListener(_onFocusChanged);
|
||||
widget.editorState.service.selectionService.currentSelection
|
||||
.removeListener(_onCancelWhenSelectionChanged);
|
||||
widget.editorState.service.selectionService.unRegister(interceptor);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
@ -119,34 +151,26 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
fontSize: 14,
|
||||
),
|
||||
const Spacer(),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
text: FlowyText.regular(
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
onTap: () async {
|
||||
await openLearnMorePage();
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInputWidget(BuildContext context) {
|
||||
return RawKeyboardListener(
|
||||
focusNode: focusNode,
|
||||
onKey: (RawKeyEvent event) async {
|
||||
if (event is! RawKeyDownEvent) return;
|
||||
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||
if (controller.text.isNotEmpty) {
|
||||
textFieldFocusNode.unfocus();
|
||||
await _onGenerate();
|
||||
}
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
await _onExit();
|
||||
}
|
||||
},
|
||||
child: FlowyTextField(
|
||||
hintText: LocaleKeys.document_plugins_autoGeneratorHintText.tr(),
|
||||
controller: controller,
|
||||
maxLines: 3,
|
||||
focusNode: textFieldFocusNode,
|
||||
autoFocus: false,
|
||||
),
|
||||
return FlowyTextField(
|
||||
hintText: LocaleKeys.document_plugins_autoGeneratorHintText.tr(),
|
||||
controller: controller,
|
||||
maxLines: 3,
|
||||
focusNode: textFieldFocusNode,
|
||||
autoFocus: false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -157,15 +181,9 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.button_generate.tr()} ',
|
||||
text: LocaleKeys.button_generate.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(
|
||||
text: '↵',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () async => await _onGenerate(),
|
||||
@ -175,19 +193,23 @@ class _AutoCompletionInputState extends State<_AutoCompletionInput> {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.button_Cancel.tr()} ',
|
||||
text: LocaleKeys.button_Cancel.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(
|
||||
text: LocaleKeys.button_esc.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () async => await _onExit(),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: FlowyText.regular(
|
||||
LocaleKeys.document_plugins_warning.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -2,10 +2,13 @@ import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/au
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
SelectionMenuItem autoGeneratorMenuItem = SelectionMenuItem.node(
|
||||
name: 'Auto Generator',
|
||||
name: LocaleKeys.document_plugins_autoGeneratorMenuItemName.tr(),
|
||||
iconData: Icons.generating_tokens,
|
||||
keywords: ['autogenerator', 'auto generator'],
|
||||
keywords: ['ai', 'openai' 'writer', 'autogenerator'],
|
||||
nodeBuilder: (editorState) {
|
||||
final node = Node(
|
||||
type: kAutoCompletionInputType,
|
||||
|
@ -0,0 +1,28 @@
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
class DiscardDialog extends StatelessWidget {
|
||||
const DiscardDialog({
|
||||
super.key,
|
||||
required this.onConfirm,
|
||||
required this.onCancel,
|
||||
});
|
||||
|
||||
final VoidCallback onConfirm;
|
||||
final VoidCallback onCancel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigatorOkCancelDialog(
|
||||
message: LocaleKeys.document_plugins_discardResponse.tr(),
|
||||
okTitle: LocaleKeys.button_discard.tr(),
|
||||
cancelTitle: LocaleKeys.button_Cancel.tr(),
|
||||
onOkPressed: onConfirm,
|
||||
onCancelPressed: onCancel,
|
||||
);
|
||||
}
|
||||
}
|
@ -10,11 +10,39 @@ enum SmartEditAction {
|
||||
String get toInstruction {
|
||||
switch (this) {
|
||||
case SmartEditAction.summarize:
|
||||
return 'Make this shorter and more concise:';
|
||||
return 'Tl;dr';
|
||||
case SmartEditAction.fixSpelling:
|
||||
return 'Correct this to standard English:';
|
||||
}
|
||||
}
|
||||
|
||||
String prompt(String input) {
|
||||
switch (this) {
|
||||
case SmartEditAction.summarize:
|
||||
return '$input\n\nTl;dr';
|
||||
case SmartEditAction.fixSpelling:
|
||||
return 'Correct this to standard English:\n\n$input';
|
||||
}
|
||||
}
|
||||
|
||||
static SmartEditAction from(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return SmartEditAction.summarize;
|
||||
case 1:
|
||||
return SmartEditAction.fixSpelling;
|
||||
}
|
||||
return SmartEditAction.fixSpelling;
|
||||
}
|
||||
|
||||
String get name {
|
||||
switch (this) {
|
||||
case SmartEditAction.summarize:
|
||||
return LocaleKeys.document_plugins_smartEditSummarize.tr();
|
||||
case SmartEditAction.fixSpelling:
|
||||
return LocaleKeys.document_plugins_smartEditFixSpelling.tr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SmartEditActionWrapper extends ActionCell {
|
||||
@ -26,11 +54,6 @@ class SmartEditActionWrapper extends ActionCell {
|
||||
|
||||
@override
|
||||
String get name {
|
||||
switch (inner) {
|
||||
case SmartEditAction.summarize:
|
||||
return LocaleKeys.document_plugins_smartEditSummarize.tr();
|
||||
case SmartEditAction.fixSpelling:
|
||||
return LocaleKeys.document_plugins_smartEditFixSpelling.tr();
|
||||
}
|
||||
return inner.name;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/error.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/service/text_edit.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/util/learn_more_action.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/discard_dialog.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/decoration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:dartz/dartz.dart' as dartz;
|
||||
import 'package:appflowy/util/either_extension.dart';
|
||||
|
||||
const String kSmartEditType = 'smart_edit_input';
|
||||
const String kSmartEditInstructionType = 'smart_edit_instruction';
|
||||
@ -22,15 +21,15 @@ const String kSmartEditInputType = 'smart_edit_input';
|
||||
class SmartEditInputBuilder extends NodeWidgetBuilder<Node> {
|
||||
@override
|
||||
NodeValidator<Node> get nodeValidator => (node) {
|
||||
return SmartEditAction.values.map((e) => e.toInstruction).contains(
|
||||
node.attributes[kSmartEditInstructionType],
|
||||
) &&
|
||||
return SmartEditAction.values
|
||||
.map((e) => e.index)
|
||||
.contains(node.attributes[kSmartEditInstructionType]) &&
|
||||
node.attributes[kSmartEditInputType] is String;
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(NodeWidgetContext<Node> context) {
|
||||
return _SmartEditInput(
|
||||
return _HoverSmartInput(
|
||||
key: context.node.key,
|
||||
node: context.node,
|
||||
editorState: context.editorState,
|
||||
@ -38,28 +37,111 @@ class SmartEditInputBuilder extends NodeWidgetBuilder<Node> {
|
||||
}
|
||||
}
|
||||
|
||||
class _SmartEditInput extends StatefulWidget {
|
||||
final Node node;
|
||||
|
||||
final EditorState editorState;
|
||||
const _SmartEditInput({
|
||||
Key? key,
|
||||
class _HoverSmartInput extends StatefulWidget {
|
||||
const _HoverSmartInput({
|
||||
required super.key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
});
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_HoverSmartInput> createState() => _HoverSmartInputState();
|
||||
}
|
||||
|
||||
class _HoverSmartInputState extends State<_HoverSmartInput> {
|
||||
final popoverController = PopoverController();
|
||||
final key = GlobalKey(debugLabel: 'smart_edit_input');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
popoverController.show();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final width = _maxWidth();
|
||||
|
||||
return AppFlowyPopover(
|
||||
controller: popoverController,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
triggerActions: PopoverTriggerFlags.none,
|
||||
margin: EdgeInsets.zero,
|
||||
constraints: BoxConstraints(maxWidth: width),
|
||||
decoration: FlowyDecoration.decoration(
|
||||
Colors.transparent,
|
||||
Colors.transparent,
|
||||
),
|
||||
child: const SizedBox(
|
||||
width: double.infinity,
|
||||
),
|
||||
canClose: () async {
|
||||
final completer = Completer<bool>();
|
||||
final state = key.currentState as _SmartEditInputState;
|
||||
if (state.result.isEmpty) {
|
||||
completer.complete(true);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return DiscardDialog(
|
||||
onConfirm: () => completer.complete(true),
|
||||
onCancel: () => completer.complete(false),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
return completer.future;
|
||||
},
|
||||
popupBuilder: (BuildContext popoverContext) {
|
||||
return _SmartEditInput(
|
||||
key: key,
|
||||
node: widget.node,
|
||||
editorState: widget.editorState,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
double _maxWidth() {
|
||||
var width = double.infinity;
|
||||
final editorSize = widget.editorState.renderBox?.size;
|
||||
final padding = widget.editorState.editorStyle.padding;
|
||||
if (editorSize != null && padding != null) {
|
||||
width = editorSize.width - padding.left - padding.right;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
class _SmartEditInput extends StatefulWidget {
|
||||
const _SmartEditInput({
|
||||
required super.key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
});
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_SmartEditInput> createState() => _SmartEditInputState();
|
||||
}
|
||||
|
||||
class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
String get instruction => widget.node.attributes[kSmartEditInstructionType];
|
||||
SmartEditAction get action =>
|
||||
SmartEditAction.from(widget.node.attributes[kSmartEditInstructionType]);
|
||||
String get input => widget.node.attributes[kSmartEditInputType];
|
||||
|
||||
final focusNode = FocusNode();
|
||||
final client = http.Client();
|
||||
dartz.Either<OpenAIError, TextEditResponse>? result;
|
||||
bool loading = true;
|
||||
String result = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -72,12 +154,7 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
widget.editorState.service.keyboardService?.enable();
|
||||
}
|
||||
});
|
||||
_requestEdits().then(
|
||||
(value) => setState(() {
|
||||
result = value;
|
||||
loading = false;
|
||||
}),
|
||||
);
|
||||
_requestCompletions();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -99,28 +176,16 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
}
|
||||
|
||||
Widget _buildSmartEditPanel(BuildContext context) {
|
||||
return RawKeyboardListener(
|
||||
focusNode: focusNode,
|
||||
onKey: (RawKeyEvent event) async {
|
||||
if (event is! RawKeyDownEvent) return;
|
||||
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||
await _onReplace();
|
||||
await _onExit();
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
await _onExit();
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeaderWidget(context),
|
||||
const Space(0, 10),
|
||||
_buildResultWidget(context),
|
||||
const Space(0, 10),
|
||||
_buildInputFooterWidget(context),
|
||||
],
|
||||
),
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeaderWidget(context),
|
||||
const Space(0, 10),
|
||||
_buildResultWidget(context),
|
||||
const Space(0, 10),
|
||||
_buildInputFooterWidget(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@ -128,13 +193,19 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
return Row(
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
LocaleKeys.document_plugins_smartEditTitleName.tr(),
|
||||
'${LocaleKeys.document_plugins_openAI.tr()}: ${action.name}',
|
||||
fontSize: 14,
|
||||
),
|
||||
const Spacer(),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
text: FlowyText.regular(
|
||||
LocaleKeys.document_plugins_autoGeneratorLearnMore.tr(),
|
||||
),
|
||||
onTap: () async {
|
||||
await openLearnMorePage();
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -147,25 +218,14 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
child: const CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
if (result.isEmpty) {
|
||||
return loading;
|
||||
}
|
||||
return result!.fold((error) {
|
||||
return Flexible(
|
||||
child: Text(
|
||||
error.message,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
}, (response) {
|
||||
return Flexible(
|
||||
child: Text(
|
||||
response.choices.map((e) => e.text).join('\n'),
|
||||
),
|
||||
);
|
||||
});
|
||||
return Flexible(
|
||||
child: Text(
|
||||
result,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInputFooterWidget(BuildContext context) {
|
||||
@ -175,19 +235,13 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.button_replace.tr()} ',
|
||||
text: LocaleKeys.button_replace.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(
|
||||
text: '↵',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
_onReplace();
|
||||
onPressed: () async {
|
||||
await _onReplace();
|
||||
_onExit();
|
||||
},
|
||||
),
|
||||
@ -196,19 +250,33 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.button_Cancel.tr()} ',
|
||||
text: LocaleKeys.button_insertBelow.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () async {
|
||||
await _onInsertBelow();
|
||||
_onExit();
|
||||
},
|
||||
),
|
||||
const Space(10, 0),
|
||||
FlowyRichTextButton(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: LocaleKeys.button_esc.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
text: LocaleKeys.button_Cancel.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () async => await _onExit(),
|
||||
),
|
||||
const Spacer(),
|
||||
FlowyText.regular(
|
||||
LocaleKeys.document_plugins_warning.tr(),
|
||||
color: Theme.of(context).hintColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -219,12 +287,11 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
final selectedNodes = widget
|
||||
.editorState.service.selectionService.currentSelectedNodes.normalized
|
||||
.whereType<TextNode>();
|
||||
if (selection == null || result == null || result!.isLeft()) {
|
||||
if (selection == null || result.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final texts = result!.asRight().choices.first.text.split('\n')
|
||||
..removeWhere((element) => element.isEmpty);
|
||||
final texts = result.split('\n')..removeWhere((element) => element.isEmpty);
|
||||
final transaction = widget.editorState.transaction;
|
||||
transaction.replaceTexts(
|
||||
selectedNodes.toList(growable: false),
|
||||
@ -234,6 +301,25 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
return widget.editorState.apply(transaction);
|
||||
}
|
||||
|
||||
Future<void> _onInsertBelow() async {
|
||||
final selection = widget.editorState.service.selectionService
|
||||
.currentSelection.value?.normalized;
|
||||
if (selection == null || result.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final texts = result.split('\n')..removeWhere((element) => element.isEmpty);
|
||||
final transaction = widget.editorState.transaction;
|
||||
transaction.insertNodes(
|
||||
selection.normalized.end.path.next,
|
||||
texts.map(
|
||||
(e) => TextNode(
|
||||
delta: Delta()..insert(e),
|
||||
),
|
||||
),
|
||||
);
|
||||
return widget.editorState.apply(transaction);
|
||||
}
|
||||
|
||||
Future<void> _onExit() async {
|
||||
final transaction = widget.editorState.transaction;
|
||||
transaction.deleteNode(widget.node);
|
||||
@ -246,35 +332,63 @@ class _SmartEditInputState extends State<_SmartEditInput> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<dartz.Either<OpenAIError, TextEditResponse>> _requestEdits() async {
|
||||
Future<void> _requestCompletions() async {
|
||||
final result = await UserBackendService.getCurrentUserProfile();
|
||||
return result.fold((userProfile) async {
|
||||
return result.fold((l) async {
|
||||
final openAIRepository = HttpOpenAIRepository(
|
||||
client: client,
|
||||
apiKey: userProfile.openaiKey,
|
||||
apiKey: l.openaiKey,
|
||||
);
|
||||
final edits = await openAIRepository.getEdits(
|
||||
input: input,
|
||||
instruction: instruction,
|
||||
n: 1,
|
||||
);
|
||||
return edits.fold((error) async {
|
||||
return dartz.Left(
|
||||
OpenAIError(
|
||||
message:
|
||||
LocaleKeys.document_plugins_smartEditCouldNotFetchResult.tr(),
|
||||
),
|
||||
|
||||
var lines = input.split('\n\n');
|
||||
if (action == SmartEditAction.summarize) {
|
||||
lines = [lines.join('\n')];
|
||||
}
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
final element = lines[i];
|
||||
await openAIRepository.getStreamedCompletions(
|
||||
useAction: true,
|
||||
prompt: action.prompt(element),
|
||||
onStart: () async {
|
||||
setState(() {
|
||||
loading = false;
|
||||
});
|
||||
},
|
||||
onProcess: (response) async {
|
||||
setState(() {
|
||||
this.result += response.choices.first.text;
|
||||
});
|
||||
},
|
||||
onEnd: () async {
|
||||
setState(() {
|
||||
if (i != lines.length - 1) {
|
||||
this.result += '\n';
|
||||
}
|
||||
});
|
||||
},
|
||||
onError: (error) async {
|
||||
await _showError(error.message);
|
||||
await _onExit();
|
||||
},
|
||||
);
|
||||
}, (textEdit) async {
|
||||
return dartz.Right(textEdit);
|
||||
});
|
||||
}, (error) async {
|
||||
// error
|
||||
return dartz.Left(
|
||||
OpenAIError(
|
||||
message: LocaleKeys.document_plugins_smartEditCouldNotFetchKey.tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}, (r) async {
|
||||
await _showError(r.msg);
|
||||
await _onExit();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _showError(String message) async {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
action: SnackBarAction(
|
||||
label: LocaleKeys.button_Cancel.tr(),
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
},
|
||||
),
|
||||
content: FlowyText(message),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +101,17 @@ class _SmartEditWidgetState extends State<_SmartEditWidget> {
|
||||
textNodes.normalized,
|
||||
selection.normalized,
|
||||
);
|
||||
while (input.last.isEmpty) {
|
||||
input.removeLast();
|
||||
}
|
||||
final transaction = widget.editorState.transaction;
|
||||
transaction.insertNode(
|
||||
selection.normalized.end.path.next,
|
||||
Node(
|
||||
type: kSmartEditType,
|
||||
attributes: {
|
||||
kSmartEditInstructionType: actionWrapper.inner.toInstruction,
|
||||
kSmartEditInputType: input,
|
||||
kSmartEditInstructionType: actionWrapper.inner.index,
|
||||
kSmartEditInputType: input.join('\n\n'),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
Reference in New Issue
Block a user