Restore Use as input images and move engine pickers up.
Generated stills always fill Still A again (including Generate and queue restore), and Flux/Krea, MiniMax/LTX, and ACE/YuE sit on the input holder so you pick the engine before the prompt.
This commit is contained in:
+106
-94
@@ -27,36 +27,87 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="!(studioMode === 'editv2' && v2Mode === 'generate')"
|
class="overflow-hidden rounded-2xl border border-white/15 bg-zinc-950/50"
|
||||||
class="relative cursor-pointer rounded-2xl border border-dashed border-white/15 bg-zinc-950/50 p-4 transition hover:border-amber-300/50"
|
|
||||||
:class="{ 'border-amber-300/70 bg-amber-400/5': dragging }"
|
:class="{ 'border-amber-300/70 bg-amber-400/5': dragging }"
|
||||||
@dragover.prevent="dragging = true"
|
|
||||||
@dragleave.prevent="dragging = false"
|
|
||||||
@drop.prevent="onDrop"
|
|
||||||
@click="openPicker('main')"
|
|
||||||
>
|
>
|
||||||
<div v-if="studioMode === 'editv2' && v2Mode === 'refine' && preview" class="relative" @click.stop>
|
<div
|
||||||
<ImageV2MaskPainter
|
v-if="!(studioMode === 'editv2' && v2Mode === 'generate')"
|
||||||
ref="refinePainter"
|
class="relative cursor-pointer border-dashed p-4 transition hover:border-amber-300/50"
|
||||||
:src="preview"
|
:class="studioMode === 'editv2' || (studioMode === 'video' && ltxEnabled) ? 'border-b border-white/10' : ''"
|
||||||
:hide-preview="hideInputPreview"
|
@dragover.prevent="dragging = true"
|
||||||
@painted="refineMaskDirty = $event"
|
@dragleave.prevent="dragging = false"
|
||||||
/>
|
@drop.prevent="onDrop"
|
||||||
<button type="button" class="absolute right-3 top-3 z-10 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
@click="openPicker('main')"
|
||||||
|
>
|
||||||
|
<div v-if="studioMode === 'editv2' && v2Mode === 'refine' && preview" class="relative" @click.stop>
|
||||||
|
<ImageV2MaskPainter
|
||||||
|
ref="refinePainter"
|
||||||
|
:src="preview"
|
||||||
|
:hide-preview="hideInputPreview"
|
||||||
|
@painted="refineMaskDirty = $event"
|
||||||
|
/>
|
||||||
|
<button type="button" class="absolute right-3 top-3 z-10 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="preview && !hideInputPreview" class="relative">
|
||||||
|
<img :src="preview" alt="Source still" class="max-h-72 w-full rounded-xl object-contain bg-black/40">
|
||||||
|
<button type="button" class="absolute right-3 top-3 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="preview && hideInputPreview" class="relative flex min-h-44 flex-col items-center justify-center text-center">
|
||||||
|
<span class="sensitive-glow absolute inset-3 rounded-xl" />
|
||||||
|
<p class="relative font-medium">Still loaded · preview hidden</p>
|
||||||
|
<p class="relative mt-1 text-sm text-zinc-500">Toggle Hide input still off to show it</p>
|
||||||
|
<button type="button" class="relative mt-3 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex min-h-44 flex-col items-center justify-center text-center">
|
||||||
|
<p class="font-medium">{{ studioMode === 'edit' || studioMode === 'editv2' ? (studioMode === 'editv2' ? (v2Mode === 'refine' ? 'Still A · canvas' : 'Still A') : 'Image 1') : (textToVideo ? 'Start still · optional' : 'Choose an image') }}</p>
|
||||||
|
<p class="mt-1 text-sm text-zinc-500">{{ textToVideo ? 'Text-to-video does not need a still. Shot 2+ will still extend from the last frame.' : 'Browse the library or upload a new PNG, JPG, or WEBP' }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="preview && !hideInputPreview" class="relative">
|
<div
|
||||||
<img :src="preview" alt="Source still" class="max-h-72 w-full rounded-xl object-contain bg-black/40">
|
v-else-if="studioMode === 'editv2' && v2Mode === 'generate' && (file || inputStillId)"
|
||||||
<button type="button" class="absolute right-3 top-3 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
class="border-b border-white/10 px-4 py-3 text-sm text-zinc-400"
|
||||||
|
>
|
||||||
|
Still A is loaded for Edit / Compose / Refine. Generate ignores it and uses the prompt only.
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="preview && hideInputPreview" class="relative flex min-h-44 flex-col items-center justify-center text-center">
|
<div v-if="studioMode === 'editv2'" class="grid grid-cols-2">
|
||||||
<span class="sensitive-glow absolute inset-3 rounded-xl" />
|
<button
|
||||||
<p class="relative font-medium">Still loaded · preview hidden</p>
|
type="button"
|
||||||
<p class="relative mt-1 text-sm text-zinc-500">Toggle Hide input still off to show it</p>
|
class="border-r border-white/10 px-3 py-2.5 text-left text-sm transition"
|
||||||
<button type="button" class="relative mt-3 rounded-full bg-zinc-950/80 px-3 py-1 text-xs" @click.stop="resetImage">Reset</button>
|
:class="v2Engine === 'flux' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
|
@click="setV2Engine('flux')"
|
||||||
|
>
|
||||||
|
<span class="block font-semibold">Flux</span>
|
||||||
|
<span class="text-xs opacity-80">Klein 9B</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-3 py-2.5 text-left text-sm transition"
|
||||||
|
:class="v2Engine === 'krea' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
|
@click="setV2Engine('krea')"
|
||||||
|
>
|
||||||
|
<span class="block font-semibold">Krea</span>
|
||||||
|
<span class="text-xs opacity-80">Krea 2 Turbo</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex min-h-44 flex-col items-center justify-center text-center">
|
<div v-else-if="studioMode === 'video' && ltxEnabled" class="grid grid-cols-2">
|
||||||
<p class="font-medium">{{ studioMode === 'edit' || studioMode === 'editv2' ? (studioMode === 'editv2' ? (v2Mode === 'refine' ? 'Still A · canvas' : 'Still A') : 'Image 1') : (textToVideo ? 'Start still · optional' : 'Choose an image') }}</p>
|
<button
|
||||||
<p class="mt-1 text-sm text-zinc-500">{{ textToVideo ? 'Text-to-video does not need a still. Shot 2+ will still extend from the last frame.' : 'Browse the library or upload a new PNG, JPG, or WEBP' }}</p>
|
type="button"
|
||||||
|
class="border-r border-white/10 px-3 py-2.5 text-left text-sm transition"
|
||||||
|
:class="videoEngine === 'minimax' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
|
@click="selectVideoEngine('minimax')"
|
||||||
|
>
|
||||||
|
<span class="block font-semibold">MiniMax H3</span>
|
||||||
|
<span class="text-xs opacity-80">Stereo · identity refs</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-3 py-2.5 text-left text-sm transition"
|
||||||
|
:class="videoEngine === 'ltx' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
|
@click="selectVideoEngine('ltx')"
|
||||||
|
>
|
||||||
|
<span class="block font-semibold">LTX-2.3</span>
|
||||||
|
<span class="text-xs opacity-80">PinkCherry</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -611,29 +662,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="studioMode === 'video'" class="space-y-3">
|
<div v-if="studioMode === 'video'" class="space-y-3">
|
||||||
<div v-if="ltxEnabled">
|
|
||||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Video model</p>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="rounded-xl border px-3 py-2 text-left text-sm"
|
|
||||||
:class="videoEngine === 'minimax' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
|
||||||
@click="selectVideoEngine('minimax')"
|
|
||||||
>
|
|
||||||
<span class="block font-semibold">MiniMax H3</span>
|
|
||||||
<span class="text-xs text-zinc-400">Native stereo audio. Identity refs on v2.</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="rounded-xl border px-3 py-2 text-left text-sm"
|
|
||||||
:class="videoEngine === 'ltx' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
|
||||||
@click="selectVideoEngine('ltx')"
|
|
||||||
>
|
|
||||||
<span class="block font-semibold">LTX-2.3 PinkCherry</span>
|
|
||||||
<span class="text-xs text-zinc-400">PinkCherry + distilled LoRA. I2V and T2V.</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Start from</p>
|
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Start from</p>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
@@ -757,28 +785,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="studioMode === 'editv2'" class="space-y-3">
|
<div v-else-if="studioMode === 'editv2'" class="space-y-3">
|
||||||
<p class="text-xs text-zinc-500">Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video. Landscape / Portrait / Square fill new edges without stretching the still.</p>
|
<p class="text-xs text-zinc-500">Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video. Landscape / Portrait / Square fill new edges without stretching the still.</p>
|
||||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Image engine</p>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="rounded-xl border px-3 py-2 text-left text-sm"
|
|
||||||
:class="v2Engine === 'flux' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
|
||||||
@click="setV2Engine('flux')"
|
|
||||||
>
|
|
||||||
<span class="block font-semibold">Flux</span>
|
|
||||||
<span class="text-xs text-zinc-400">Klein 9B. Edit, compose, refine, and generate.</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="rounded-xl border px-3 py-2 text-left text-sm"
|
|
||||||
:class="v2Engine === 'krea' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
|
||||||
@click="setV2Engine('krea')"
|
|
||||||
>
|
|
||||||
<span class="block font-semibold">Krea</span>
|
|
||||||
<span class="text-xs text-zinc-400">Krea 2 Turbo. Photograph engine for stills.</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="mt-2 text-xs text-zinc-500">Flux is Klein 9B. Krea is Krea 2 Turbo. Video stays MiniMax or LTX.</p>
|
|
||||||
<div class="max-w-md">
|
<div class="max-w-md">
|
||||||
<label
|
<label
|
||||||
v-if="v2ConceptLoraEnabled && imageV2ConceptFile"
|
v-if="v2ConceptLoraEnabled && imageV2ConceptFile"
|
||||||
@@ -5518,14 +5524,17 @@ async function restoreStudioEditJob(payload: {
|
|||||||
imageLoraStack.value = filterLoraStackForStudio(readLoraStack(payload), ltxEnabled.value)
|
imageLoraStack.value = filterLoraStackForStudio(readLoraStack(payload), ltxEnabled.value)
|
||||||
aspect.value = parseImageAspect(payload.aspect)
|
aspect.value = parseImageAspect(payload.aspect)
|
||||||
}
|
}
|
||||||
const generate = v2 && payload.v2Mode === 'generate'
|
// Always restore Still A when the payload has one — including Generate jobs —
|
||||||
if (!generate && payload.stillId) {
|
// so switching to Edit / Compose / Refine keeps the image in the input slot.
|
||||||
|
if (payload.stillId) {
|
||||||
const blob = await $fetch<Blob>(`/api/library/stills/${payload.stillId}`, { responseType: 'blob' })
|
const blob = await $fetch<Blob>(`/api/library/stills/${payload.stillId}`, { responseType: 'blob' })
|
||||||
if (payload.hideInput && showPrivacyToggles.value) hideInputPreview.value = true
|
if (payload.hideInput && showPrivacyToggles.value) hideInputPreview.value = true
|
||||||
readFile(new File([blob], stillFileName(stills.value.find(item => item.id === payload.stillId)) || payload.stillFilename || 'still.png', { type: blob.type || 'image/png' }), { stillId: payload.stillId })
|
readFile(new File([blob], stillFileName(stills.value.find(item => item.id === payload.stillId)) || payload.stillFilename || 'still.png', { type: blob.type || 'image/png' }), { stillId: payload.stillId })
|
||||||
}
|
}
|
||||||
if (v2 && payload.v2Mode === 'compose' && payload.referenceStillId) {
|
if (v2 && payload.v2Mode === 'compose' && payload.referenceStillId) {
|
||||||
await loadStillAsEditRef(payload.referenceStillId).catch(() => null)
|
await loadStillAsEditRef(payload.referenceStillId).catch(() => null)
|
||||||
|
} else if (!(v2 && payload.v2Mode === 'compose')) {
|
||||||
|
clearEditRef()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6349,14 +6358,13 @@ function applyStillSettings(still: LibraryStill) {
|
|||||||
folderId.value = still.folderId
|
folderId.value = still.folderId
|
||||||
browseFolderId.value = still.folderId
|
browseFolderId.value = still.folderId
|
||||||
rememberInputSeed(settings?.seed)
|
rememberInputSeed(settings?.seed)
|
||||||
// Stay on the current Video/Image tab. On Video, only restore prompt for
|
applyPromptRestore(still.prompt || '', still)
|
||||||
// video-origin stills; image stills are start-frame pixels only.
|
// Video start frames stay on Video. Image stills open Image with full settings.
|
||||||
if (!imageStudio.value) {
|
if (!dest) {
|
||||||
if (!dest) applyPromptRestore(still.prompt || '', still)
|
studioMode.value = 'video'
|
||||||
|
outputFocus.value = 'video'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
applyPromptRestore(still.prompt || '', still)
|
|
||||||
if (!dest) return
|
|
||||||
if (dest === 'imagev2') {
|
if (dest === 'imagev2') {
|
||||||
studioMode.value = 'editv2'
|
studioMode.value = 'editv2'
|
||||||
outputFocus.value = 'edit'
|
outputFocus.value = 'edit'
|
||||||
@@ -6417,39 +6425,37 @@ async function loadStillAsEditRef(stillId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stillUseAsInputMessage(still: LibraryStill) {
|
function stillUseAsInputMessage(still: LibraryStill) {
|
||||||
if (!imageStudio.value) {
|
|
||||||
return 'Loaded as start still. Stays on Video — switch to Image only if you want to edit it there.'
|
|
||||||
}
|
|
||||||
const dest = inferStillPipeline(still)
|
const dest = inferStillPipeline(still)
|
||||||
const mode = still.settings?.mode
|
const mode = still.settings?.mode
|
||||||
if (dest === 'imagev2' && mode === 'generate') return 'Generate settings loaded. Tweak and generate.'
|
if (dest === 'imagev2' && mode === 'generate') return 'Generate settings and still loaded. Switch to Edit anytime — the image is already in Still A.'
|
||||||
if (dest === 'imagev2' && mode === 'compose') {
|
if (dest === 'imagev2' && mode === 'compose') {
|
||||||
return still.settings?.referenceStillId
|
return still.settings?.referenceStillId
|
||||||
? 'Compose settings loaded. Tweak and generate.'
|
? 'Compose settings loaded with stills A and B.'
|
||||||
: 'Compose settings loaded. Re-add still B, then generate.'
|
: 'Compose settings loaded. Re-add still B, then generate.'
|
||||||
}
|
}
|
||||||
if (dest === 'imagev2' && mode === 'refine') return 'Refine settings loaded. Paint a mask, then generate.'
|
if (dest === 'imagev2' && mode === 'refine') return 'Refine settings and canvas still loaded.'
|
||||||
if (dest === 'imagev2') return 'Image v2 settings loaded. Tweak and generate.'
|
if (dest === 'imagev2') return 'Image settings and still loaded.'
|
||||||
if (dest === 'image' && still.settings) return 'Image edit settings loaded. Tweak and generate.'
|
if (dest === 'image' && still.settings) return 'Image edit settings and still loaded.'
|
||||||
return 'Loaded as input. It stays in Output, not the upload library.'
|
return 'Loaded as input with prompt and still.'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function useStillAsInput(still: LibraryStill) {
|
async function useStillAsInput(still: LibraryStill) {
|
||||||
try {
|
try {
|
||||||
applyStillSettings(still)
|
applyStillSettings(still)
|
||||||
const dest = inferStillPipeline(still)
|
const dest = inferStillPipeline(still)
|
||||||
const generate = dest === 'imagev2' && still.settings?.mode === 'generate'
|
// Always put the still pixels in the input slot — including Generate outputs —
|
||||||
if (!generate) {
|
// so Edit / Compose / Refine / Video all have an image after Use as input.
|
||||||
const blob = await $fetch<Blob>(`/api/library/stills/${still.id}`, { responseType: 'blob' })
|
const blob = await $fetch<Blob>(`/api/library/stills/${still.id}`, { responseType: 'blob' })
|
||||||
if (still.hideInput && showPrivacyToggles.value) hideInputPreview.value = true
|
if (still.hideInput && showPrivacyToggles.value) hideInputPreview.value = true
|
||||||
readFile(new File([blob], stillFileName(still), { type: blob.type || 'image/png' }), { stillId: still.id })
|
readFile(new File([blob], stillFileName(still), { type: blob.type || 'image/png' }), { stillId: still.id })
|
||||||
}
|
|
||||||
if (dest === 'imagev2' && still.settings?.mode === 'compose' && still.settings.referenceStillId) {
|
if (dest === 'imagev2' && still.settings?.mode === 'compose' && still.settings.referenceStillId) {
|
||||||
try {
|
try {
|
||||||
await loadStillAsEditRef(still.settings.referenceStillId)
|
await loadStillAsEditRef(still.settings.referenceStillId)
|
||||||
} catch {
|
} catch {
|
||||||
toast('Compose settings loaded, but still B is missing. Re-add it.')
|
toast('Compose settings loaded, but still B is missing. Re-add it.')
|
||||||
}
|
}
|
||||||
|
} else if (dest !== 'imagev2' || still.settings?.mode !== 'compose') {
|
||||||
|
clearEditRef()
|
||||||
}
|
}
|
||||||
scrollToStudioInput()
|
scrollToStudioInput()
|
||||||
toast(stillUseAsInputMessage(still))
|
toast(stillUseAsInputMessage(still))
|
||||||
@@ -7379,11 +7385,17 @@ async function editImageV2() {
|
|||||||
|
|
||||||
async function useEditAsInput() {
|
async function useEditAsInput() {
|
||||||
if (!editResultUrl.value) return
|
if (!editResultUrl.value) return
|
||||||
|
const stillId = lastV2StillId.value || stillIdFromLibraryUrl(editResultUrl.value)
|
||||||
|
const libraryStill = stillId ? stills.value.find(item => item.id === stillId) : undefined
|
||||||
|
if (libraryStill) {
|
||||||
|
await useStillAsInput(libraryStill)
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const blob = await $fetch<Blob>(editResultUrl.value, { responseType: 'blob' })
|
const blob = await $fetch<Blob>(editResultUrl.value, { responseType: 'blob' })
|
||||||
if (!blob || !blob.size) throw new Error('empty still')
|
if (!blob || !blob.size) throw new Error('empty still')
|
||||||
readFile(new File([blob], `${editDownloadName.value || 'edited'}.png`, { type: blob.type || 'image/png' }), {
|
readFile(new File([blob], `${editDownloadName.value || 'edited'}.png`, { type: blob.type || 'image/png' }), {
|
||||||
stillId: stillIdFromLibraryUrl(editResultUrl.value)
|
stillId: stillId || undefined
|
||||||
})
|
})
|
||||||
toast('Edited still is now the input')
|
toast('Edited still is now the input')
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
+17
-21
@@ -10,32 +10,28 @@
|
|||||||
<StudioKindCards current="music" @select="goStudio" />
|
<StudioKindCards current="music" @select="goStudio" />
|
||||||
<div class="grid min-w-0 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] lg:items-stretch">
|
<div class="grid min-w-0 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] lg:items-stretch">
|
||||||
<section class="glass min-w-0 max-w-full space-y-5 rounded-3xl p-5 sm:p-6">
|
<section class="glass min-w-0 max-w-full space-y-5 rounded-3xl p-5 sm:p-6">
|
||||||
<div>
|
<div class="overflow-hidden rounded-2xl border border-white/15 bg-zinc-950/50">
|
||||||
<h2 class="font-display text-2xl font-bold">Music Generator</h2>
|
<div class="border-b border-white/10 px-4 py-3">
|
||||||
<p class="mt-1 text-sm text-zinc-400">
|
<h2 class="font-display text-2xl font-bold">Music Generator</h2>
|
||||||
Tags describe style and voice. Lyrics use structure chips. Instrumental skips the vocal line.
|
<p class="mt-1 text-sm text-zinc-400">
|
||||||
</p>
|
Tags describe style and voice. Lyrics use structure chips. Instrumental skips the vocal line.
|
||||||
</div>
|
</p>
|
||||||
|
</div>
|
||||||
<div>
|
<div class="grid grid-cols-2">
|
||||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Engine</p>
|
|
||||||
<div class="grid grid-cols-2 gap-2">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded-xl border px-3 py-2 text-left text-sm"
|
class="border-r border-white/10 px-3 py-2.5 text-left text-sm transition"
|
||||||
:class="engineFamily === 'ace' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
:class="engineFamily === 'ace' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
@click="selectEngineFamily('ace')"
|
@click="selectEngineFamily('ace')"
|
||||||
>
|
>
|
||||||
<span class="block font-semibold">ACE-Step</span>
|
<span class="block font-semibold">ACE-Step</span>
|
||||||
<span class="text-xs text-zinc-400">Native Comfy checkpoint. Fast vocals.</span>
|
<span class="text-xs opacity-80">Fast vocals</span>
|
||||||
</button>
|
</button>
|
||||||
<div
|
<button
|
||||||
class="cursor-pointer rounded-xl border px-3 py-2 text-left text-sm"
|
type="button"
|
||||||
:class="engineFamily === 'yue' ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/10 hover:border-white/20'"
|
class="px-3 py-2.5 text-left text-sm transition"
|
||||||
role="button"
|
:class="engineFamily === 'yue' ? 'bg-amber-400/10 text-amber-100' : 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"
|
||||||
tabindex="0"
|
|
||||||
@click="selectEngineFamily('yue')"
|
@click="selectEngineFamily('yue')"
|
||||||
@keydown.enter.prevent="selectEngineFamily('yue')"
|
|
||||||
>
|
>
|
||||||
<span class="flex items-center gap-2">
|
<span class="flex items-center gap-2">
|
||||||
<span class="font-semibold">YuE</span>
|
<span class="font-semibold">YuE</span>
|
||||||
@@ -44,8 +40,8 @@
|
|||||||
text="YuE is a separate lyrics-to-song engine (stage-1 7B + stage-2 1B + xcodec). It needs the ComfyUI_YuE custom node on Beast and is slower and heavier than ACE-Step. Use it when you want a full sung track from lyrics rather than ACE-Step’s tag-conditioned sampler."
|
text="YuE is a separate lyrics-to-song engine (stage-1 7B + stage-2 1B + xcodec). It needs the ComfyUI_YuE custom node on Beast and is slower and heavier than ACE-Step. Use it when you want a full sung track from lyrics rather than ACE-Step’s tag-conditioned sampler."
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-zinc-400">Lyrics-to-song. Custom node.</span>
|
<span class="text-xs opacity-80">Lyrics-to-song</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ export function loraBasename(name: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function loraIdentityKey(name: string) {
|
export function loraIdentityKey(name: string) {
|
||||||
return loraBasename(name).replace(/^xaigen-?/i, '').toLowerCase()
|
return loraBasename(name).replace(/^(xai|xia)gen-?/i, '').toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isXaigenOnlyLora(name: string) {
|
export function isXaigenOnlyLora(name: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user