2 Commits

Author SHA1 Message Date
H1K0 528b7004e3 fix(frontend): open original only when tapping the preview image
deploy / deploy (push) Successful in 1m24s
The preview area was one big link, so a click anywhere in it — including
the black letterbox margins — opened the original. Make the link
pointer-events:none and re-enable pointer-events on the image itself, so
"open original" fires only on the image, not the surrounding margins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 22:36:16 +03:00
H1K0 41c4667eb4 feat(frontend): enlarge viewer prev/next into full-height tap zones
Paging previously required hitting a small arrow circle pinned near each
edge. Replace them with full-height left/right tap zones (30%, capped at
220px) so a tap anywhere on that side pages. The arrow stays as a chip
near the edge with a hover-only hint gradient; the zones sit above the
image, and the "Replacing…" overlay is lifted above them so an in-flight
content replace still blocks paging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 22:35:47 +03:00
@@ -499,39 +499,43 @@
</div> </div>
{/if} {/if}
<!-- Prev / Next --> <!-- Prev / Next: the whole left/right side of the preview is a tap zone -->
{#if prevId} {#if prevId}
<button <button
class="nav-btn nav-prev" class="nav-zone nav-prev"
onclick={() => prevId && onNavigate(prevId)} onclick={() => prevId && onNavigate(prevId)}
aria-label="Previous file" aria-label="Previous file"
> >
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"> <span class="nav-chip">
<path <svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
d="M11 3L5 9L11 15" <path
stroke="currentColor" d="M11 3L5 9L11 15"
stroke-width="2" stroke="currentColor"
stroke-linecap="round" stroke-width="2"
stroke-linejoin="round" stroke-linecap="round"
/> stroke-linejoin="round"
</svg> />
</svg>
</span>
</button> </button>
{/if} {/if}
{#if nextId} {#if nextId}
<button <button
class="nav-btn nav-next" class="nav-zone nav-next"
onclick={() => nextId && onNavigate(nextId)} onclick={() => nextId && onNavigate(nextId)}
aria-label="Next file" aria-label="Next file"
> >
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true"> <span class="nav-chip">
<path <svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">
d="M7 3L13 9L7 15" <path
stroke="currentColor" d="M7 3L13 9L7 15"
stroke-width="2" stroke="currentColor"
stroke-linecap="round" stroke-width="2"
stroke-linejoin="round" stroke-linecap="round"
/> stroke-linejoin="round"
</svg> />
</svg>
</span>
</button> </button>
{/if} {/if}
</div> </div>
@@ -756,15 +760,17 @@
overflow: hidden; overflow: hidden;
} }
/* Whole preview area is a link: click opens the original in a new tab. */ /* The link fills the area for centring, but only the image itself is
clickable (pointer-events below) — tapping the black margins does nothing,
so "open original" fires only on the preview. */
.preview-link { .preview-link {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: zoom-in;
text-decoration: none; text-decoration: none;
pointer-events: none;
} }
.preview-img { .preview-img {
@@ -772,12 +778,14 @@
max-height: 100%; max-height: 100%;
object-fit: contain; object-fit: contain;
display: block; display: block;
cursor: zoom-in;
pointer-events: auto; /* only the image opens the original */
} }
.preview-busy { .preview-busy {
position: absolute; position: absolute;
inset: 0; inset: 0;
z-index: 2; z-index: 4; /* above the nav zones — an in-flight replace blocks paging */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -818,33 +826,58 @@
background-color: #1a1010; background-color: #1a1010;
} }
/* ---- Nav buttons ---- */ /* ---- Nav zones ----
.nav-btn { Each covers the full-height left/right portion of the preview so paging
only needs a tap on that side, not a precise hit on the arrow. They sit
above the image, so over a full-width image the sides page and the centre
still opens the original. The dark hint gradient shows on hover only, to
keep photos clean on touch where there is no hover. */
.nav-zone {
position: absolute; position: absolute;
top: 50%; top: 0;
transform: translateY(-50%); bottom: 0;
width: 40px; width: 30%;
height: 40px; max-width: 220px;
border-radius: 50%;
border: none; border: none;
background-color: rgba(0, 0, 0, 0.55); background: none;
color: #fff; padding: 0 12px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
cursor: pointer; cursor: pointer;
transition: background-color 0.15s; z-index: 3;
} -webkit-tap-highlight-color: transparent;
.nav-btn:hover {
background-color: rgba(0, 0, 0, 0.8);
} }
.nav-prev { .nav-prev {
left: 10px; left: 0;
justify-content: flex-start;
} }
.nav-next { .nav-next {
right: 10px; right: 0;
justify-content: flex-end;
}
.nav-prev:hover {
background: linear-gradient(to right, rgba(0, 0, 0, 0.3), transparent);
}
.nav-next:hover {
background: linear-gradient(to left, rgba(0, 0, 0, 0.3), transparent);
}
.nav-chip {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.55);
color: #fff;
transition: background-color 0.15s;
}
.nav-zone:hover .nav-chip {
background-color: rgba(0, 0, 0, 0.8);
} }
/* ---- Metadata panel ---- */ /* ---- Metadata panel ---- */