Add manual image groups to the library
This commit is contained in:
+41
-7
@@ -128,6 +128,8 @@
|
||||
{{ folder.name }}{{ folder.protected && !folder.unlocked ? ' · locked' : '' }}
|
||||
</option>
|
||||
</select>
|
||||
<button v-if="canGroupSelection" type="button" class="rounded-lg border border-amber-300/40 px-2 py-1 text-amber-100 disabled:opacity-50" :disabled="groupBusy" @click="groupSelectedImages()">Group images</button>
|
||||
<button v-if="canUngroupSelection" type="button" class="text-amber-100 disabled:opacity-50" :disabled="groupBusy" @click="groupSelectedImages(true)">Ungroup</button>
|
||||
<button type="button" class="text-red-300 hover:text-red-200" @click="askRemoveSelected">Delete</button>
|
||||
<button type="button" class="text-zinc-400 hover:text-white" @click="clearSelection">Clear</button>
|
||||
</div>
|
||||
@@ -183,6 +185,7 @@
|
||||
class="h-full w-full object-cover"
|
||||
>
|
||||
<span v-else class="block h-full w-full bg-zinc-800" />
|
||||
<FamilyNav v-if="entry.kind === 'still'" :count="partCount(entry)" :index="cardImageIndex(entry)" @step="stepCardImage(entry, $event)" />
|
||||
<span class="absolute left-2 top-2 rounded-md bg-black/60 px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-zinc-200">{{ entry.kind === 'clip' ? 'Video' : entry.kind === 'track' ? 'Music' : 'Image' }}</span>
|
||||
<span
|
||||
v-if="partCount(entry) > 1"
|
||||
@@ -340,7 +343,9 @@
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="focusedParts.length > 1 && !detailsHidden" class="mt-3 grid grid-cols-5 gap-1.5">
|
||||
<details v-else-if="focusedParts.length > 1 && !detailsHidden" class="mt-3">
|
||||
<summary class="cursor-pointer text-sm text-amber-100">Expand images ({{ focusedParts.length }})</summary>
|
||||
<div class="mt-2 grid grid-cols-5 gap-1.5">
|
||||
<button
|
||||
v-for="part in focusedParts"
|
||||
:key="part.id"
|
||||
@@ -349,7 +354,7 @@
|
||||
:class="part.id === focusedPartId ? 'ring-2 ring-amber-300' : ''"
|
||||
@click="focusPart(part.id)"
|
||||
>
|
||||
<span v-if="'hideThumbnail' in part && part.hideThumbnail" class="sensitive-glow block aspect-square w-full" />
|
||||
<span v-if="('hideThumbnail' in part && part.hideThumbnail) || ('hideInput' in part && part.hideInput)" class="sensitive-glow block aspect-square w-full" />
|
||||
<img
|
||||
v-else
|
||||
:src="partThumb(part)"
|
||||
@@ -358,7 +363,8 @@
|
||||
loading="lazy"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<p v-else-if="focused.kind === 'clip' && focusedDisplayNodes.length > 1 && detailsHidden" class="mt-3 text-xs text-zinc-500">
|
||||
{{ focusedDisplayNodes.filter(node => node.type === 'clip').length }} parts hidden with preview
|
||||
</p>
|
||||
@@ -507,6 +513,7 @@ import {
|
||||
formatClipDuration,
|
||||
formatWhen,
|
||||
treeOrderedParts,
|
||||
wrappedIndex,
|
||||
type FamilyDisplayNode,
|
||||
type LibraryClip,
|
||||
type LibraryEntry,
|
||||
@@ -549,6 +556,33 @@ const sort = ref<LibrarySort>('date-desc')
|
||||
const searchDraft = ref('')
|
||||
const search = ref('')
|
||||
const selectedKeys = ref<string[]>([])
|
||||
const groupBusy = ref(false)
|
||||
const cardImages = ref<Record<string, number>>({})
|
||||
const canGroupSelection = computed(() => selectedEntries().length >= 2 && selectedEntries().every(entry => entry.kind === 'still'))
|
||||
const canUngroupSelection = computed(() => selectedEntries().length > 0 && selectedEntries().every(entry => entry.kind === 'still' && entry.stillGroup?.head.manualGroupId))
|
||||
function cardImageIndex(entry: LibraryEntry) {
|
||||
return (cardImages.value[entry.key] || 0) % Math.max(1, partCount(entry))
|
||||
}
|
||||
function stepCardImage(entry: LibraryEntry, delta: number) {
|
||||
cardImages.value[entry.key] = wrappedIndex(partCount(entry), cardImageIndex(entry), delta)
|
||||
if (focusedKey.value === entry.key) focusedPartId.value = entry.stillGroup!.parts[cardImageIndex(entry)].id
|
||||
}
|
||||
async function groupSelectedImages(ungroup = false) {
|
||||
if (groupBusy.value) return
|
||||
groupBusy.value = true
|
||||
try {
|
||||
const ids = selectedEntries().flatMap(entry => entry.stillGroup?.parts.map(part => part.id) || [])
|
||||
applyLibrary(await $fetch('/api/library/still-groups', { method: 'POST', body: { ids, ungroup } }))
|
||||
clearSelection()
|
||||
clearFocus()
|
||||
toast(ungroup ? 'Images ungrouped. Original iterations restored.' : 'Images grouped. Use the arrows to browse, or open the group to expand it.')
|
||||
} catch (error: any) {
|
||||
toast(error?.data?.statusMessage || 'Could not update image group')
|
||||
} finally {
|
||||
groupBusy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const lastSelectedKey = ref('')
|
||||
const selectMode = ref(false)
|
||||
const lightboxOpen = ref(false)
|
||||
@@ -796,11 +830,11 @@ function cardClass(entry: LibraryEntry) {
|
||||
function itemHidden(entry: LibraryEntry) {
|
||||
if (entry.kind === 'clip') return Boolean(entry.group?.head.hideThumbnail)
|
||||
if (entry.kind === 'track') return false
|
||||
return Boolean(entry.stillGroup?.head.hideInput)
|
||||
return Boolean(entry.stillGroup?.parts[cardImageIndex(entry)]?.hideInput)
|
||||
}
|
||||
|
||||
function thumbHidden(entry: LibraryEntry) {
|
||||
const id = entry.kind === 'clip' ? entry.group?.head.id : entry.stillGroup?.head.id
|
||||
const id = entry.kind === 'clip' ? entry.group?.head.id : entry.stillGroup?.parts[cardImageIndex(entry)]?.id
|
||||
if (id && forceHiddenIds.value.includes(id)) return true
|
||||
return itemHidden(entry) && !revealAll.value
|
||||
}
|
||||
@@ -852,7 +886,7 @@ async function toggleFocusedHidden() {
|
||||
function thumbSrc(entry: LibraryEntry) {
|
||||
if (entry.kind === 'clip' && entry.group) return `/api/library/clips/${entry.group.head.id}/thumb`
|
||||
if (entry.kind === 'track') return ''
|
||||
if (entry.stillGroup) return `/api/library/stills/${entry.stillGroup.head.id}?thumb=1`
|
||||
if (entry.stillGroup) return `/api/library/stills/${entry.stillGroup.parts[cardImageIndex(entry)].id}?thumb=1`
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -960,7 +994,7 @@ function onCardClick(entry: LibraryEntry, event: MouseEvent) {
|
||||
? (entry.group?.head.id || '')
|
||||
: entry.kind === 'track'
|
||||
? (entry.track?.id || '')
|
||||
: (entry.stillGroup?.head.id || '')
|
||||
: (entry.stillGroup?.parts[cardImageIndex(entry)]?.id || '')
|
||||
if (!sameCard) detailsHidden.value = itemHidden(entry) && !revealAll.value
|
||||
cancelRename()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user