dev→main: cursor-paged Patreon backfill + mobile modal fixes #70

Merged
bvandeusen merged 2 commits from dev into main 2026-06-05 11:10:02 -04:00
2 changed files with 37 additions and 5 deletions
Showing only changes of commit add1c1ad14 - Show all commits
+27 -4
View File
@@ -203,14 +203,37 @@ function nextFrame() {
}
@media (max-width: 900px) {
.fc-viewer__body { flex-direction: column; }
/* Side panel drops below the image — the next arrow uses the full width. */
.fc-viewer__nav--next { right: 16px; }
/* Stacked layout (operator-flagged 2026-06-05): pin the image pane and
its controls at the top while the metadata panel scrolls beneath it.
Previously the image + a 40vh panel shared one fixed viewport and the
controls could be pushed out of view; now the body scrolls and the
media is sticky, so the image + prev/next/close (and the integrity
badge) stay visible no matter how far the panel is scrolled. */
.fc-viewer__body {
flex-direction: column;
overflow-y: auto;
}
.fc-viewer__media {
flex: none;
height: 55vh;
position: sticky;
top: 0;
z-index: 1;
/* Opaque obsidian so the scrolling panel never bleeds through the
haze behind the pinned image. */
background: rgb(20, 23, 26);
}
.fc-viewer__side {
width: 100%;
max-height: 40vh;
max-height: none;
border-left: none;
border-top: 1px solid rgb(var(--v-theme-surface-light));
}
/* Re-center the prev/next arrows over the 55vh image band (their base
top:50% would land on the scrolling panel); next uses the full width
now that the panel is below, not beside. Close + integrity badge keep
their top:16px/72px — already within the pinned band. */
.fc-viewer__nav { top: 27.5vh; }
.fc-viewer__nav--next { right: 16px; }
}
</style>
@@ -65,8 +65,17 @@ const store = useTagStore()
// Vuetify's v-text-field exposes .focus() on the component instance;
// nextTick waits for the modal's mount to finish so the inner <input>
// element exists.
//
// EXCEPTION — not on mobile (operator-flagged 2026-06-05): focusing the
// field pops the soft keyboard, which shrinks the visual viewport and
// shoves the pinned image + its nav/close controls out of view. 900px is
// the modal's stacked-layout breakpoint (ImageViewer.vue). matchMedia is
// safe on plain HTTP (not a secure-context API).
const inputRef = ref(null)
onMounted(() => { nextTick(() => inputRef.value?.focus?.()) })
onMounted(() => {
if (window.matchMedia?.('(max-width: 900px)')?.matches) return
nextTick(() => inputRef.value?.focus?.())
})
// Single text input; no kind dropdown. Client-side mirror of the
// backend's parse_kind_prefix lives below — kept in sync with