Clarify output image actions and preserve user VRAM policy

This commit is contained in:
Towsty
2026-09-09 16:38:48 -05:00
parent 6fb87273d7
commit 9b1d8ae798
4 changed files with 97 additions and 63 deletions
+38 -59
View File
@@ -1609,6 +1609,7 @@
<button
type="button"
class="inline-flex rounded-2xl border border-amber-300/40 px-4 py-2 text-sm text-amber-100 hover:border-amber-300/70"
title="Replace only the input image; keep the current prompt and settings"
@click="useEditAsInput"
>
Use as input still
@@ -1617,6 +1618,7 @@
type="button"
class="inline-flex rounded-2xl border border-amber-300/40 px-4 py-2 text-sm text-amber-100 hover:border-amber-300/70 disabled:opacity-50"
:disabled="usingReferenceBusy"
title="Replace only the reference photo; keep the input image, prompt, and settings"
@click="useOutputAsFluxReference"
>
{{ usingReferenceBusy ? 'Loading reference…' : 'Use as reference' }}
@@ -1631,17 +1633,12 @@
</button>
<button
type="button"
class="inline-flex rounded-2xl border border-amber-300/40 px-4 py-2 text-sm text-amber-100 hover:border-amber-300/70"
@click="sendToEdit"
class="inline-flex rounded-2xl border border-amber-300/40 px-4 py-2 text-sm text-amber-100 hover:border-amber-300/70 disabled:opacity-50"
title="Replace the input image, prompt, and generation settings with this image's saved setup"
:disabled="usingInputSettingsBusy"
@click="useOutputWithSettings"
>
Send to Edit
</button>
<button
type="button"
class="inline-flex rounded-2xl border border-amber-300/40 px-4 py-2 text-sm text-amber-100 hover:border-amber-300/70"
@click="sendToRefine"
>
Send to Refine
{{ usingInputSettingsBusy ? 'Loading settings…' : 'Use as input' }}
</button>
<button
v-if="backupStudioEnabled"
@@ -6448,7 +6445,7 @@ function applyStillSettings(still: LibraryStill) {
persistImageV2LoraStack(imageV2LoraStack.value, v2Engine.value)
v2Engine.value = parseImageV2Engine(settings?.engine, 'flux')
if (!settings) return
if (typeof settings.negative === 'string') v2Negative.value = settings.negative
v2Negative.value = settings.negative || ''
if (settings.steps != null) v2Steps.value = clampImageSteps(settings.steps, IMAGE_V2_STEPS_DEFAULT)
if (settings.cfg != null) v2Cfg.value = clampImageCfg(settings.cfg, IMAGE_V2_CFG_DEFAULT)
if (settings.megapixels != null) v2Megapixels.value = clampImageScaleMegapixels(settings.megapixels, 1)
@@ -6458,12 +6455,12 @@ function applyStillSettings(still: LibraryStill) {
aspect.value = parseImageAspect(settings.aspect)
if (mode === 'generate') {
const size = generateSizeForAspect(aspect.value)
v2Width.value = size.width
v2Height.value = size.height
v2Width.value = settings.width && settings.width > 0 ? settings.width : size.width
v2Height.value = settings.height && settings.height > 0 ? settings.height : size.height
}
if (mode === 'compose' && settings.task) v2Task.value = settings.task
imageV2LoraStack.value = mergeLegacyImageV2Loras(
settings.loraStack?.length || settings.loraName ? readLoraStack(settings) : imageV2LoraStack.value,
readLoraStack(settings),
{
engine: v2Engine.value,
mode,
@@ -6482,12 +6479,10 @@ function applyStillSettings(still: LibraryStill) {
if (!settings) return
if (settings.steps != null) editSteps.value = clampImageSteps(settings.steps)
if (settings.cfg != null) editCfg.value = clampImageCfg(settings.cfg)
if (typeof settings.negative === 'string') editNegative.value = settings.negative
editNegative.value = settings.negative || ''
editScaleToTotalPixels.value = settings.scaleToTotalPixels === true
if (settings.scaleMegapixels != null) editScaleMegapixels.value = clampImageScaleMegapixels(settings.scaleMegapixels)
if (settings.loraStack?.length || settings.loraName) {
imageLoraStack.value = filterLoraStackForStudio(readLoraStack(settings), ltxEnabled.value)
}
imageLoraStack.value = filterLoraStackForStudio(readLoraStack(settings), ltxEnabled.value)
aspect.value = parseImageAspect(settings.aspect)
}
@@ -7303,34 +7298,6 @@ async function ensureRefineCanvas() {
}
}
async function sendToEdit() {
if (!editResultUrl.value) return
clearEditPasses()
studioMode.value = 'editv2'
outputFocus.value = 'edit'
try {
await useEditAsInput()
setV2Mode('edit')
toast('Output is Still A. Write an edit prompt.')
} catch {
toast('Could not send that still to Edit')
}
}
async function sendToRefine() {
if (!editResultUrl.value) return
clearEditPasses()
studioMode.value = 'editv2'
outputFocus.value = 'edit'
try {
await useEditAsInput()
setV2Mode('refine')
toast('Output is Still A. Paint a mask, then refine.')
} catch {
toast('Could not send that still to Refine')
}
}
async function editImageV2() {
if (editV2Blocked.value) {
toast(editV2BlockReason.value || 'Load still A first.')
@@ -7502,12 +7469,9 @@ async function useOutputAsFluxReference() {
const blob = await $fetch<Blob>(editResultUrl.value, { responseType: 'blob' })
if (!blob?.size) throw new Error('Empty image')
const reference = new File([blob], `${editDownloadName.value || 'reference'}.png`, { type: blob.type || 'image/png' })
selectStudioKind('image')
setV2Engine('flux')
resetImage()
v2Mode.value = editPassQueue.value.length ? 'iterate' : 'generate'
setEditRef(reference)
toast('Added to Flux Klein reference photo. Your prompt will generate a new image.')
scrollToStudioInput()
toast('Reference photo updated. Your input image, prompt, and settings were kept.')
} catch {
toast('Could not load that image as a reference')
} finally {
@@ -7515,21 +7479,36 @@ async function useOutputAsFluxReference() {
}
}
const usingInputSettingsBusy = ref(false)
async function useOutputWithSettings() {
if (!editResultUrl.value || usingInputSettingsBusy.value) return
usingInputSettingsBusy.value = true
const stillId = stillIdFromLibraryUrl(editResultUrl.value) || lastV2StillId.value
try {
if (!stillId) throw new Error('This image has no saved settings yet.')
if (!stills.value.some(item => item.id === stillId)) await loadLibrary()
const still = stills.value.find(item => item.id === stillId)
if (!still) throw new Error('Saved settings for this image are unavailable.')
await useStillAsInput(still)
} catch (error: any) {
toast(error?.message || 'Could not load this image and its settings')
} finally {
usingInputSettingsBusy.value = false
}
}
async function useEditAsInput() {
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
}
const stillId = stillIdFromLibraryUrl(editResultUrl.value) || lastV2StillId.value
try {
const blob = await $fetch<Blob>(editResultUrl.value, { responseType: 'blob' })
if (!blob || !blob.size) throw new Error('empty still')
readFile(new File([blob], `${editDownloadName.value || 'edited'}.png`, { type: blob.type || 'image/png' }), {
stillId: stillId || undefined
stillId: stillId || undefined, keepThumbnailPref: true
})
toast('Edited still is now the input')
scrollToStudioInput()
toast('Input image updated. Your prompt and settings were kept.')
} catch {
toast('Could not load that still as input')
}