@@ -215,6 +215,9 @@ class DrawLayer {
215215 /** @type {Set<HTMLDivElement> } */
216216 static #selections = new Set ( ) ;
217217
218+ /** @type {boolean } */
219+ static #isSelectingWithMouse = false ;
220+
218221 /** @type {Set<Element> } */
219222 static #textLayerSet = new Set ( ) ;
220223
@@ -245,15 +248,38 @@ class DrawLayer {
245248 DrawLayer . #textLayers. set ( textLayer , { drawLayer : this } ) ;
246249 DrawLayer . #textLayerSet. add ( textLayer ) ;
247250 this . #textLayer = textLayer ;
248- if (
249- DrawLayer . #textLayerSet. size === 1 &&
250- DrawLayer . #selectionChangeAC === null
251- ) {
251+ if ( DrawLayer . #selectionChangeAC === null ) {
252252 DrawLayer . #selectionChangeAC = new AbortController ( ) ;
253+ const { signal } = DrawLayer . #selectionChangeAC;
253254 document . addEventListener (
254255 "selectionchange" ,
255256 DrawLayer . #selectionChange. bind ( DrawLayer ) ,
256- { signal : DrawLayer . #selectionChangeAC. signal }
257+ { signal }
258+ ) ;
259+ // Track mouse selection state to preserve selections during
260+ // cross-boundary drags.
261+ document . addEventListener (
262+ "mousedown" ,
263+ ( ) => {
264+ DrawLayer . #isSelectingWithMouse = true ;
265+ } ,
266+ { signal }
267+ ) ;
268+ document . addEventListener (
269+ "mouseup" ,
270+ ( ) => {
271+ DrawLayer . #isSelectingWithMouse = false ;
272+ } ,
273+ { signal }
274+ ) ;
275+ // If the mouse is released outside the window, we may not get a
276+ // corresponding `mouseup` event.
277+ window . addEventListener (
278+ "blur" ,
279+ ( ) => {
280+ DrawLayer . #isSelectingWithMouse = false ;
281+ } ,
282+ { signal }
257283 ) ;
258284 }
259285 }
@@ -325,6 +351,15 @@ class DrawLayer {
325351 let { startContainer, startOffset, endContainer, endOffset } = range ;
326352 let startTextLayer = getTextLayer ( startContainer ) ;
327353 let endTextLayer = getTextLayer ( endContainer ) ;
354+ const startMissing = startTextLayer === null ;
355+ const endMissing = endTextLayer === null ;
356+
357+ // XOR case: exactly one boundary is outside tracked text layers.
358+ // In Firefox/Safari this can happen transiently while dragging outside
359+ // the page. Preserve the current overlay and exit early.
360+ if ( this . #isSelectingWithMouse && startMissing !== endMissing ) {
361+ return ;
362+ }
328363
329364 if ( selection . rangeCount === 1 ) {
330365 const { anchorNode, anchorOffset, focusNode, focusOffset } = selection ;
@@ -356,6 +391,7 @@ class DrawLayer {
356391 }
357392
358393 if ( ! startTextLayer || ! endTextLayer ) {
394+ // Any remaining partial/outside range can be ignored.
359395 continue ;
360396 }
361397 if ( endContainer . nodeType === Node . ELEMENT_NODE ) {
@@ -769,6 +805,7 @@ class DrawLayer {
769805 if ( DrawLayer . #textLayerSet. size === 0 ) {
770806 DrawLayer . #selectionChangeAC?. abort ( ) ;
771807 DrawLayer . #selectionChangeAC = null ;
808+ DrawLayer . #isSelectingWithMouse = false ;
772809 }
773810 }
774811 this . #textLayer = null ;
0 commit comments