Add same-mode extra passes to Image v2, and hide Shot script from image Recommend.
Library tiles show a check when selected, and xAIGen can hide a preview from the details pane. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+85
-11
@@ -162,14 +162,27 @@
|
||||
v-if="partCount(entry) > 1"
|
||||
class="absolute right-2 top-2 rounded-md bg-black/60 px-1.5 py-0.5 text-[10px] text-amber-100"
|
||||
>{{ partCount(entry) }}</span>
|
||||
<label class="absolute bottom-2 left-2" @click.stop>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="h-4 w-4 rounded border-white/20 bg-zinc-950 accent-amber-400"
|
||||
:checked="selectedKeys.includes(entry.key)"
|
||||
@click.prevent="onSelectClick(entry.key, $event)"
|
||||
<button
|
||||
type="button"
|
||||
role="checkbox"
|
||||
class="absolute bottom-2 left-2 flex h-4 w-4 items-center justify-center rounded-[4px] border shadow-[0_1px_4px_rgba(0,0,0,0.55)]"
|
||||
:class="selectedKeys.includes(entry.key)
|
||||
? 'border-white bg-amber-400 text-zinc-950'
|
||||
: 'border-white/30 bg-black/45'"
|
||||
:aria-checked="selectedKeys.includes(entry.key)"
|
||||
:aria-label="selectedKeys.includes(entry.key) ? 'Deselect' : 'Select'"
|
||||
@click.stop="onSelectClick(entry.key, $event)"
|
||||
>
|
||||
<svg
|
||||
v-if="selectedKeys.includes(entry.key)"
|
||||
class="h-3 w-3"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
>
|
||||
</label>
|
||||
<path d="M3.2 8.2 6.4 11.3 12.8 4.6" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="px-2.5 py-2">
|
||||
<p class="truncate text-xs font-medium text-zinc-100">{{ entry.name }}</p>
|
||||
@@ -184,8 +197,8 @@
|
||||
|
||||
<aside class="glass w-full shrink-0 overflow-y-auto rounded-3xl p-4 sm:p-5 lg:h-full lg:w-[22rem]">
|
||||
<template v-if="focused">
|
||||
<div class="overflow-hidden rounded-2xl bg-zinc-900">
|
||||
<span v-if="thumbHidden(focused)" class="sensitive-glow block aspect-video w-full" />
|
||||
<div class="relative overflow-hidden rounded-2xl bg-zinc-900">
|
||||
<span v-if="previewHidden" class="sensitive-glow block aspect-video w-full" />
|
||||
<video
|
||||
v-else-if="focused.kind === 'clip' && previewVideo"
|
||||
:src="previewVideo"
|
||||
@@ -194,8 +207,8 @@
|
||||
playsinline
|
||||
/>
|
||||
<img
|
||||
v-else-if="focused.kind === 'still' && focused.stillGroup"
|
||||
:src="`/api/library/stills/${focused.stillGroup.head.id}`"
|
||||
v-else-if="focused.kind === 'still' && previewStillSrc"
|
||||
:src="previewStillSrc"
|
||||
alt=""
|
||||
class="aspect-video w-full object-contain"
|
||||
>
|
||||
@@ -205,6 +218,24 @@
|
||||
alt=""
|
||||
class="aspect-video w-full object-cover"
|
||||
>
|
||||
<button
|
||||
v-if="showPrivacyToggles"
|
||||
type="button"
|
||||
class="absolute right-2 top-2 z-10 flex h-8 w-8 items-center justify-center rounded-full border border-white/20 bg-black/60 text-zinc-100 hover:border-amber-300/50 hover:text-amber-100 disabled:opacity-50"
|
||||
:title="previewHidden ? 'Show in library' : 'Hide in library'"
|
||||
:aria-label="previewHidden ? 'Show in library' : 'Hide in library'"
|
||||
:disabled="hideBusy"
|
||||
@click="togglePreviewHidden"
|
||||
>
|
||||
<svg v-if="previewHidden" class="h-4 w-4" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M3 3l18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
<path d="M10.6 10.7a2.5 2.5 0 0 0 3.7 3.6M9.9 5.2A11 11 0 0 1 12 5c6.5 0 10 7 10 7a18 18 0 0 1-4.1 4.8M6.7 6.8A18 18 0 0 0 2 12s3.5 7 10 7c1.3 0 2.5-.3 3.6-.7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<svg v-else class="h-4 w-4" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round" />
|
||||
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-if="renaming"
|
||||
@@ -321,6 +352,7 @@ import {
|
||||
type LibrarySort,
|
||||
type LibraryStill
|
||||
} from '~/utils/libraryBrowse'
|
||||
import { isXaigenStudio } from '~/utils/videoModels'
|
||||
|
||||
const CARD_MIN = 156
|
||||
const GAP = 10
|
||||
@@ -335,6 +367,10 @@ const filterOptions = [
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const instanceName = ref(String(runtimeConfig.public.instanceName || 'AIGen'))
|
||||
const showPrivacyToggles = computed(() => isXaigenStudio({
|
||||
studio: runtimeConfig.public.studio,
|
||||
instanceName: runtimeConfig.public.instanceName || instanceName.value
|
||||
}))
|
||||
const authEnabled = ref(false)
|
||||
const authMode = ref('')
|
||||
const userName = ref('')
|
||||
@@ -357,6 +393,7 @@ const renaming = ref(false)
|
||||
const renameDraft = ref('')
|
||||
const renameInput = ref<HTMLInputElement | null>(null)
|
||||
const deleteBusy = ref(false)
|
||||
const hideBusy = ref(false)
|
||||
const pendingDelete = ref<{ title: string; name: string; detail?: string; consequence: string; keys: string[] } | null>(null)
|
||||
const toasts = ref<Array<{ id: number; message: string }>>([])
|
||||
let toastId = 0
|
||||
@@ -406,6 +443,24 @@ const previewVideo = computed(() => {
|
||||
const id = focusedPartId.value || focused.value.group.head.id
|
||||
return `/api/library/clips/${id}/video`
|
||||
})
|
||||
const previewStillSrc = computed(() => {
|
||||
if (focused.value?.kind !== 'still' || !focused.value.stillGroup) return ''
|
||||
const id = focusedPartId.value || focused.value.stillGroup.head.id
|
||||
return `/api/library/stills/${id}`
|
||||
})
|
||||
const focusedPreviewItem = computed(() => {
|
||||
const entry = focused.value
|
||||
if (!entry) return null
|
||||
if (entry.kind === 'clip') {
|
||||
return entry.group?.parts.find(part => part.id === focusedPartId.value) || entry.group?.head || null
|
||||
}
|
||||
return entry.stillGroup?.parts.find(part => part.id === focusedPartId.value) || entry.stillGroup?.head || null
|
||||
})
|
||||
const previewHidden = computed(() => {
|
||||
const item = focusedPreviewItem.value
|
||||
if (!item) return false
|
||||
return 'hideThumbnail' in item ? Boolean(item.hideThumbnail) : Boolean(item.hideInput)
|
||||
})
|
||||
const focusedParts = computed(() => {
|
||||
if (focused.value?.kind === 'clip') return focused.value.group?.parts || []
|
||||
return focused.value?.stillGroup?.parts || []
|
||||
@@ -677,6 +732,25 @@ async function saveRename() {
|
||||
}
|
||||
}
|
||||
|
||||
async function togglePreviewHidden() {
|
||||
const entry = focused.value
|
||||
const item = focusedPreviewItem.value
|
||||
if (!entry || !item || hideBusy.value) return
|
||||
hideBusy.value = true
|
||||
const next = !previewHidden.value
|
||||
try {
|
||||
if (entry.kind === 'clip') {
|
||||
applyLibrary(await $fetch(`/api/library/clips/${item.id}`, { method: 'PATCH', body: { hideThumbnail: next } }))
|
||||
} else {
|
||||
applyLibrary(await $fetch(`/api/library/stills/${item.id}`, { method: 'PATCH', body: { hideInput: next } }))
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast(error?.data?.statusMessage || 'Could not update visibility')
|
||||
} finally {
|
||||
hideBusy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function askRemoveFocused() {
|
||||
if (!focused.value) return
|
||||
askRemoveKeys([focused.value.key])
|
||||
|
||||
Reference in New Issue
Block a user