fix: ai text insertion (#7615)

* fix: dont scroll to ai writer node if path not found

* chore: rename text robot clear method and add reset

* fix: insert position is off if using ai writer multiple times

* chore: reorganize code

* fix: undo not working after accept
This commit is contained in:
Richard Shiue 2025-03-25 22:41:35 +08:00 committed by GitHub
parent eb4b015de8
commit 039c191d1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 98 deletions

View File

@ -48,16 +48,16 @@ Future<void> removeAiWriterNode(
);
}
void formatSelection(
Future<void> formatSelection(
EditorState editorState,
Selection selection,
Transaction transaction,
ApplySuggestionFormatType formatType,
) {
) async {
final nodes = editorState.getNodesInSelection(selection).toList();
if (nodes.isEmpty) {
return;
}
final transaction = editorState.transaction;
if (nodes.length == 1) {
final node = nodes.removeAt(0);
@ -103,25 +103,43 @@ void formatSelection(
}
transaction.compose();
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
}
Position ensurePreviousNodeIsEmptyParagraph(
Future<Position> ensurePreviousNodeIsEmptyParagraph(
EditorState editorState,
Node aiWriterNode,
Transaction transaction,
) {
) async {
final previous = aiWriterNode.previous;
final needsEmptyParagraphNode = previous == null ||
previous.type != ParagraphBlockKeys.type ||
(previous.delta?.toPlainText().isNotEmpty ?? false);
final Position position;
final transaction = editorState.transaction;
if (needsEmptyParagraphNode) {
position = Position(path: aiWriterNode.path);
transaction.insertNode(aiWriterNode.path, paragraphNode());
} else {
position = Position(path: previous.path);
}
transaction.afterSelection = Selection.collapsed(position);
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
);
return position;
}

View File

@ -54,6 +54,7 @@ class AiWriterCubit extends Cubit<AiWriterState> {
if (withDiscard) {
await _textRobot.discard();
}
_textRobot.clear();
_textRobot.reset();
onRemoveNode?.call();
records.clear();
@ -65,21 +66,11 @@ class AiWriterCubit extends Cubit<AiWriterState> {
if (selection == null) {
return;
}
final transaction = editorState.transaction;
formatSelection(
await formatSelection(
editorState,
selection,
transaction,
ApplySuggestionFormatType.clear,
);
await editorState.apply(
transaction,
options: const ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
}
if (aiWriterNode != null) {
await removeAiWriterNode(editorState, aiWriterNode!);
@ -122,7 +113,7 @@ class AiWriterCubit extends Cubit<AiWriterState> {
}
await _textRobot.discard();
_textRobot.reset();
_textRobot.clear();
switch (command) {
case AiWriterCommand.continueWriting:
@ -222,11 +213,15 @@ class AiWriterCubit extends Cubit<AiWriterState> {
if (action case SuggestionAction.accept) {
await _textRobot.persist();
await formatSelection(
editorState,
selection,
ApplySuggestionFormatType.clear,
);
final nodes = editorState.getNodesInSelection(selection);
final transaction = editorState.transaction..deleteNodes(nodes);
await editorState.apply(
transaction,
options: const ApplyOptions(recordUndo: false),
withUpdateSelection: false,
);
await exit(withDiscard: false, withUnformat: false);
@ -235,6 +230,8 @@ class AiWriterCubit extends Cubit<AiWriterState> {
if (action case SuggestionAction.keep) {
await _textRobot.persist();
await exit(withDiscard: false);
return;
}
if (action case SuggestionAction.insertBelow) {
@ -244,20 +241,9 @@ class AiWriterCubit extends Cubit<AiWriterState> {
final command = (state as ReadyAiWriterState).command;
final markdownText = (state as ReadyAiWriterState).markdownText;
if (command == AiWriterCommand.explain && markdownText.isNotEmpty) {
final transaction = editorState.transaction;
final position = ensurePreviousNodeIsEmptyParagraph(
final position = await ensurePreviousNodeIsEmptyParagraph(
editorState,
aiWriterNode!,
transaction,
);
transaction.afterSelection = null;
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
_textRobot.start(position: position);
await _textRobot.persist(markdownText: markdownText);
@ -265,22 +251,14 @@ class AiWriterCubit extends Cubit<AiWriterState> {
await _textRobot.persist();
}
final transaction = editorState.transaction;
formatSelection(
await formatSelection(
editorState,
selection,
transaction,
ApplySuggestionFormatType.clear,
);
await editorState.apply(
transaction,
options: const ApplyOptions(recordUndo: false),
withUpdateSelection: false,
);
}
await exit(withDiscard: false);
}
}
bool hasUnusedResponse() {
return switch (state) {
@ -308,12 +286,12 @@ class AiWriterCubit extends Cubit<AiWriterState> {
if (command == AiWriterCommand.continueWriting) {
return (true, '');
} else {
}
if (selection.isCollapsed) {
return (true, '');
} else {
final selectionText =
await editorState.getMarkdownInSelection(selection);
}
final selectionText = await editorState.getMarkdownInSelection(selection);
if (command == AiWriterCommand.userQuestion) {
records.add(
@ -324,8 +302,6 @@ class AiWriterCubit extends Cubit<AiWriterState> {
return (true, selectionText);
}
}
}
}
Future<String> _getDocumentContentFromTopToPosition(Position position) async {
final beginningToCursorSelection = Selection(
@ -360,19 +336,9 @@ class AiWriterCubit extends Cubit<AiWriterState> {
sourceIds: selectedSourcesNotifier.value,
completionType: command.toCompletionType(),
onStart: () async {
final transaction = editorState.transaction;
final position = ensurePreviousNodeIsEmptyParagraph(
final position = await ensurePreviousNodeIsEmptyParagraph(
editorState,
aiWriterNode!,
transaction,
);
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
_textRobot.start(position: position);
records.add(
@ -461,20 +427,9 @@ class AiWriterCubit extends Cubit<AiWriterState> {
sourceIds: selectedSourcesNotifier.value,
format: predefinedFormat,
onStart: () async {
final transaction = editorState.transaction;
final position = ensurePreviousNodeIsEmptyParagraph(
final position = await ensurePreviousNodeIsEmptyParagraph(
editorState,
aiWriterNode!,
transaction,
);
transaction.afterSelection = null;
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
_textRobot.start(position: position);
records.add(
@ -555,26 +510,14 @@ class AiWriterCubit extends Cubit<AiWriterState> {
history: records,
sourceIds: selectedSourcesNotifier.value,
onStart: () async {
final transaction = editorState.transaction;
formatSelection(
await formatSelection(
editorState,
selection,
transaction,
ApplySuggestionFormatType.original,
);
final position = ensurePreviousNodeIsEmptyParagraph(
final position = await ensurePreviousNodeIsEmptyParagraph(
editorState,
aiWriterNode!,
transaction,
);
transaction.afterSelection = null;
await editorState.apply(
transaction,
options: ApplyOptions(
inMemoryUpdate: true,
recordUndo: false,
),
withUpdateSelection: false,
);
_textRobot.start(position: position);
records.add(

View File

@ -198,10 +198,12 @@ class _AiWriterScrollWrapperState extends State<AiWriterScrollWrapper> {
throttler.call(() {
if (aiWriterCubit.aiWriterNode != null) {
final path = aiWriterCubit.aiWriterNode!.path;
if (path.isNotEmpty) {
widget.editorState.updateSelectionWithReason(
Selection.collapsed(Position(path: path)),
);
}
}
});
}

View File

@ -58,7 +58,7 @@ class MarkdownTextRobot {
void start({
Position? position,
}) {
_insertPosition ??= position ?? editorState.selection?.start;
_insertPosition = position ?? editorState.selection?.start;
if (_enableDebug) {
Log.info(
@ -148,11 +148,15 @@ class MarkdownTextRobot {
}
}
void reset() {
void clear() {
_markdownText = '';
_insertedNodes = [];
}
void reset() {
_insertPosition = null;
}
Future<void> _refresh({
required bool inMemoryUpdate,
bool updateSelection = false,