Simplify generation controls with task navigation and settings drawer

This commit is contained in:
Towsty
2026-09-07 08:36:33 -05:00
parent 7a2069462e
commit 174d40a7dc
7 changed files with 177 additions and 13 deletions
+44 -5
View File
@@ -1,7 +1,7 @@
<template>
<div class="min-h-dvh min-w-0">
<AppHeader
:subtitle="ltxEnabled ? 'MiniMax H3 · LTX-2.3 · Flux.2 Klein' : 'MiniMax H3 · Flux.2 Klein'"
:subtitle="advanced ? (ltxEnabled ? 'MiniMax H3 · LTX-2.3 · Flux.2 Klein' : 'MiniMax H3 · Flux.2 Klein') : 'Create images, video, and music'"
current="studio"
:queue-count="queueBadgeCount"
@gpu="pollComfyHealth"
@@ -9,6 +9,7 @@
<main class="mx-auto min-w-0 max-w-7xl space-y-6 px-4 py-6 sm:px-6">
<StudioKindCards
:advanced="advanced"
:current="imageStudio ? 'image' : 'video'"
:ltx-enabled="ltxEnabled"
:video-engine="videoEngine"
@@ -18,6 +19,12 @@
@video-engine="selectVideoEngine"
@image-engine="setV2Engine"
/>
<div class="flex flex-wrap items-center justify-between gap-3">
<div v-if="!advanced" class="flex flex-wrap gap-2" role="group" aria-label="Generation task">
<button v-for="task in ['From text', 'From image', 'Edit', 'Extend']" :key="task" type="button" class="rounded-full border px-4 py-2 text-sm" :class="simpleTask === task ? 'border-amber-300 bg-amber-400/10 text-amber-100' : 'border-white/15 text-zinc-300'" :aria-pressed="simpleTask === task" @click="chooseSimpleTask(task)">{{ task }}</button>
</div>
<label class="ml-auto flex items-center gap-2 text-sm text-zinc-300"><input v-model="advanced" type="checkbox" class="accent-amber-400">Advanced mode</label>
</div>
<div class="grid min-w-0 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] lg:items-stretch">
<section id="studio-input" class="glass min-w-0 max-w-full space-y-5 rounded-3xl p-5 sm:p-6">
<div>
@@ -127,7 +134,7 @@
</button>
</div>
<div v-if="studioMode === 'editv2'" class="flex flex-wrap gap-2">
<div v-if="studioMode === 'editv2' && (advanced || simpleTask === 'Edit')" class="flex flex-wrap gap-2">
<button
type="button"
class="rounded-full border px-3 py-1 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-40"
@@ -266,7 +273,7 @@
</div>
</div>
<DirectorScriptBar
v-if="studioMode === 'video'"
v-if="advanced && studioMode === 'video'"
class="mb-2"
:scripts="directorScripts"
v-model:selected-id="directorScriptId"
@@ -288,6 +295,7 @@
>
<div class="min-w-0">
<PromptPartsEditor
:simple="!advanced"
v-model:pre="promptPre"
v-model:prompt="prompt"
v-model:post="promptPost"
@@ -660,6 +668,11 @@
</div>
</div>
<div v-if="!advanced" class="space-y-4">
<AspectRatioPick v-if="imageStudio" :model-value="aspect" heading="Image size" :options="imageAspects" @update:model-value="setImageAspect" />
<template v-else><DurationField v-model="duration" label="Duration" /><AspectRatioPick v-model="aspect" heading="Video size" :options="videoAspects" /></template>
</div>
<GenerationSettings :advanced="advanced">
<div v-if="studioMode === 'video'" class="space-y-3">
<p class="text-xs text-zinc-500">{{ videoModelHint }}</p>
<div v-if="showLegacyMinimaxV1 && videoEngine === 'minimax' && videoStart === 'still'">
@@ -737,6 +750,11 @@
</details>
</div>
<label class="block space-y-2 text-sm">
<span>Model</span>
<select v-if="imageStudio" :value="v2Engine" class="w-full rounded-xl border border-white/15 bg-zinc-900 p-3" @change="setV2Engine(($event.target as HTMLSelectElement).value as ImageV2Engine)"><option value="flux">Flux Klein 9B</option><option value="krea">Krea 2 Turbo</option></select>
<select v-else :value="videoEngine" class="w-full rounded-xl border border-white/15 bg-zinc-900 p-3" @change="selectVideoEngine(($event.target as HTMLSelectElement).value as VideoEngineId)"><option value="minimax">MiniMax H3</option><option v-if="ltxEnabled" value="ltx">LTX 2.3</option></select>
</label>
<div v-if="studioMode === 'video'" class="max-w-md space-y-2">
<LoraSelect
v-model="videoLoraStack"
@@ -1126,14 +1144,15 @@
</span>
</label>
</div>
<div class="flex flex-wrap gap-3">
</GenerationSettings>
<div class="sticky bottom-3 z-10 flex flex-wrap gap-3 rounded-2xl bg-zinc-950/95 p-2 shadow-lg">
<button
type="button"
class="flex-1 rounded-2xl bg-amber-400 px-5 py-3 font-semibold text-zinc-950 transition hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="studioMode === 'editv2' ? editV2Blocked : (studioMode === 'edit' ? editBlocked : videoGenerateDisabled)"
@click="studioMode === 'editv2' ? editImageV2() : (studioMode === 'edit' ? editImage() : generate())"
>
{{ studioMode === 'editv2' ? editV2SubmitLabel : (studioMode === 'edit' ? editSubmitLabel : generateLabel) }}
{{ !advanced ? 'Queue' : studioMode === 'editv2' ? editV2SubmitLabel : (studioMode === 'edit' ? editSubmitLabel : generateLabel) }}
</button>
<button
v-if="showWakeGpu"
@@ -2393,6 +2412,26 @@ import {
const runtimeConfig = useRuntimeConfig()
const route = useRoute()
const router = useRouter()
const { advanced } = useStudioLayout()
const editingTask = ref(false)
const simpleTask = computed(() => extendOpen.value ? 'Extend' : editingTask.value || (imageStudio.value && ['compose', 'refine'].includes(v2Mode.value)) ? 'Edit' : file.value ? 'From image' : 'From text')
function chooseSimpleTask(task: string) {
if (task === 'Extend') {
selectStudioKind('video')
void openExtend()
return
}
closeExtend()
editingTask.value = task === 'Edit'
if (task === 'Edit') selectStudioKind('image')
if (task === 'From text') {
resetImage()
if (imageStudio.value) v2Mode.value = editPassQueue.value.length ? 'iterate' : 'generate'
} else {
if (imageStudio.value && !editPassQueue.value.length) v2Mode.value = 'edit'
if (!file.value) openPicker('main')
}
}
type AspectId = 'auto' | '16:9' | '9:16' | '1:1'
+14 -4
View File
@@ -1,18 +1,20 @@
<template>
<div class="min-h-dvh min-w-0">
<AppHeader
:subtitle="headerSubtitle"
:subtitle="advanced ? headerSubtitle : 'Create music from text'"
current="music"
:queue-count="queueCount"
/>
<main class="mx-auto min-w-0 max-w-7xl space-y-6 px-4 py-6 sm:px-6">
<StudioKindCards
:advanced="advanced"
current="music"
:music-engine="engineFamily"
@select="goStudio"
@music-engine="selectEngineFamily"
/>
<div class="flex items-center justify-between gap-3"><span class="rounded-full border border-amber-300/60 px-4 py-2 text-sm text-amber-100">From text</span><label class="flex items-center gap-2 text-sm text-zinc-300"><input v-model="advanced" type="checkbox" class="accent-amber-400">Advanced mode</label></div>
<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">
<div>
@@ -22,6 +24,12 @@
</p>
</div>
<GenerationSettings :advanced="advanced">
<label class="block space-y-2 text-sm"><span>Model</span><select :value="engineFamily" class="w-full rounded-xl border border-white/15 bg-zinc-900 p-3" @change="selectEngineFamily(($event.target as HTMLSelectElement).value as 'ace' | 'yue')"><option value="ace">ACE-Step</option><option value="yue">YuE</option></select></label>
<div v-if="!advanced" class="space-y-4">
<label v-if="engineFamily !== 'yue'" class="block text-sm">Steps<input v-model.number="steps" type="number" :min="stepsMin" :max="stepsMax" class="mt-2 w-full rounded-xl border border-white/15 bg-zinc-900 p-3"></label>
<label class="block text-sm">Seed<input v-model="seed" placeholder="random" class="mt-2 w-full rounded-xl border border-white/15 bg-zinc-900 p-3"></label>
</div>
<div v-if="engineFamily === 'ace'" class="flex items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<span class="min-w-0">
<span class="flex items-center gap-2">
@@ -40,6 +48,7 @@
</label>
</div>
</GenerationSettings>
<p v-if="error" class="rounded-2xl border border-red-400/30 bg-red-400/10 px-4 py-3 text-sm text-red-200">{{ error }}</p>
<label class="block text-sm">
@@ -93,7 +102,7 @@
>
<span class="mt-1 block text-[11px] text-zinc-500">{{ engineFamily === 'yue' ? 'YuE uses this as stage-A length' : `${durationMin}–${durationMax} seconds` }}</span>
</label>
<label v-if="engineFamily !== 'yue'" class="block text-sm">
<label v-if="advanced && engineFamily !== 'yue'" class="block text-sm">
<span class="mb-1 block font-medium text-zinc-300">Steps · {{ steps }}</span>
<input
v-model.number="steps"
@@ -108,7 +117,7 @@
</div>
<div class="grid gap-3 sm:grid-cols-2">
<label class="block text-sm">
<label v-if="advanced" class="block text-sm">
<span class="mb-1 block font-medium text-zinc-300">Seed</span>
<input
v-model="seed"
@@ -144,7 +153,7 @@
v-if="busy"
class="h-4 w-4 animate-spin rounded-full border-2 border-zinc-950/20 border-t-zinc-950"
/>
{{ busy ? (status || 'Generating…') : 'Generate' }}
{{ busy ? (status || 'Generating…') : advanced ? 'Generate' : 'Queue' }}
</button>
<button
v-if="busy || queued"
@@ -213,6 +222,7 @@
</template>
<script setup lang="ts">
const { advanced } = useStudioLayout()
import {
DEFAULT_MUSIC_LYRICS,
DEFAULT_MUSIC_TAGS,