Add per-image iteration settings and steps/CFG sweeps
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="mt-3 space-y-3 border-t border-white/10 pt-3">
|
||||
<p class="text-xs text-zinc-400">Blank values use the shared settings. A blank prompt uses the first prompt.</p>
|
||||
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<label class="text-sm text-zinc-300">Steps
|
||||
<input :value="modelValue.steps" type="number" min="1" max="50" step="1" :placeholder="String(baseSteps)" class="iteration-input" :disabled="disabled" @input="numberField('steps', $event)">
|
||||
</label>
|
||||
<label class="text-sm text-zinc-300">CFG
|
||||
<input :value="modelValue.cfg" type="number" min="0" max="20" step="0.1" :placeholder="String(baseCfg)" class="iteration-input" :disabled="disabled" @input="numberField('cfg', $event)">
|
||||
</label>
|
||||
<label class="text-sm text-zinc-300">Seed
|
||||
<select :value="seedMode" class="iteration-input" :disabled="disabled" @change="setSeedMode($event)">
|
||||
<option value="random">Random</option><option value="same">Same as first</option><option value="custom">Custom</option>
|
||||
</select>
|
||||
</label>
|
||||
<label v-if="seedMode === 'custom'" class="text-sm text-zinc-300">Seed number
|
||||
<input :value="modelValue.seed" type="number" min="0" max="2147483647" step="1" class="iteration-input" :disabled="disabled" @input="numberField('seed', $event)">
|
||||
</label>
|
||||
<label v-if="allowStrength" class="text-sm text-zinc-300">Denoise
|
||||
<input :value="modelValue.strength" type="number" min="0.15" max="0.75" step="0.01" placeholder="Shared" class="iteration-input" :disabled="disabled" @input="numberField('strength', $event)">
|
||||
</label>
|
||||
</div>
|
||||
<label class="flex items-center gap-2 text-sm text-zinc-300">
|
||||
<input type="checkbox" :checked="modelValue.loraStack === undefined" :disabled="disabled" @change="inheritLoras($event)"> Use shared LoRAs
|
||||
</label>
|
||||
<LoraSelect v-if="modelValue.loraStack !== undefined" :model-value="modelValue.loraStack" :names="loraNames" :engine="engine" :disabled="disabled" label="This variation's LoRAs" empty-label="No LoRAs" @update:model-value="update({ loraStack: $event })" />
|
||||
<p v-if="modelValue.steps !== undefined || modelValue.cfg !== undefined" class="text-xs text-zinc-400">Custom steps or CFG override the shared Turbo preset for this variation.</p>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { ImageIteration } from '~/utils/imageIterations'
|
||||
const props = withDefaults(defineProps<{ modelValue: ImageIteration; baseSteps?: number; baseCfg?: number; loraNames?: string[]; engine?: 'flux' | 'krea'; allowStrength?: boolean; disabled?: boolean }>(), { baseSteps: 20, baseCfg: 1, loraNames: () => [], engine: 'flux', allowStrength: false, disabled: false })
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: ImageIteration] }>()
|
||||
const seedMode = computed(() => typeof props.modelValue.seed === 'number' ? 'custom' : props.modelValue.seed || 'random')
|
||||
function update(patch: Partial<ImageIteration>) { emit('update:modelValue', { ...props.modelValue, ...patch }) }
|
||||
function numberField(key: 'steps' | 'cfg' | 'seed' | 'strength', event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value
|
||||
update({ [key]: value === '' ? undefined : Number(value) })
|
||||
}
|
||||
function setSeedMode(event: Event) {
|
||||
const mode = (event.target as HTMLSelectElement).value
|
||||
update({ seed: mode === 'custom' ? 0 : mode === 'same' ? 'same' : 'random' })
|
||||
}
|
||||
function inheritLoras(event: Event) { update({ loraStack: (event.target as HTMLInputElement).checked ? undefined : [] }) }
|
||||
</script>
|
||||
<style scoped>
|
||||
.iteration-input { @apply mt-1 w-full rounded-lg border border-white/15 bg-zinc-950 px-3 py-2 text-sm text-zinc-100 outline-none focus:ring-2 focus:ring-amber-300/40 disabled:opacity-50; }
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<details class="rounded-2xl border border-amber-300/25 bg-amber-400/5 p-4">
|
||||
<summary class="cursor-pointer text-sm font-semibold text-amber-100">Compare settings · build a sweep</summary>
|
||||
<p class="mt-3 text-sm text-zinc-300">Use one prompt with several step counts or CFG values. Each combination becomes an editable variation.</p>
|
||||
<div class="mt-3 grid gap-3 sm:grid-cols-2">
|
||||
<label class="text-sm text-zinc-300">Steps, separated by commas
|
||||
<input v-model="steps" class="mt-1 w-full rounded-lg border border-white/15 bg-zinc-950 px-3 py-2" placeholder="24, 20, 18, 19">
|
||||
</label>
|
||||
<label class="text-sm text-zinc-300">CFG values
|
||||
<input v-model="cfg" class="mt-1 w-full rounded-lg border border-white/15 bg-zinc-950 px-3 py-2" placeholder="1.5">
|
||||
</label>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-zinc-300">{{ preview }}</p>
|
||||
<p class="mt-1 text-xs text-zinc-400">Uses the same seed for comparison and the original input for every image. Replaces the current variations; nothing renders until you queue.</p>
|
||||
<button type="button" class="mt-3 rounded-lg bg-amber-400 px-4 py-2 text-sm font-semibold text-zinc-950 disabled:opacity-50" :disabled="!rows.length" @click="emit('apply', rows)">Build {{ rows.length || '' }} variations</button>
|
||||
</details>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { imageIterationSweep, type ImageIteration } from '~/utils/imageIterations'
|
||||
const emit = defineEmits<{ apply: [rows: ImageIteration[]] }>()
|
||||
const steps = ref('24, 20, 18, 19')
|
||||
const cfg = ref('1.5')
|
||||
const result = computed(() => {
|
||||
try { return { rows: imageIterationSweep(steps.value, cfg.value), error: '' } }
|
||||
catch (error) { return { rows: [] as ImageIteration[], error: error instanceof Error ? error.message : String(error) } }
|
||||
})
|
||||
const rows = computed(() => result.value.rows)
|
||||
const preview = computed(() => result.value.error || `${rows.value.length} images · ${rows.value.map(row => `${row.steps} steps / CFG ${row.cfg}`).join(' · ')}`)
|
||||
</script>
|
||||
@@ -76,6 +76,17 @@
|
||||
<div v-for="(shot, index) in extraShots" :key="index" class="rounded-2xl border border-white/10 bg-zinc-950/50 px-3 py-3">
|
||||
<p class="text-sm font-medium text-zinc-200">{{ job.kind === 'edit' ? `Pass ${index + 2}` : `Shot ${index + 2}` }}</p>
|
||||
<textarea v-model="shot.prompt" rows="3" class="mt-2 max-h-36 w-full overflow-y-auto rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm outline-none ring-amber-300/40 [overflow-wrap:anywhere] focus:ring-2 disabled:opacity-70" :disabled="!editable" />
|
||||
<ImageIterationSettings
|
||||
v-if="job.kind === 'edit' && job.payload.v2Mode === 'iterate'"
|
||||
:model-value="shot"
|
||||
:base-steps="draft.steps"
|
||||
:base-cfg="draft.cfg"
|
||||
:lora-names="loraNames"
|
||||
:engine="job.payload.engine === 'krea' ? 'krea' : 'flux'"
|
||||
:allow-strength="job.payload.engine === 'krea' && Boolean(job.payload.stillId)"
|
||||
:disabled="!editable"
|
||||
@update:model-value="extraShots[index] = { ...$event, duration: shot.duration }"
|
||||
/>
|
||||
<label v-if="job.kind !== 'edit'" class="mt-2 block text-xs text-zinc-400">
|
||||
Duration
|
||||
<input v-model.number="shot.duration" type="number" min="0.5" max="120" step="0.1" class="mt-1 w-28 rounded-lg border border-white/10 bg-zinc-950 px-2 py-1 text-sm disabled:opacity-70" :disabled="!editable">
|
||||
|
||||
Reference in New Issue
Block a user