From 919569aac2d46129a7c8f7fa9f6c35bda1049d61 Mon Sep 17 00:00:00 2001 From: John Wooten Date: Mon, 26 Oct 2026 13:06:45 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=A3=EF=B8=8F=20Refactor=20minimap=20di?= =?UTF-8?q?splay=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/viewer/annotation-layer.css | 3 ++ src/viewer/navigation/minimap.css | 6 ++-- src/viewer/navigation/minimap.js | 17 +++------- tests/minimap.test.mjs | 52 ++++++++++++++----------------- 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/viewer/annotation-layer.css b/src/viewer/annotation-layer.css index 0611b2b..49bf3ac 100644 --- a/src/viewer/annotation-layer.css +++ b/src/viewer/annotation-layer.css @@ -18,6 +18,9 @@ .annotation-layer .linkAnnotation { pointer-events: auto; + /* PDF.js adds inline borders for LaTeX/hyperref links. Keep the hit areas + without drawing boxes around citations and footnote markers. */ + border: 0 !important; } .annotation-layer .linkAnnotation > a { diff --git a/src/viewer/navigation/minimap.css b/src/viewer/navigation/minimap.css index 833cc21..166baa9 100644 --- a/src/viewer/navigation/minimap.css +++ b/src/viewer/navigation/minimap.css @@ -210,11 +210,11 @@ html.minimap-disabled body::-webkit-scrollbar-thumb { height: 100%; } -:is(.minimap-local, .minimap-local-active) .minimap-page { +.minimap-local .minimap-page { z-index: 1; } -:is(.minimap-local, .minimap-local-active) .minimap-local-thumbnail { +.minimap-local .minimap-local-thumbnail { display: block; } @@ -256,7 +256,7 @@ html.minimap-disabled body::-webkit-scrollbar-thumb { filter: invert(1) brightness(2.4) contrast(1.1) invert(1); } -:is(.minimap-local, .minimap-local-active) .minimap-strip { +.minimap-local .minimap-strip { opacity: 1; } diff --git a/src/viewer/navigation/minimap.js b/src/viewer/navigation/minimap.js index 141d49d..0e1238c 100644 --- a/src/viewer/navigation/minimap.js +++ b/src/viewer/navigation/minimap.js @@ -86,13 +86,8 @@ function minimapMode() { return document.documentElement.classList.contains("minimap-local") ? "local" : "overview"; } -function displayedMinimapMode(widthScaledHeight, trackHeight) { - return minimapMode() === "local" || widthScaledHeight <= trackHeight ? "local" : "overview"; -} - function localMinimapActive() { - return minimapMode() === "local" || - document.documentElement.classList.contains("minimap-local-active"); + return minimapMode() === "local"; } function setMinimapAccessibility() { @@ -295,13 +290,11 @@ function syncMinimap() { const widthScale = thumbnailWidth / pageWidth; const widthScaledHeights = pages.map((page) => page.getBoundingClientRect().height * widthScale); const widthScaledHeight = widthScaledHeights.reduce((total, height) => total + height, 0); - const displayMode = displayedMinimapMode(widthScaledHeight, trackHeight); - document.documentElement.classList.toggle( - "minimap-local-active", - displayMode === "local" && minimapMode() !== "local", - ); + const displayMode = minimapMode(); + // Overview spans the full track, including short PDFs on tall monitors. + // Local mode retains thumbnails at their natural width-scaled height. const heightCompression = - displayMode === "overview" && widthScaledHeight > trackHeight + displayMode === "overview" && widthScaledHeight > 0 ? trackHeight / widthScaledHeight : 1; const scale = widthScale * heightCompression; diff --git a/tests/minimap.test.mjs b/tests/minimap.test.mjs index 714e03d..61dcc40 100644 --- a/tests/minimap.test.mjs +++ b/tests/minimap.test.mjs @@ -93,48 +93,42 @@ test('switching to local view keeps thumbnails unclipped at the middle and end', } }); -test('short overview documents temporarily use local navigator sizing until the viewport needs compression', () => { - const f = fixture(2); - assert.ok(!f.classes.has('minimap-local')); - assert.ok(f.classes.has('minimap-local-active')); - assert.equal(f.storedValues.get('pdf-viewer-minimap-mode'), undefined); - assert.equal(parseFloat(f.tiles[0].style.top), 0); - assert.equal(parseFloat(f.tiles[0].style.height), 112); - assert.ok(parseFloat(f.tiles[1].style.top) + 112 < f.track.clientHeight); - - f.window.innerHeight = 200; - f.track.clientHeight = 148; - f.sync(); - assert.ok(!f.classes.has('minimap-local-active')); - assert.ok(parseFloat(f.tiles[0].style.height) < 112); - assert.ok(Math.abs( - parseFloat(f.tiles[1].style.top) + parseFloat(f.tiles[1].style.height) - f.track.clientHeight, - ) < 0.00001); - - f.window.innerHeight = 1600; - f.track.clientHeight = 1548; - f.sync(); - assert.ok(f.classes.has('minimap-local-active')); - assert.equal(parseFloat(f.tiles[0].style.height), 112); +test('short overview documents fill the track on tall monitors and after resizing', () => { + for (const count of [1, 2, 9]) { + const f = fixture(count, 1900); + for (const height of [1900, 200, 1600]) { + f.window.innerHeight = height; + f.track.clientHeight = height - 52; + f.sync(); + const last = f.tiles.at(-1); + const lastBottom = parseFloat(last.style.top) + parseFloat(last.style.height); + assert.ok(Math.abs(lastBottom - f.track.clientHeight) < 0.00001); + assert.ok(!f.classes.has('minimap-local')); + assert.equal(f.storedValues.get('pdf-viewer-minimap-mode'), undefined); + assert.equal(vm.runInContext('mapOffset', f.context), 0); + vm.runInContext('scrollFromViewportTop(mapHeight - viewportHeight)', f.context); + assert.equal(f.window.scrollY, Math.max(count * 1420 - height, 0)); + } + } }); -test('short minimaps gradually fade their document-colored tail to half opacity', () => { - const f = fixture(2); +test('explicit local mode keeps natural thumbnail heights and fades the unused tail', () => { + const f = fixture(2, 1900, { 'pdf-viewer-minimap-mode': 'local' }); + assert.ok(f.classes.has('minimap-local')); assert.equal(f.track.style['--minimap-content-height'], '224px'); assert.equal(f.track.style['--minimap-tail-fade-height'], '112px'); assert.match(styles, /\.minimap::after\s*\{[\s\S]*?top:\s*min\(var\(--minimap-content-height, 100%\), 100%\)/); - assert.match(styles, /\.minimap::after\s*\{[\s\S]*?right:\s*8px;[\s\S]*?left:\s*8px;/); - assert.match(styles, /\.minimap::after\s*\{[\s\S]*?background:\s*linear-gradient\(\s*to bottom,\s*var\(--page-bg\) 0,\s*color-mix\(in srgb, var\(--page-bg\) 50%, transparent\)\s*max\(100%, var\(--minimap-tail-fade-height, 0px\)\)\s*\)/); assert.match(styles, /\.minimap-pages\s*\{[\s\S]*?z-index:\s*1/); f.window.innerHeight = 200; f.track.clientHeight = 148; f.sync(); + assert.equal(parseFloat(f.tiles[0].style.height), 112); assert.equal(f.track.style['--minimap-content-height'], '148px'); }); -test('minimap packs page thumbnails without viewer gaps', () => { - const f = fixture(3); +test('local minimap packs page thumbnails without viewer gaps', () => { + const f = fixture(3, 900, { 'pdf-viewer-minimap-mode': 'local' }); assert.equal(parseFloat(f.tiles[0].style.top), 0); assert.equal(parseFloat(f.tiles[1].style.top), 112); assert.equal(parseFloat(f.tiles[2].style.top), 224);