mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-11-07 05:38:51 +00:00
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:
parent
510d8357ee
commit
62c4a8c541
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:appflowy/shared/markdown_to_document.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -8,7 +9,7 @@ import 'package:synchronized/synchronized.dart';
|
|||||||
class MarkdownTextRobot {
|
class MarkdownTextRobot {
|
||||||
MarkdownTextRobot({
|
MarkdownTextRobot({
|
||||||
required this.editorState,
|
required this.editorState,
|
||||||
this.enableDebug = false,
|
this.enableDebug = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final EditorState editorState;
|
final EditorState editorState;
|
||||||
@ -92,7 +93,7 @@ class MarkdownTextRobot {
|
|||||||
final transaction = editorState.transaction;
|
final transaction = editorState.transaction;
|
||||||
|
|
||||||
// Convert markdown and deep copy nodes
|
// Convert markdown and deep copy nodes
|
||||||
final nodes = markdownToDocument(_markdownText).root.children.map(
|
final nodes = customMarkdownToDocument(_markdownText).root.children.map(
|
||||||
(node) => node.copyWith(),
|
(node) => node.copyWith(),
|
||||||
); // deep copy the nodes to avoid the linked entities being changed.
|
); // deep copy the nodes to avoid the linked entities being changed.
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:universal_platform/universal_platform.dart';
|
import 'package:universal_platform/universal_platform.dart';
|
||||||
|
|
||||||
@ -107,6 +108,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isGenerating = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -134,34 +137,48 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
final child = Card(
|
final child = Focus(
|
||||||
elevation: 5,
|
onKeyEvent: (node, event) {
|
||||||
shape: RoundedRectangleBorder(
|
if (event is! KeyDownEvent) {
|
||||||
borderRadius: BorderRadius.circular(10),
|
return KeyEventResult.ignored;
|
||||||
),
|
}
|
||||||
color: Theme.of(context).colorScheme.surface,
|
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||||
child: Container(
|
if (!isGenerating) {
|
||||||
margin: const EdgeInsets.all(10),
|
_onGenerate();
|
||||||
child: Column(
|
}
|
||||||
mainAxisSize: MainAxisSize.min,
|
return KeyEventResult.handled;
|
||||||
children: [
|
}
|
||||||
const AIWriterBlockHeader(),
|
return KeyEventResult.ignored;
|
||||||
const Space(0, 10),
|
},
|
||||||
if (prompt.isEmpty && generationCount < 1) ...[
|
child: Card(
|
||||||
_buildInputWidget(context),
|
elevation: 5,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.all(10),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const AIWriterBlockHeader(),
|
||||||
const Space(0, 10),
|
const Space(0, 10),
|
||||||
AIWriterBlockInputField(
|
if (prompt.isEmpty && generationCount < 1) ...[
|
||||||
onGenerate: _onGenerate,
|
_buildInputWidget(context),
|
||||||
onExit: _onExit,
|
const Space(0, 10),
|
||||||
),
|
AIWriterBlockInputField(
|
||||||
] else ...[
|
onGenerate: _onGenerate,
|
||||||
AIWriterBlockFooter(
|
onExit: _onExit,
|
||||||
onKeep: _onExit,
|
),
|
||||||
onRewrite: _onRewrite,
|
] else ...[
|
||||||
onDiscard: _onDiscard,
|
AIWriterBlockFooter(
|
||||||
),
|
onKeep: _onExit,
|
||||||
|
onRewrite: _onRewrite,
|
||||||
|
onDiscard: _onDiscard,
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -188,6 +205,12 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onGenerate() async {
|
Future<void> _onGenerate() async {
|
||||||
|
if (isGenerating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isGenerating = true;
|
||||||
|
|
||||||
await aiWriterOperations.updatePromptText(controller.text);
|
await aiWriterOperations.updatePromptText(controller.text);
|
||||||
|
|
||||||
if (!_isAIWriterEnabled) {
|
if (!_isAIWriterEnabled) {
|
||||||
@ -219,6 +242,7 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
onEnd: () async {
|
onEnd: () async {
|
||||||
barrierDialog?.dismiss();
|
barrierDialog?.dismiss();
|
||||||
await markdownTextRobot.stop();
|
await markdownTextRobot.stop();
|
||||||
|
editorState.service.keyboardService?.enable();
|
||||||
},
|
},
|
||||||
onError: (error) async {
|
onError: (error) async {
|
||||||
barrierDialog?.dismiss();
|
barrierDialog?.dismiss();
|
||||||
@ -227,6 +251,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await aiWriterOperations.updateGenerationCount(generationCount + 1);
|
await aiWriterOperations.updateGenerationCount(generationCount + 1);
|
||||||
|
|
||||||
|
isGenerating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onDiscard() async {
|
Future<void> _onDiscard() async {
|
||||||
@ -238,6 +264,12 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onRewrite() async {
|
Future<void> _onRewrite() async {
|
||||||
|
if (isGenerating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isGenerating = true;
|
||||||
|
|
||||||
final previousOutput = _getPreviousOutput();
|
final previousOutput = _getPreviousOutput();
|
||||||
if (previousOutput == null) {
|
if (previousOutput == null) {
|
||||||
return;
|
return;
|
||||||
@ -277,6 +309,8 @@ class _AIWriterBlockComponentState extends State<AIWriterBlockComponent> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await aiWriterOperations.updateGenerationCount(generationCount + 1);
|
await aiWriterOperations.updateGenerationCount(generationCount + 1);
|
||||||
|
|
||||||
|
isGenerating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _getPreviousOutput() {
|
String? _getPreviousOutput() {
|
||||||
|
|||||||
@ -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/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/openai/widgets/ask_ai_action.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.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/user/application/ai_service.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
@ -141,7 +142,7 @@ class AskAIActionBloc extends Bloc<AskAIEvent, AskAIState> {
|
|||||||
if (selection == null) {
|
if (selection == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final nodes = markdownToDocument(state.result)
|
final nodes = customMarkdownToDocument(state.result)
|
||||||
.root
|
.root
|
||||||
.children
|
.children
|
||||||
.map((e) => e.copyWith())
|
.map((e) => e.copyWith())
|
||||||
@ -178,7 +179,7 @@ class AskAIActionBloc extends Bloc<AskAIEvent, AskAIState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final nodes = markdownToDocument(state.result)
|
final nodes = customMarkdownToDocument(state.result)
|
||||||
.root
|
.root
|
||||||
.children
|
.children
|
||||||
.map((e) => e.copyWith())
|
.map((e) => e.copyWith())
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/markdown_text_robot.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/markdown_text_robot.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
void main() {
|
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",
|
" Enhanced",
|
||||||
" survival skills",
|
" 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",
|
||||||
|
];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user