Fix more Scribe mobile issues

- Add getSavedSelection to check saved selection without consuming it
- Distinguish between setText (replace all) and insertText (at cursor) operations
- Fix replace callback to use setText when no selection exists
- Clean up saved selection state after restoration in HTML utils
- Move mobile editor focus and selection restore to appropriate callbacks
This commit is contained in:
Théo Poizat
2026-01-21 16:19:04 +01:00
committed by Dat H. Pham
parent be7f28f270
commit b5606626f4
2 changed files with 71 additions and 23 deletions
@@ -34,11 +34,7 @@ extension HandleAiScribeInComposerExtension on ComposerController {
}
}
Future<void> insertTextInEditor(String text) async {
if (PlatformInfo.isMobile) {
await ensureMobileEditorFocused();
}
Future<void> insertTextInEditor(String text) async {
try {
final htmlContent = StringConvert.convertTextContentToHtmlContent(text);
@@ -57,6 +53,18 @@ extension HandleAiScribeInComposerExtension on ComposerController {
}
}
Future<void> setTextInEditor(String text) async {
try {
if (PlatformInfo.isWeb) {
richTextWebController?.editorController.setText(text);
} else {
await richTextMobileTabletController?.htmlEditorApi?.setText(text);
}
} catch (e) {
logError('$runtimeType::insertTextInEditor:Exception = $e');
}
}
Future<void> collapseSelection() async {
try {
if (PlatformInfo.isWeb) {
@@ -117,6 +125,28 @@ extension HandleAiScribeInComposerExtension on ComposerController {
}
}
Future<String> getSavedSelection() async {
try {
if (PlatformInfo.isWeb) {
final result = await richTextWebController?.editorController.evaluateJavascriptWeb(
HtmlUtils.getSavedSelection.name,
hasReturnValue: true,
);
return result;
} else {
final result = await richTextMobileTabletController?.htmlEditorApi?.webViewController
.evaluateJavascript(
source: HtmlUtils.getSavedSelection.script,
);
return result;
}
} catch (e) {
logError('$runtimeType::getSavedSelection:Exception = $e');
return "";
}
}
Future<void> clearSavedSelection() async {
try {
if (PlatformInfo.isWeb) {
@@ -158,21 +188,15 @@ extension HandleAiScribeInComposerExtension on ComposerController {
}
}
Future<void> clearTextInEditor() async {
try {
if (PlatformInfo.isWeb) {
richTextWebController?.editorController.setText('');
} else {
await richTextMobileTabletController?.htmlEditorApi?.setText('');
}
} catch (e) {
logWarning('$runtimeType::clearTextInEditor:Exception = $e');
}
}
// Ensure we only insert at cursor position by collapsing selection before inserting
Future<void> onInsertTextCallback(String text) async {
if (PlatformInfo.isMobile) {
await ensureMobileEditorFocused();
await restoreSelection();
}
await collapseSelection();
await insertTextInEditor(text);
}
@@ -180,13 +204,25 @@ extension HandleAiScribeInComposerExtension on ComposerController {
Future<void> onReplaceTextCallback(String text) async {
final selection = editorTextSelection.value?.selectedText;
final isSelectionRestored = PlatformInfo.isMobile ? await restoreSelection() : "";
final savedSelection = PlatformInfo.isMobile ? await getSavedSelection() : "";
if ((selection == null || selection.isEmpty) && isSelectionRestored.isEmpty) {
await clearTextInEditor();
final shouldReplaceEverything = (selection == null || selection.isEmpty) && savedSelection.isEmpty;
if (shouldReplaceEverything) {
try {
await setTextInEditor(text);
} catch (e) {
logError('$runtimeType::onReplaceTextCallback:Exception = $e');
}
} else {
if (PlatformInfo.isMobile) {
await ensureMobileEditorFocused();
await restoreSelection();
}
await insertTextInEditor(text);
}
await insertTextInEditor(text);
}
Future<void> openAIAssistantModal(Offset? position, Size? size) async {