fix(modal): Esc closes from tag input; Provenance cards scroll at ~2.5
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 22s
CI / backend-lint-and-test (push) Successful in 29s
CI / intimp (push) Successful in 3m54s
CI / intapi (push) Successful in 7m32s
CI / intcore (push) Successful in 8m16s

Two operator-flagged UX gaps from 2026-06-01:

1. **Esc trapped inside the tag-entry field**
   The autofocused TagAutocomplete input made Esc-to-close unreachable
   because ImageViewer's keydown handler bailed early on
   `isTextEntry(ev.target)` for every key including Escape. Now Escape
   closes the modal from text inputs too — except when a Vuetify
   overlay is open (FandomPicker, autocomplete dropdown, suggestion
   3-dot menu), in which case that overlay's own Esc-handling fires
   instead of closing the whole modal mid-interaction. Detected via
   `.v-overlay--active`. Arrow keys still gate on isTextEntry so the
   tag input handles typing without navigating images.

2. **Provenance cards expanded the side panel unbounded**
   When an image had many ImageProvenance entries the cards stack
   pushed the Tags section below the fold. Wrapped the cards in a
   `.fc-prov__cards` container with max-height 270px (≈2.5 cards) and
   thin overflow scrollbar. Title stays anchored at top; Tags panel
   sits at a consistent position below regardless of provenance count.
This commit is contained in:
2026-06-01 08:17:54 -04:00
parent 9564d073b9
commit 4c56cf121f
2 changed files with 28 additions and 3 deletions
+10 -1
View File
@@ -92,7 +92,16 @@ let prevBodyOverflow = null
// own keystrokes.
function onKeyDown(ev) {
if (ev.key === 'Escape') {
if (isTextEntry(ev.target)) return
// Escape closes the modal even from inside a text input — that's
// the universal "get me out of here" expectation, and the
// autofocused tag-entry field would otherwise trap focus with no
// visible escape (operator-flagged 2026-06-01). EXCEPTION: when a
// nested Vuetify overlay is open (v-menu autocomplete dropdown,
// FandomPicker v-dialog, per-suggestion 3-dot menu), let that
// overlay's own Esc handling fire instead of closing the whole
// modal mid-interaction. Vuetify marks open overlays with
// `.v-overlay--active`.
if (document.querySelector('.v-overlay--active')) return
ev.preventDefault()
emit('close')
} else if (ev.key === 'ArrowLeft') {
@@ -10,7 +10,11 @@
density="compact"
>{{ state.error }}</v-alert>
<template v-else>
<!-- Cards scroll independently of the section title + attachments
below them. Cap at ~2.5 cards visible (operator-asked 2026-06-01:
keeps the Tags section anchored below at a consistent position;
the half-visible third card hints there's more). -->
<div v-else class="fc-prov__cards">
<article
v-for="e in state.entries" :key="e.provenance_id" class="fc-prov__card"
>
@@ -58,7 +62,7 @@
</RouterLink>
</div>
</article>
</template>
</div>
<div v-if="attachments.length" class="fc-prov__attach">
<h4 class="fc-prov__attach-title">Attachments</h4>
@@ -159,6 +163,18 @@ function openPost(postId, artistId) {
color: rgb(var(--v-theme-on-surface));
margin-bottom: 12px;
}
.fc-prov__cards {
/* 2.5 cards-worth at the typical collapsed card height (~108px each
incl. 10px gap). Slightly under to ensure the third card's bottom
edge is clipped — the visual cue that there's more below. */
max-height: 270px;
overflow-y: auto;
/* Hairline scrollbar that doesn't compete with content. */
scrollbar-width: thin;
scrollbar-color: rgb(var(--v-theme-surface-light)) transparent;
/* Pad-right so the scrollbar gutter doesn't squeeze card borders. */
padding-right: 4px;
}
.fc-prov__card {
border: 1px solid rgb(var(--v-theme-surface-light));
border-radius: 6px; padding: 10px 12px; margin-bottom: 10px;