From 6eacce7349679a4c61762e7fd32bee8f37b52ddc Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Fri, 13 Nov 2026 20:04:20 +0530 Subject: [PATCH] fix(editor): clear native selection handles when the editor loses focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the DOM renderer, Android kept drawing its native selection handles when focus moved from the editor to the ⋮ menu or a page such as Settings, because the DOM selection stayed inside the editor. The handles floated above the menu and page and could still be dragged. Clear a DOM selection inside the editor when focus leaves it for another element of the app. drawSelection() keeps showing CodeMirror's selection, and CodeMirror writes the DOM selection again when the editor is focused. Fixes #3059 --- src/cm/baseExtensions.ts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/cm/baseExtensions.ts b/src/cm/baseExtensions.ts index c429b2063..4ea778b30 100644 --- a/src/cm/baseExtensions.ts +++ b/src/cm/baseExtensions.ts @@ -20,6 +20,7 @@ import { crosshairCursor, drawSelection, dropCursor, + EditorView, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, @@ -49,6 +50,30 @@ export interface BaseExtensionOptions { highlightSelectionMatches?: boolean; } +/** + * Android draws its native selection handles for a DOM selection inside + * the editor even after focus moves to a menu or page over it, so they + * float above the ⋮ menu or Settings and can still be dragged there + * (Acode issue #3059). Focus leaving the editor doesn't clear the DOM + * selection, so this does. With `drawSelection()` the visible selection + * is CodeMirror's own layer and stays; CodeMirror writes the DOM selection + * again from its state when the editor gets focus back. + */ +const releaseNativeSelection = EditorView.domEventHandlers({ + blur(_event, view) { + // Focus leaving the app (backgrounded) keeps it as it was. + if (!document.hasFocus()) return false; + const selection = document.getSelection(); + if ( + selection?.rangeCount && + view.contentDOM.contains(selection.anchorNode) + ) { + selection.removeAllRanges(); + } + return false; + }, +}); + /** * Base extensions roughly matching the useful parts of CodeMirror's basicSetup */ @@ -77,7 +102,9 @@ export default function createBaseExtensions( indentationFolding, ); } - if (!canvas) extensions.push(drawSelection(), dropCursor()); + if (!canvas) { + extensions.push(drawSelection(), dropCursor(), releaseNativeSelection); + } extensions.push(EditorState.allowMultipleSelections.of(true)); if (autoIndent) extensions.push(indentOnInput()); extensions.push(