Add output navigation preserving viewer visibility

This commit is contained in:
Towsty
2026-09-10 07:09:52 -05:00
parent 9b1d8ae798
commit 7e4b69677f
2 changed files with 74 additions and 5 deletions
+49 -5
View File
@@ -1261,6 +1261,11 @@
<div class="flex items-center justify-between gap-3">
<div class="flex items-center gap-3">
<h2 class="font-display text-2xl font-bold">Output</h2>
<div v-if="outputNavigationIndex >= 0 && outputNavigationItems.length > 1" class="flex items-center gap-2" aria-label="Browse outputs">
<button type="button" aria-label="Previous output" title="Previous output" class="h-9 w-9 rounded-full border border-white/20 hover:border-amber-300/60" @click="stepOutput(-1)">←</button>
<span class="text-xs tabular-nums text-zinc-400">{{ outputNavigationIndex + 1 }} / {{ outputNavigationItems.length }}</span>
<button type="button" aria-label="Next output" title="Next output" class="h-9 w-9 rounded-full border border-white/20 hover:border-amber-300/60" @click="stepOutput(1)">→</button>
</div>
<button
v-if="canRestoreOutput"
type="button"
@@ -1593,9 +1598,11 @@
Edited still appears here
</div>
<FamilyNav
:count="outputFamilyStills.length"
:index="outputFamilyIndex"
@step="stepOutputFamily"
:count="outputNavigationIndex >= 0 ? outputNavigationItems.length : 0"
:index="outputNavigationIndex"
prev-label="Previous output"
next-label="Next output"
@step="stepOutput"
/>
</div>
<div v-if="editResultUrl && !editLockedSave && !editAwaitingReveal" class="mt-4 flex flex-wrap gap-3">
@@ -3536,6 +3543,15 @@ const reviewingStillId = computed(() => {
if (outputPreviewKind.value !== 'edit' || editLockedSave.value) return ''
return stillIdFromLibraryUrl(editResultUrl.value)
})
type OutputNavigationItem = { kind: 'still'; item: LibraryStill } | { kind: 'clip'; item: LibraryClip }
const outputNavigationItems = computed<OutputNavigationItem[]>(() => libraryEntries.value.flatMap((entry): OutputNavigationItem[] =>
entry.kind === 'still'
? chronologicalStills(entry.stillGroup.parts).map(item => ({ kind: 'still' as const, item }))
: chronologicalParts(entry.group.parts).map(item => ({ kind: 'clip' as const, item }))
))
const outputNavigationIndex = computed(() => outputNavigationItems.value.findIndex(entry =>
entry.kind === 'still' ? entry.item.id === reviewingStillId.value : entry.item.id === reviewingClipId.value
))
const STUDIO_LIBRARY_CAP = 36
const visibleLibraryEntries = computed(() => libraryEntries.value.slice(0, STUDIO_LIBRARY_CAP))
const libraryCapped = computed(() => libraryEntries.value.length > STUDIO_LIBRARY_CAP)
@@ -5872,9 +5888,37 @@ function onStudioKey(event: KeyboardEvent) {
}
if (typingInField(event)) return
if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return
if (outputFamilyStills.value.length < 2) return
if (editLightboxUrl.value) {
if (outputFamilyStills.value.length < 2) return
event.preventDefault()
stepOutputFamily(event.key === 'ArrowLeft' ? -1 : 1)
return
}
if (outputNavigationIndex.value < 0 || outputNavigationItems.value.length < 2) return
event.preventDefault()
stepOutputFamily(event.key === 'ArrowLeft' ? -1 : 1)
stepOutput(event.key === 'ArrowLeft' ? -1 : 1)
}
function stepOutput(delta: number) {
const items = outputNavigationItems.value
const index = outputNavigationIndex.value
if (index < 0 || items.length < 2) return
const next = items[(index + delta + items.length) % items.length]
if (!next) return
const covered = outputPreviewKind.value === 'edit' ? editAwaitingReveal.value : awaitingReveal.value
if (next.kind === 'still') {
openStill(next.item)
lastV2StillId.value = next.item.id
editAwaitingReveal.value = covered
concealEditOutput.value = covered || next.item.hideInput === true
if (!editBusy.value) editStatusMessage.value = covered ? 'Hidden still ready' : 'Loaded from library'
} else {
player.value?.pause()
openClip(next.item)
awaitingReveal.value = covered
concealOutput.value = covered || next.item.hideThumbnail === true
if (!videoBusy.value) statusMessage.value = covered ? 'Hidden clip ready' : 'Loaded from library'
}
}
function showOutputStill(still: LibraryStill) {