Make family branches readable and fix Library part previews.
Lead rows with Start/pN labels and fork headers, collapse hidden frame strips to one glow, and stop Library part thumbs from leaking or cloning the Start frame. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+4
-3
@@ -1785,7 +1785,7 @@
|
||||
<template v-for="node in familyDisplayNodes(entry.group.parts)" :key="node.key">
|
||||
<li
|
||||
v-if="node.type === 'fork'"
|
||||
class="rounded-lg px-2 py-1 text-[11px] font-medium uppercase tracking-wide text-amber-200/70"
|
||||
class="rounded-lg border-l-2 border-amber-300/50 bg-amber-400/5 px-2 py-1.5 text-[11px] font-semibold uppercase tracking-wide text-amber-100"
|
||||
:style="node.depth ? { marginLeft: `${Math.min(node.depth, 6) * 0.85}rem` } : undefined"
|
||||
>
|
||||
{{ node.label }}
|
||||
@@ -1823,7 +1823,7 @@
|
||||
@blur="saveRename(node.clip)"
|
||||
>
|
||||
<p v-else class="min-w-0 flex-1 truncate text-sm font-medium">
|
||||
{{ node.clip.name || node.clip.prompt }}
|
||||
<span class="text-amber-100">{{ clipFamilyPartTitle(node.clip, entry.group.parts) }}</span>
|
||||
<span v-if="node.fromLabel" class="ml-1.5 text-[11px] font-normal text-amber-200/80">{{ node.fromLabel }}</span>
|
||||
<span v-if="node.branchLabel" class="ml-1.5 text-[11px] font-normal text-zinc-400">{{ node.branchLabel }}</span>
|
||||
<span v-if="extendOpen && node.clip.id === extendClipId" class="ml-1.5 text-[10px] font-semibold uppercase tracking-wide text-amber-200">extending</span>
|
||||
@@ -1835,7 +1835,7 @@
|
||||
@copy="copyPrompt"
|
||||
/>
|
||||
</div>
|
||||
<p class="truncate text-xs text-zinc-500">{{ clipMetaLine(node.clip) }}</p>
|
||||
<p class="truncate text-xs text-zinc-500">{{ node.clip.name || node.clip.prompt }} · {{ clipMetaLine(node.clip) }}</p>
|
||||
<PermanenceRefStrip
|
||||
v-if="!node.clip.hideThumbnail"
|
||||
class="mt-2"
|
||||
@@ -2258,6 +2258,7 @@ import { parseImageAspect, generateSizeForAspect } from '~/utils/imageAspect'
|
||||
import { buildOutputBaseName, nextUniqueName } from '~/utils/outputNames'
|
||||
import { emptyKeepPrompts, insertKeepSnippet, normalizeKeepPrompts, type KeepPrompt, type KeepPromptLists } from '~/utils/keepPrompts'
|
||||
import {
|
||||
clipFamilyPartTitle,
|
||||
clipPartLabel,
|
||||
familyBaseName,
|
||||
familyBranchSummary,
|
||||
|
||||
+33
-8
@@ -303,11 +303,11 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="focused.kind === 'clip' && focusedDisplayNodes.length > 1" class="mt-3 space-y-1.5">
|
||||
<div v-if="focused.kind === 'clip' && focusedDisplayNodes.length > 1 && !detailsHidden" class="mt-3 space-y-1.5">
|
||||
<template v-for="node in focusedDisplayNodes" :key="node.key">
|
||||
<p
|
||||
v-if="node.type === 'fork'"
|
||||
class="px-1 text-[11px] font-medium uppercase tracking-wide text-amber-200/70"
|
||||
class="rounded-md border-l-2 border-amber-300/50 bg-amber-400/5 px-2 py-1 text-[11px] font-semibold uppercase tracking-wide text-amber-100"
|
||||
:style="node.depth ? { marginLeft: `${Math.min(node.depth, 6) * 0.65}rem` } : undefined"
|
||||
>
|
||||
{{ node.label }}
|
||||
@@ -320,28 +320,48 @@
|
||||
:style="node.depth ? { marginLeft: `${Math.min(node.depth, 6) * 0.65}rem` } : undefined"
|
||||
@click="focusPart(node.clip.id)"
|
||||
>
|
||||
<img :src="partThumb(node.clip)" alt="" class="h-12 w-12 shrink-0 rounded object-cover" loading="lazy">
|
||||
<span class="relative h-12 w-12 shrink-0 overflow-hidden rounded bg-zinc-900">
|
||||
<span v-if="node.clip.hideThumbnail" class="sensitive-glow block h-full w-full" />
|
||||
<img
|
||||
v-else
|
||||
:src="partThumb(node.clip)"
|
||||
alt=""
|
||||
class="h-full w-full object-cover"
|
||||
loading="lazy"
|
||||
>
|
||||
</span>
|
||||
<span class="min-w-0 flex-1">
|
||||
<span class="block truncate text-xs font-medium text-zinc-100">{{ node.clip.name }}</span>
|
||||
<span class="block truncate text-xs font-medium text-amber-100">{{ clipFamilyPartTitle(node.clip, focused.group!.parts) }}</span>
|
||||
<span v-if="node.fromLabel || node.branchLabel" class="block truncate text-[11px] text-amber-200/80">
|
||||
{{ [node.fromLabel, node.branchLabel].filter(Boolean).join(' · ') }}
|
||||
</span>
|
||||
<span class="block truncate text-[11px] text-zinc-500">{{ node.clip.name }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="focusedParts.length > 1" class="mt-3 grid grid-cols-5 gap-1.5">
|
||||
<div v-else-if="focusedParts.length > 1 && !detailsHidden" class="mt-3 grid grid-cols-5 gap-1.5">
|
||||
<button
|
||||
v-for="part in focusedParts"
|
||||
:key="part.id"
|
||||
type="button"
|
||||
class="overflow-hidden rounded-lg border border-white/10"
|
||||
class="relative overflow-hidden rounded-lg border border-white/10"
|
||||
:class="part.id === focusedPartId ? 'ring-2 ring-amber-300' : ''"
|
||||
@click="focusPart(part.id)"
|
||||
>
|
||||
<img :src="partThumb(part)" alt="" class="aspect-square w-full object-cover" loading="lazy">
|
||||
<span v-if="'hideThumbnail' in part && part.hideThumbnail" class="sensitive-glow block aspect-square w-full" />
|
||||
<img
|
||||
v-else
|
||||
:src="partThumb(part)"
|
||||
alt=""
|
||||
class="aspect-square w-full object-cover"
|
||||
loading="lazy"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<button type="button" class="rounded-full border border-amber-300/40 px-3 py-1.5 text-xs text-amber-100" @click="openInStudio(focused)">{{ focused.kind === 'track' ? 'Open in Music' : 'Open in Studio' }}</button>
|
||||
<button v-if="focused.kind !== 'track'" type="button" class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:text-white" @click="useInStudio(focused)">Use as input</button>
|
||||
@@ -481,6 +501,7 @@ import {
|
||||
chronologicalStills,
|
||||
clipBranchFromLabel,
|
||||
clipBranchLetterLabel,
|
||||
clipFamilyPartTitle,
|
||||
familyBranchSummary,
|
||||
familyDisplayNodes,
|
||||
formatClipDuration,
|
||||
@@ -852,7 +873,11 @@ function cardMeta(entry: LibraryEntry) {
|
||||
}
|
||||
|
||||
function partThumb(part: LibraryClip | LibraryStill) {
|
||||
if ('hideThumbnail' in part) return `/api/library/clips/${part.id}/thumb`
|
||||
if ('hideThumbnail' in part && !('role' in part)) {
|
||||
const clip = part as LibraryClip
|
||||
// Prefer this clip’s first frame so siblings don’t all look like the Start thumb.
|
||||
return `/api/library/clips/${clip.id}/first-frame?thumb=1&v=${encodeURIComponent(clip.id)}`
|
||||
}
|
||||
return `/api/library/stills/${part.id}?thumb=1`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user