Reader: lange Notizen wurden im Rand zu früh ausgeblendet (SW v28)
All checks were successful
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 15s

_repositionMarginMarkers() filterte Einträge anhand eines festen ±60px-
Fensters um den reinen Ankerpunkt, bevor die tatsächliche Kartenhöhe bekannt
war. Für Notizen mit mehr Text als eine "Standard"-Karte reichte das nicht:
die Karte hätte teilweise schon sichtbar sein müssen, obwohl ihr Anker noch
weiter außerhalb lag — sie blieb beim Scrollen also länger verschwunden als
erwartet.

Vorfilter jetzt großzügig (mind. 400px bzw. die Höhe des sichtbaren
Bereichs), danach ein zweiter Cull-Durchgang mit den tatsächlich gemessenen
Kartenhöhen nach dem Stapeln — dadurch werden nur Einträge entfernt, die
wirklich komplett außerhalb liegen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
marwin 2026-08-04 14:09:02 +02:00
parent 9648730be6
commit f9573d3d62
2 changed files with 18 additions and 2 deletions

View file

@ -5386,6 +5386,13 @@ function _repositionMarginMarkers() {
if (!readerAnnotationsMarginOpen || currentPdfDoc) return; if (!readerAnnotationsMarginOpen || currentPdfDoc) return;
const areaRect = markersEl.getBoundingClientRect(); const areaRect = markersEl.getBoundingClientRect();
// A generous pre-filter band: a note card can be considerably taller than
// its own anchor point, so a card whose anchor is somewhat outside the
// strictly-visible area can still be partially (or fully) on-screen once
// rendered. A tight band here excluded it before it ever got a chance to
// be measured — the exact bug reported ("tall notes stay hidden until
// scrolled much further than expected").
const band = Math.max(areaRect.height, 400);
const entries = []; const entries = [];
for (const h of currentHighlights) { for (const h of currentHighlights) {
let range; let range;
@ -5394,13 +5401,14 @@ function _repositionMarginMarkers() {
const rect = range.getBoundingClientRect(); const rect = range.getBoundingClientRect();
if (!rect.width && !rect.height) continue; // detached/invalid if (!rect.width && !rect.height) continue; // detached/invalid
const y = rect.top + rect.height / 2 - areaRect.top; const y = rect.top + rect.height / 2 - areaRect.top;
if (y < -60 || y > areaRect.height + 60) continue; // off-screen band if (y < -band || y > areaRect.height + band) continue;
entries.push({h, y}); entries.push({h, y});
} }
entries.sort((a, b) => a.y - b.y); entries.sort((a, b) => a.y - b.y);
let nextTop = -Infinity; let nextTop = -Infinity;
const GAP = 4; const GAP = 4;
const rendered = [];
for (const {h, y} of entries) { for (const {h, y} of entries) {
const hasText = !!h.note; const hasText = !!h.note;
const el = document.createElement('div'); const el = document.createElement('div');
@ -5420,6 +5428,14 @@ function _repositionMarginMarkers() {
const measured = el.getBoundingClientRect().height || (hasText ? 20 : 12); const measured = el.getBoundingClientRect().height || (hasText ? 20 : 12);
nextTop = top + measured + GAP; nextTop = top + measured + GAP;
rendered.push({el, top, measured});
}
// Now that actual card heights are known, do the real cull: drop anything
// that ended up entirely outside the visible band after stacking (e.g.
// pushed down by a very tall card above it).
for (const {el, top, measured} of rendered) {
if (top + measured < -40 || top > areaRect.height + 40) el.remove();
} }
} }

View file

@ -2,7 +2,7 @@
* diora service worker caches the app shell for offline use. * diora service worker caches the app shell for offline use.
*/ */
const CACHE = 'diora-v27'; const CACHE = 'diora-v28';
const PODCAST_CACHE = 'diora-podcast-v1'; const PODCAST_CACHE = 'diora-podcast-v1';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',