diff --git a/components/ImagePickerModal.vue b/components/ImagePickerModal.vue index 49c3b27..79b8e1d 100644 --- a/components/ImagePickerModal.vue +++ b/components/ImagePickerModal.vue @@ -149,8 +149,34 @@
- {{ still.name || still.filename }} - + + {{ stillLabel(still) }} +
+ + +
@@ -210,6 +236,10 @@ const revealAll = ref(false) const revealedIds = ref([]) const picking = ref(false) const keepingId = ref('') +const renamingId = ref('') +const renameDraft = ref('') +const renameInput = ref(null) +const renameSaving = ref(false) function onKey(event: KeyboardEvent) { if (!props.open) return @@ -316,6 +346,45 @@ function onDrop(event: DragEvent) { if (next) void ingest(next) } +function stillLabel(still: PickerStill) { + return (still.name || still.filename.replace(/\.[a-z0-9]+$/i, '') || 'Untitled still').trim() +} + +function startRename(still: PickerStill) { + renamingId.value = still.id + renameDraft.value = stillLabel(still) + nextTick(() => { + renameInput.value?.focus() + renameInput.value?.select() + }) +} + +function cancelRename() { + renamingId.value = '' +} + +async function saveRename(still: PickerStill) { + if (renamingId.value !== still.id || renameSaving.value) return + const name = renameDraft.value.trim().slice(0, 80) + if (!name) { + emit('error', 'A name is required') + return + } + if (name === stillLabel(still)) { + cancelRename() + return + } + renameSaving.value = true + try { + emit('updated', await $fetch(`/api/library/stills/${still.id}`, { method: 'PATCH', body: { name } })) + cancelRename() + } catch (error: any) { + emit('error', error?.data?.statusMessage || 'Could not rename still') + } finally { + renameSaving.value = false + } +} + async function keepAsInput(still: PickerStill) { if (keepingId.value) return keepingId.value = still.id diff --git a/pages/index.vue b/pages/index.vue index 1ef6f29..149025a 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -105,10 +105,15 @@ diff --git a/pages/library.vue b/pages/library.vue index 95fa2e7..1a303c4 100644 --- a/pages/library.vue +++ b/pages/library.vue @@ -81,6 +81,25 @@ + @@ -380,6 +399,7 @@ const renameDraft = ref('') const renameInput = ref(null) const deleteBusy = ref(false) const detailsHidden = ref(false) +const revealAll = ref(false) const pendingDelete = ref<{ title: string; name: string; detail?: string; consequence: string; keys: string[] } | null>(null) const keepingInputBusy = ref(false) const toasts = ref>([]) @@ -549,11 +569,24 @@ function cardClass(entry: LibraryEntry) { return 'border-white/10' } -function thumbHidden(entry: LibraryEntry) { +function itemHidden(entry: LibraryEntry) { if (entry.kind === 'clip') return Boolean(entry.group?.head.hideThumbnail) return Boolean(entry.stillGroup?.head.hideInput) } +function thumbHidden(entry: LibraryEntry) { + return itemHidden(entry) && !revealAll.value +} + +const hasHiddenThumbs = computed(() => entries.value.some(entry => itemHidden(entry))) + +function toggleRevealAll() { + revealAll.value = !revealAll.value + if (focused.value && itemHidden(focused.value)) { + detailsHidden.value = !revealAll.value + } +} + function thumbSrc(entry: LibraryEntry) { if (entry.kind === 'clip' && entry.group?.head.hasThumbnail) return `/api/library/clips/${entry.group.head.id}/thumb` if (entry.stillGroup) return `/api/library/stills/${entry.stillGroup.head.id}?thumb=1` @@ -639,7 +672,7 @@ function onCardClick(entry: LibraryEntry, event: MouseEvent) { const sameCard = focusedKey.value === entry.key focusedKey.value = entry.key focusedPartId.value = entry.kind === 'clip' ? (entry.group?.head.id || '') : (entry.stillGroup?.head.id || '') - if (!sameCard) detailsHidden.value = thumbHidden(entry) + if (!sameCard) detailsHidden.value = itemHidden(entry) && !revealAll.value cancelRename() }