From 40a83ec35078441d933e0fc042dae2f17d509337 Mon Sep 17 00:00:00 2001 From: Marty Date: Wed, 6 May 2026 13:51:43 -0400 Subject: [PATCH 1/2] fix: show selection popup below text on mobile (Android) On touch devices the popup now always appears below the selection, avoiding overlap with Android's native copy/paste action bar. Co-Authored-By: Claude Sonnet 4.6 --- .../app/features/readers/ebook-reader/core/event.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts b/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts index 8b88bbbb9..ad4714cfe 100644 --- a/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts +++ b/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts @@ -395,7 +395,8 @@ export class ReaderEventService { } const minSpaceAbove = 120; - const showBelow = selectionTop < minSpaceAbove; + const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + const showBelow = isMobile || selectionTop < minSpaceAbove; let popupY: number; if (showBelow) { From eed8dac0ca0f51cf1d905fc94036b9087d0279c4 Mon Sep 17 00:00:00 2001 From: Marty Date: Wed, 6 May 2026 14:00:42 -0400 Subject: [PATCH 2/2] fix: run text-selected emission inside NgZone for immediate change detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iframe document event listeners run outside Angular's Zone.js, so the popup state was being set but Angular never ran change detection — the popup only appeared after an unrelated Angular interaction (scroll, tap). Wrapping the text-selected emission in ngZone.run() triggers immediate change detection so the popup renders at the same time as the selection. Co-Authored-By: Claude Sonnet 4.6 --- .../readers/ebook-reader/core/event.service.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts b/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts index ad4714cfe..704b8d988 100644 --- a/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts +++ b/booklore-ui/src/app/features/readers/ebook-reader/core/event.service.ts @@ -1,4 +1,4 @@ -import {inject, Injectable} from '@angular/core'; +import {inject, Injectable, NgZone} from '@angular/core'; import {Subject} from 'rxjs'; import {ReaderAnnotationService} from '../features/annotations/annotation-renderer.service'; @@ -33,6 +33,7 @@ export class ReaderEventService { private readonly SWIPE_THRESHOLD_PX = 50; private annotationService = inject(ReaderAnnotationService); + private ngZone = inject(NgZone); private view: any; private viewCallbacks: ViewCallbacks | null = null; @@ -407,10 +408,12 @@ export class ReaderEventService { popupX = Math.max(100, Math.min(popupX, window.innerWidth - 150)); - this.eventSubject.next({ - type: 'text-selected', - detail: {text, cfi, range, index}, - popupPosition: {x: popupX, y: popupY, showBelow} + this.ngZone.run(() => { + this.eventSubject.next({ + type: 'text-selected', + detail: {text, cfi, range, index}, + popupPosition: {x: popupX, y: popupY, showBelow} + }); }); } }, 10);