fix: unable to render code block in ai writer block (#6883)

* fix: unable to render code block in ai writer block

* feat: enter to generate ai result
This commit is contained in:
Lucas 2024-11-29 18:49:47 +08:00 committed by GitHub
parent 510d8357ee
commit 62c4a8c541
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 274 additions and 30 deletions

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:appflowy/shared/markdown_to_document.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/foundation.dart';
@ -8,7 +9,7 @@ import 'package:synchronized/synchronized.dart';
class MarkdownTextRobot {
MarkdownTextRobot({
required this.editorState,
this.enableDebug = false,
this.enableDebug = true,
});
final EditorState editorState;
@ -92,7 +93,7 @@ class MarkdownTextRobot {
final transaction = editorState.transaction;
// Convert markdown and deep copy nodes
final nodes = markdownToDocument(_markdownText).root.children.map(
final nodes = customMarkdownToDocument(_markdownText).root.children.map(
(node) => node.copyWith(),
); // deep copy the nodes to avoid the linked entities being changed.

View File

@ -15,6 +15,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:universal_platform/universal_platform.dart';
@ -107,6 +108,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
return null;
}
bool isGenerating = false;
@override
void initState() {
super.initState();
@ -134,7 +137,20 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
return const SizedBox.shrink();
}
final child = Card(
final child = Focus(
onKeyEvent: (node, event) {
if (event is! KeyDownEvent) {
return KeyEventResult.ignored;
}
if (event.logicalKey == LogicalKeyboardKey.enter) {
if (!isGenerating) {
_onGenerate();
}
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
@ -164,6 +180,7 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
],
),
),
),
);
return Padding(
@ -188,6 +205,12 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
}
Future<void> _onGenerate() async {
if (isGenerating) {
return;
}
isGenerating = true;
await aiWriterOperations.updatePromptText(controller.text);
if (!_isAIWriterEnabled) {
@ -219,6 +242,7 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
onEnd: () async {
barrierDialog?.dismiss();
await markdownTextRobot.stop();
editorState.service.keyboardService?.enable();
},
onError: (error) async {
barrierDialog?.dismiss();
@ -227,6 +251,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
);
await aiWriterOperations.updateGenerationCount(generationCount + 1);
isGenerating = false;
}
Future<void> _onDiscard() async {
@ -238,6 +264,12 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
}
Future<void> _onRewrite() async {
if (isGenerating) {
return;
}
isGenerating = true;
final previousOutput = _getPreviousOutput();
if (previousOutput == null) {
return;
@ -277,6 +309,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
);
await aiWriterOperations.updateGenerationCount(generationCount + 1);
isGenerating = false;
}
String? _getPreviousOutput() {

View File

@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/ser
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/error.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/ask_ai_action.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/shared/markdown_to_document.dart';
import 'package:appflowy/user/application/ai_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
@ -141,7 +142,7 @@ class AskAIActionBloc extends Bloc<AskAIEvent, AskAIState> {
if (selection == null) {
return;
}
final nodes = markdownToDocument(state.result)
final nodes = customMarkdownToDocument(state.result)
.root
.children
.map((e) => e.copyWith())
@ -178,7 +179,7 @@ class AskAIActionBloc extends Bloc<AskAIEvent, AskAIState> {
return;
}
final nodes = markdownToDocument(state.result)
final nodes = customMarkdownToDocument(state.result)
.root
.children
.map((e) => e.copyWith())

View File

@ -1,6 +1,7 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/markdown_text_robot.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
@ -236,6 +237,63 @@ void main() {
},
);
});
// Partial sample
// Sure, let's provide an alternative Rust implementation for the Two Sum problem, focusing on clarity and efficiency but with a slightly different approach:
// ```rust
// fn two_sum(nums: &[i32], target: i32) -> Vec<(usize, usize)> {
// let mut results = Vec::new();
// let mut map = std::collections::HashMap::new();
//
// for (i, &num) in nums.iter().enumerate() {
// let complement = target - num;
// if let Some(&j) = map.get(&complement) {
// results.push((j, i));
// }
// map.insert(num, i);
// }
//
// results
// }
//
// fn main() {
// let nums = vec![2, 7, 11, 15];
// let target = 9;
//
// let pairs = two_sum(&nums, target);
// if pairs.is_empty() {
// println!("No two sum solution found");
// } else {
// for (i, j) in pairs {
// println!("Indices: {}, {}", i, j);
// }
// }
// }
// ```
test('live refresh (4)', () async {
await testLiveRefresh(
_liveRefreshSample4,
expect: (editorState) {
final nodes = editorState.document.root.children;
expect(nodes.length, 3);
final n1 = nodes[0];
expect(n1.type, ParagraphBlockKeys.type);
expect(
n1.delta!.toPlainText(),
'''Sure, let's provide an alternative Rust implementation for the Two Sum problem, focusing on clarity and efficiency but with a slightly different approach:''',
);
final n2 = nodes[1];
expect(n2.type, CodeBlockKeys.type);
expect(
n2.delta!.toPlainText(),
isNotEmpty,
);
expect(n2.attributes[CodeBlockKeys.language], 'rust');
},
);
});
});
}
@ -372,3 +430,153 @@ const _liveRefreshSample3 = [
" Enhanced",
" survival skills",
];
const _liveRefreshSample4 = [
"Sure",
", let's",
" provide an",
" alternative Rust",
" implementation for the Two",
" Sum",
" problem",
",",
" focusing",
" on",
" clarity",
" and efficiency",
" but with",
" a slightly",
" different approach",
":\n\n",
"```",
"rust",
"\nfn two",
"_sum",
"(nums",
": &[",
"i",
"32",
"],",
" target",
":",
" i",
"32",
")",
" ->",
" Vec",
"<(usize",
", usize",
")>",
" {\n",
" ",
" let",
" mut results",
" = Vec::",
"new",
"();\n",
" ",
" let mut",
" map",
" =",
" std::collections",
"::",
"HashMap",
"::",
"new",
"();\n\n ",
" for (",
"i,",
" &num",
") in",
" nums.iter",
"().enumer",
"ate()",
" {\n let",
" complement",
" = target",
" - num",
";\n",
" ",
" if",
" let",
" Some(&",
"j)",
" =",
" map",
".get(&",
"complement",
") {\n",
" results",
".push((",
"j",
",",
" i));\n }\n",
" ",
" map",
".insert",
"(num",
", i",
");\n",
" ",
" }\n\n ",
" results\n",
"}\n\n",
"fn",
" main()",
" {\n",
" ",
" let",
" nums",
" =",
" vec![2, ",
"7",
",",
" 11, 15];\n",
" let",
" target",
" =",
" ",
"9",
";\n\n",
" ",
" let",
" pairs",
" = two",
"_sum",
"(&",
"nums",
",",
" target);\n",
" ",
" if",
" pairs",
".is",
"_empty()",
" {\n ",
" println",
"!(\"",
"No",
" two",
" sum solution",
" found\");\n",
" ",
" }",
" else {\n for",
" (",
"i",
", j",
") in",
" pairs {\n",
" println",
"!(\"Indices",
":",
" {},",
" {}\",",
" i",
",",
" j",
");\n ",
" }\n ",
" }\n}\n",
"```\n\n",
];