mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-27 15:13:46 +00:00
fix: lose nested children when accepting AI responses (#7760)
* chore: add gitkeep in assets/font dir * Revert "feat: improve white label scripts on Windows (#7755)" This reverts commit a5eb2cdd9a0171ecbc442aef09a8cf8db469a214. * chore: use --verbose * fix: lose nested children when accept ai response * chore: lock analyzer version * Reapply "feat: improve white label scripts on Windows (#7755)" This reverts commit c73186306eaf3e9f78114fcc29e39e3a2bce528d. * chore: lock analyzer version * chore: update editor version
This commit is contained in:
parent
a5eb2cdd9a
commit
f727dde74b
0
frontend/appflowy_flutter/assets/fonts/.gitkeep
Normal file
0
frontend/appflowy_flutter/assets/fonts/.gitkeep
Normal file
@ -19,7 +19,7 @@ import 'ai_writer_node_extension.dart';
|
||||
/// Enable the debug log for the AiWriterCubit.
|
||||
///
|
||||
/// This is useful for debugging the AI writer cubit.
|
||||
const _aiWriterCubitDebugLog = false;
|
||||
const _aiWriterCubitDebugLog = true;
|
||||
|
||||
class AiWriterCubit extends Cubit<AiWriterState> {
|
||||
AiWriterCubit({
|
||||
|
||||
@ -34,7 +34,15 @@ extension AiWriterNodeExtension on EditorState {
|
||||
|
||||
// if the selected nodes are not entirely selected, slice the nodes
|
||||
final slicedNodes = <Node>[];
|
||||
final nodes = getNodesInSelection(selection);
|
||||
final List<Node> flattenNodes = getNodesInSelection(selection);
|
||||
final List<Node> nodes = [];
|
||||
|
||||
for (final node in flattenNodes) {
|
||||
if (nodes.any((element) => element.isParentOf(node))) {
|
||||
continue;
|
||||
}
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
for (final node in nodes) {
|
||||
final delta = node.delta;
|
||||
@ -76,7 +84,7 @@ extension AiWriterNodeExtension on EditorState {
|
||||
// using \n will cause the ai response treat the text as a single line
|
||||
final markdown = await customDocumentToMarkdown(
|
||||
Document.blank()..insert([0], slicedNodes),
|
||||
lineBreak: '\n\n',
|
||||
lineBreak: '\n',
|
||||
);
|
||||
|
||||
// trim the last \n if it exists
|
||||
|
||||
@ -398,6 +398,15 @@ class MarkdownTextRobot {
|
||||
|
||||
// it means the user selected the entire sentence, we just replace the node
|
||||
if (startIndex == 0 && length == node.delta?.length) {
|
||||
if (nodes.isNotEmpty && node.children.isNotEmpty) {
|
||||
// merge the children of the selected node and the first node of the ai response
|
||||
nodes[0] = nodes[0].copyWith(
|
||||
children: [
|
||||
...node.children.map((e) => e.deepCopy()),
|
||||
...nodes[0].children,
|
||||
],
|
||||
);
|
||||
}
|
||||
transaction
|
||||
..insertNodes(node.path.next, nodes)
|
||||
..deleteNode(node);
|
||||
@ -441,7 +450,14 @@ class MarkdownTextRobot {
|
||||
).root.children;
|
||||
|
||||
// Get the selected nodes.
|
||||
final nodes = editorState.getNodesInSelection(selection);
|
||||
final flattenNodes = editorState.getNodesInSelection(selection);
|
||||
final nodes = <Node>[];
|
||||
for (final node in flattenNodes) {
|
||||
if (nodes.any((element) => element.isParentOf(node))) {
|
||||
continue;
|
||||
}
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
// Note: Don't change its order, otherwise the delta will be incorrect.
|
||||
// step 1. merge the first selected node and the first node from the ai response
|
||||
@ -465,9 +481,23 @@ class MarkdownTextRobot {
|
||||
transaction
|
||||
..deleteText(firstNode, startIndex, length)
|
||||
..insertTextDelta(firstNode, startIndex, firstMarkdownDelta);
|
||||
|
||||
// if the first markdown node has children, we need to insert the children
|
||||
// and delete the children of the first node that are in the selection.
|
||||
if (firstMarkdownNode.children.isNotEmpty) {
|
||||
transaction.insertNodes(
|
||||
firstNode.path.child(0),
|
||||
firstMarkdownNode.children.map((e) => e.deepCopy()),
|
||||
);
|
||||
}
|
||||
|
||||
final nodesToDelete =
|
||||
firstNode.children.where((e) => e.path.inSelection(selection));
|
||||
transaction.deleteNodes(nodesToDelete);
|
||||
}
|
||||
|
||||
// step 2
|
||||
bool handledLastNode = false;
|
||||
final lastNode = nodes.lastOrNull;
|
||||
final lastDelta = lastNode?.delta;
|
||||
final lastMarkdownNode = markdownNodes.lastOrNull;
|
||||
@ -475,7 +505,10 @@ class MarkdownTextRobot {
|
||||
if (lastNode != null &&
|
||||
lastDelta != null &&
|
||||
lastMarkdownNode != null &&
|
||||
lastMarkdownDelta != null) {
|
||||
lastMarkdownDelta != null &&
|
||||
firstNode?.id != lastNode.id) {
|
||||
handledLastNode = true;
|
||||
|
||||
final endIndex = selection.endIndex;
|
||||
|
||||
transaction.deleteText(lastNode, 0, endIndex);
|
||||
@ -484,15 +517,30 @@ class MarkdownTextRobot {
|
||||
// selected text in the first node.
|
||||
if (lastMarkdownNode.id != firstMarkdownNode?.id) {
|
||||
transaction.insertTextDelta(lastNode, 0, lastMarkdownDelta);
|
||||
|
||||
if (lastMarkdownNode.children.isNotEmpty) {
|
||||
transaction
|
||||
..insertNodes(
|
||||
lastNode.path.child(0),
|
||||
lastMarkdownNode.children.map((e) => e.deepCopy()),
|
||||
)
|
||||
..deleteNodes(
|
||||
lastNode.children.where((e) => e.path.inSelection(selection)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 3
|
||||
final insertedPath = selection.start.path.nextNPath(1);
|
||||
if (markdownNodes.length > 2) {
|
||||
final insertLength = handledLastNode ? 2 : 1;
|
||||
if (markdownNodes.length > insertLength) {
|
||||
transaction.insertNodes(
|
||||
insertedPath,
|
||||
markdownNodes.skip(1).take(markdownNodes.length - 2).toList(),
|
||||
markdownNodes
|
||||
.skip(1)
|
||||
.take(markdownNodes.length - insertLength)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ dependencies:
|
||||
freezed_annotation: ^2.1.0
|
||||
file_picker: ^8.0.2
|
||||
file: ^7.0.0
|
||||
analyzer: 6.11.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.9
|
||||
|
||||
@ -31,6 +31,8 @@ dependencies:
|
||||
flowy_svg:
|
||||
path: ../flowy_svg
|
||||
|
||||
analyzer: 6.11.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.9
|
||||
provider: ^6.0.5
|
||||
|
||||
@ -15,7 +15,7 @@ packages:
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
@ -98,8 +98,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "4967ed5"
|
||||
resolved-ref: "4967ed57d9190948c08f868972c0babfdc470ba7"
|
||||
ref: "2361899"
|
||||
resolved-ref: "23618990b2f4ab88df67d50598b2b53cd6853e0a"
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "5.1.0"
|
||||
|
||||
@ -2,13 +2,13 @@ name: appflowy
|
||||
description: Bring projects, wikis, and teams together with AI. AppFlowy is an
|
||||
AI collaborative workspace where you achieve more without losing control of
|
||||
your data. The best open source alternative to Notion.
|
||||
publish_to: 'none'
|
||||
publish_to: "none"
|
||||
|
||||
version: 0.8.9
|
||||
|
||||
environment:
|
||||
flutter: '>=3.27.4'
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
flutter: ">=3.27.4"
|
||||
sdk: ">=3.3.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
any_date: ^1.0.4
|
||||
@ -39,7 +39,7 @@ dependencies:
|
||||
calendar_view:
|
||||
git:
|
||||
url: https://github.com/Xazin/flutter_calendar_view
|
||||
ref: '6fe0c98'
|
||||
ref: "6fe0c98"
|
||||
collection: ^1.17.1
|
||||
connectivity_plus: ^5.0.2
|
||||
cross_file: ^0.3.4+1
|
||||
@ -75,7 +75,7 @@ dependencies:
|
||||
flutter_emoji_mart:
|
||||
git:
|
||||
url: https://github.com/LucasXu0/emoji_mart.git
|
||||
ref: '355aa56'
|
||||
ref: "355aa56"
|
||||
flutter_math_fork: ^0.7.3
|
||||
flutter_slidable: ^3.0.0
|
||||
|
||||
@ -152,6 +152,8 @@ dependencies:
|
||||
talker_bloc_logger: ^4.7.1
|
||||
talker: ^4.7.1
|
||||
|
||||
analyzer: 6.11.0
|
||||
|
||||
dev_dependencies:
|
||||
# Introduce talker to log the bloc events, and only log the events in the development mode
|
||||
|
||||
@ -185,13 +187,13 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: '4967ed5'
|
||||
ref: "2361899"
|
||||
|
||||
appflowy_editor_plugins:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/AppFlowy-plugins.git
|
||||
path: 'packages/appflowy_editor_plugins'
|
||||
ref: '4efcff7'
|
||||
path: "packages/appflowy_editor_plugins"
|
||||
ref: "4efcff7"
|
||||
|
||||
sheet:
|
||||
git:
|
||||
|
||||
@ -66,9 +66,9 @@ if [ "$exclude_packages" = false ]; then
|
||||
fi
|
||||
fi
|
||||
if [ "$verbose" = true ]; then
|
||||
dart run build_runner build
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
else
|
||||
dart run build_runner build >/dev/null 2>&1
|
||||
dart run build_runner build --delete-conflicting-outputs >/dev/null 2>&1
|
||||
fi
|
||||
echo "🧊 Done generating freezed files ($d)."
|
||||
fi
|
||||
@ -108,9 +108,9 @@ fi
|
||||
|
||||
# Start the build_runner in the background
|
||||
if [ "$verbose" = true ]; then
|
||||
dart run build_runner build -d &
|
||||
dart run build_runner build --delete-conflicting-outputs &
|
||||
else
|
||||
dart run build_runner build -d >/dev/null 2>&1 &
|
||||
dart run build_runner build --delete-conflicting-outputs >/dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
# Get the PID of the background process
|
||||
|
||||
@ -64,7 +64,7 @@ cd ..
|
||||
cd freezed
|
||||
# Allow execution permissions on CI
|
||||
chmod +x ./generate_freezed.sh
|
||||
./generate_freezed.sh "${args[@]}" --show-loading
|
||||
./generate_freezed.sh "${args[@]}" --show-loading --verbose
|
||||
|
||||
# Return to the original directory
|
||||
cd "$original_dir"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user