Keep the image batch popup open, and add a lock on each dial.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<Thumbwheel
|
<Thumbwheel
|
||||||
:model-value="slider"
|
:model-value="slider"
|
||||||
|
lock-key="duration"
|
||||||
:min="SLIDER_MIN"
|
:min="SLIDER_MIN"
|
||||||
:max="SLIDER_MAX"
|
:max="SLIDER_MAX"
|
||||||
:step="0.1"
|
:step="0.1"
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div
|
<div
|
||||||
v-if="open"
|
v-if="open"
|
||||||
class="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/70 p-4 sm:p-8"
|
class="fixed inset-0 z-[80] flex items-start justify-center overflow-y-auto bg-black/70 p-4 sm:p-8"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-label="Batch image prompts"
|
aria-label="Batch image prompts"
|
||||||
@click.self="close"
|
@pointerdown.self="onBackdrop"
|
||||||
|
@click.self="onBackdrop"
|
||||||
>
|
>
|
||||||
<div class="glass flex w-full max-w-5xl flex-col rounded-3xl p-5 sm:p-6">
|
<div class="glass flex w-full max-w-5xl flex-col rounded-3xl p-5 sm:p-6" @pointerdown.stop @click.stop>
|
||||||
<div class="flex items-start justify-between gap-3">
|
<div class="flex items-start justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="font-display text-2xl font-bold">Batch script</h2>
|
<h2 class="font-display text-2xl font-bold">Batch script</h2>
|
||||||
@@ -35,11 +36,10 @@
|
|||||||
<span class="mb-2 block font-medium text-zinc-300">Shot list</span>
|
<span class="mb-2 block font-medium text-zinc-300">Shot list</span>
|
||||||
<textarea
|
<textarea
|
||||||
ref="textareaEl"
|
ref="textareaEl"
|
||||||
:value="text"
|
v-model="draft"
|
||||||
rows="16"
|
rows="16"
|
||||||
class="max-h-[min(36rem,55vh)] min-h-[16rem] w-full overflow-y-auto rounded-2xl border border-white/10 bg-zinc-950/70 px-4 py-3 text-sm outline-none ring-amber-300/40 placeholder:text-zinc-600 [overflow-wrap:anywhere] focus:ring-2"
|
class="max-h-[min(36rem,55vh)] min-h-[16rem] w-full overflow-y-auto rounded-2xl border border-white/10 bg-zinc-950/70 px-4 py-3 text-sm outline-none ring-amber-300/40 placeholder:text-zinc-600 [overflow-wrap:anywhere] focus:ring-2"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
@input="onInput"
|
|
||||||
@keydown.ctrl.enter.prevent="apply"
|
@keydown.ctrl.enter.prevent="apply"
|
||||||
@keydown.meta.enter.prevent="apply"
|
@keydown.meta.enter.prevent="apply"
|
||||||
/>
|
/>
|
||||||
@@ -100,11 +100,13 @@ const props = withDefaults(defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: []
|
close: []
|
||||||
apply: []
|
apply: [text: string]
|
||||||
'update:text': [value: string]
|
'update:text': [value: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const textareaEl = ref<HTMLTextAreaElement | null>(null)
|
const textareaEl = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
const draft = ref('')
|
||||||
|
const ignoreBackdropUntil = ref(0)
|
||||||
|
|
||||||
const placeholder = `shot 1
|
const placeholder = `shot 1
|
||||||
Change her jacket to black leather.
|
Change her jacket to black leather.
|
||||||
@@ -115,7 +117,7 @@ Put her in a red dress.
|
|||||||
shot 3
|
shot 3
|
||||||
Make it night, city lights behind her.`
|
Make it night, city lights behind her.`
|
||||||
|
|
||||||
const parsed = computed(() => parseImageBatchScript(props.text))
|
const parsed = computed(() => parseImageBatchScript(draft.value))
|
||||||
|
|
||||||
const applyLabel = computed(() => {
|
const applyLabel = computed(() => {
|
||||||
const n = parsed.value.length
|
const n = parsed.value.length
|
||||||
@@ -123,32 +125,43 @@ const applyLabel = computed(() => {
|
|||||||
return n === 1 ? 'Apply 1 prompt' : `Apply ${n} prompts`
|
return n === 1 ? 'Apply 1 prompt' : `Apply ${n} prompts`
|
||||||
})
|
})
|
||||||
|
|
||||||
function onInput(event: Event) {
|
|
||||||
emit('update:text', (event.target as HTMLTextAreaElement).value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onBackdrop() {
|
||||||
|
if (Date.now() < ignoreBackdropUntil.value) return
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
function apply() {
|
function apply() {
|
||||||
if (!parsed.value.length) return
|
const text = draft.value
|
||||||
emit('apply')
|
if (!parseImageBatchScript(text).length) return
|
||||||
|
emit('update:text', text)
|
||||||
|
emit('apply', text)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKey(event: KeyboardEvent) {
|
function onKey(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape') close()
|
if (event.key === 'Escape') close()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.open, (open) => {
|
watch(() => props.open, async (open) => {
|
||||||
if (typeof document === 'undefined') return
|
if (typeof document === 'undefined') return
|
||||||
document.body.style.overflow = open ? 'hidden' : ''
|
document.body.style.overflow = open ? 'hidden' : ''
|
||||||
if (open) {
|
if (!open) {
|
||||||
document.addEventListener('keydown', onKey)
|
|
||||||
nextTick(() => textareaEl.value?.focus())
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('keydown', onKey)
|
document.removeEventListener('keydown', onKey)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
ignoreBackdropUntil.value = Date.now() + 500
|
||||||
|
draft.value = props.text || ''
|
||||||
|
document.addEventListener('keydown', onKey)
|
||||||
|
await nextTick()
|
||||||
|
textareaEl.value?.focus()
|
||||||
|
if (draft.value.trim()) return
|
||||||
|
try {
|
||||||
|
const clip = await navigator.clipboard.readText()
|
||||||
|
if (clip.trim()) draft.value = clip
|
||||||
|
} catch { /* clipboard permission */ }
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
<Thumbwheel
|
<Thumbwheel
|
||||||
:model-value="item.strengthModel"
|
:model-value="item.strengthModel"
|
||||||
label="Model"
|
label="Model"
|
||||||
|
:lock-key="`lora-model:${item.name}`"
|
||||||
:min="LORA_STRENGTH_MIN"
|
:min="LORA_STRENGTH_MIN"
|
||||||
:max="LORA_STRENGTH_MAX"
|
:max="LORA_STRENGTH_MAX"
|
||||||
:step="LORA_STRENGTH_STEP"
|
:step="LORA_STRENGTH_STEP"
|
||||||
@@ -56,6 +57,7 @@
|
|||||||
<Thumbwheel
|
<Thumbwheel
|
||||||
:model-value="item.strengthClip"
|
:model-value="item.strengthClip"
|
||||||
label="CLIP"
|
label="CLIP"
|
||||||
|
:lock-key="`lora-clip:${item.name}`"
|
||||||
:min="LORA_STRENGTH_MIN"
|
:min="LORA_STRENGTH_MIN"
|
||||||
:max="LORA_STRENGTH_MAX"
|
:max="LORA_STRENGTH_MAX"
|
||||||
:step="LORA_STRENGTH_STEP"
|
:step="LORA_STRENGTH_STEP"
|
||||||
|
|||||||
@@ -1,30 +1,61 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-[6.5rem] min-w-0" :class="disabled ? 'opacity-50' : ''">
|
<div class="w-[6.5rem] min-w-0" :class="disabled ? 'opacity-50' : ''">
|
||||||
<div v-if="label" class="mb-1.5 text-[10px] font-medium uppercase tracking-wider text-zinc-500">{{ label }}</div>
|
<div v-if="label" class="mb-1.5 text-[10px] font-medium uppercase tracking-wider text-zinc-500">{{ label }}</div>
|
||||||
<div class="overflow-hidden rounded-2xl border border-white/10 bg-zinc-950">
|
<div
|
||||||
<div class="border-b border-white/5">
|
class="overflow-hidden rounded-2xl border bg-zinc-950"
|
||||||
|
:class="locked ? 'border-amber-300/35' : 'border-white/10'"
|
||||||
|
>
|
||||||
|
<div class="flex items-stretch border-b border-white/5">
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
<SegmentDigits :value="display" />
|
<SegmentDigits :value="display" />
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex w-7 shrink-0 items-center justify-center border-l transition"
|
||||||
|
:class="locked
|
||||||
|
? 'border-amber-300/25 bg-amber-400/10 text-amber-300'
|
||||||
|
: 'border-white/5 text-zinc-600 hover:text-zinc-300'"
|
||||||
|
:aria-pressed="locked"
|
||||||
|
:aria-label="locked ? `Unlock ${dialName}` : `Lock ${dialName}`"
|
||||||
|
:title="locked ? 'Unlock — wheel and scroll will change this again' : 'Lock — keep this value while you scroll'"
|
||||||
|
@click.stop="toggleLock"
|
||||||
|
@pointerdown.stop
|
||||||
|
>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" class="h-3.5 w-3.5" aria-hidden="true">
|
||||||
|
<rect x="3.2" y="7.2" width="9.6" height="7" rx="1.4" />
|
||||||
|
<path
|
||||||
|
v-if="locked"
|
||||||
|
stroke-linecap="round"
|
||||||
|
d="M5.2 7.2V5.1a2.8 2.8 0 015.6 0v2.1"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
v-else
|
||||||
|
stroke-linecap="round"
|
||||||
|
d="M5.2 7.2V5.1a2.8 2.8 0 015.2-1.4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
ref="root"
|
ref="root"
|
||||||
class="thumbwheel relative w-full select-none"
|
class="thumbwheel relative w-full select-none"
|
||||||
:class="[
|
:class="[
|
||||||
compact ? 'h-28' : 'h-36',
|
compact ? 'h-28' : 'h-36',
|
||||||
disabled ? 'pointer-events-none' : 'cursor-ns-resize'
|
frozen ? 'pointer-events-none cursor-default' : 'cursor-ns-resize'
|
||||||
]"
|
]"
|
||||||
role="slider"
|
role="slider"
|
||||||
:aria-label="ariaLabel || label || 'Value'"
|
:aria-label="ariaLabel || label || 'Value'"
|
||||||
:aria-valuemin="min"
|
:aria-valuemin="min"
|
||||||
:aria-valuemax="max"
|
:aria-valuemax="max"
|
||||||
:aria-valuenow="model"
|
:aria-valuenow="model"
|
||||||
:aria-disabled="disabled"
|
:aria-disabled="disabled || locked"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@pointerdown="onPointerDown"
|
@pointerdown="onPointerDown"
|
||||||
@wheel.prevent="onWheel"
|
|
||||||
@keydown="onKey"
|
@keydown="onKey"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="thumbwheel-drum pointer-events-none absolute inset-y-1 left-1 right-8 overflow-hidden rounded-xl"
|
class="thumbwheel-drum pointer-events-none absolute inset-y-1 left-1 right-8 overflow-hidden rounded-xl"
|
||||||
|
:class="locked ? 'opacity-40' : ''"
|
||||||
:style="{ backgroundPositionY: `${drumShift}px` }"
|
:style="{ backgroundPositionY: `${drumShift}px` }"
|
||||||
/>
|
/>
|
||||||
<div class="pointer-events-none absolute inset-y-2 right-2.5 w-2.5 rounded-full bg-zinc-900 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08)]">
|
<div class="pointer-events-none absolute inset-y-2 right-2.5 w-2.5 rounded-full bg-zinc-900 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08)]">
|
||||||
@@ -47,6 +78,7 @@ const props = withDefaults(defineProps<{
|
|||||||
coarseStep?: number
|
coarseStep?: number
|
||||||
label?: string
|
label?: string
|
||||||
ariaLabel?: string
|
ariaLabel?: string
|
||||||
|
lockKey?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
compact?: boolean
|
compact?: boolean
|
||||||
digits?: number
|
digits?: number
|
||||||
@@ -56,6 +88,7 @@ const props = withDefaults(defineProps<{
|
|||||||
coarseStep: 1,
|
coarseStep: 1,
|
||||||
label: '',
|
label: '',
|
||||||
ariaLabel: '',
|
ariaLabel: '',
|
||||||
|
lockKey: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
compact: false,
|
compact: false,
|
||||||
digits: 1
|
digits: 1
|
||||||
@@ -63,12 +96,36 @@ const props = withDefaults(defineProps<{
|
|||||||
|
|
||||||
const model = defineModel<number>({ required: true })
|
const model = defineModel<number>({ required: true })
|
||||||
const root = ref<HTMLElement | null>(null)
|
const root = ref<HTMLElement | null>(null)
|
||||||
|
const locked = ref(false)
|
||||||
|
|
||||||
|
const dialName = computed(() => props.label || props.ariaLabel || 'dial')
|
||||||
|
const storeKey = computed(() => {
|
||||||
|
const id = (props.lockKey || props.label || props.ariaLabel || '').trim().toLowerCase()
|
||||||
|
return id ? `aigen-thumb-lock:${id}` : ''
|
||||||
|
})
|
||||||
|
const frozen = computed(() => props.disabled || locked.value)
|
||||||
const span = computed(() => Math.max(props.max - props.min, 0.0001))
|
const span = computed(() => Math.max(props.max - props.min, 0.0001))
|
||||||
const pct = computed(() => ((clamp(model.value) - props.min) / span.value) * 100)
|
const pct = computed(() => ((clamp(model.value) - props.min) / span.value) * 100)
|
||||||
const display = computed(() => format(model.value))
|
const display = computed(() => format(model.value))
|
||||||
const drumShift = computed(() => Math.round((pct.value / 100) * 72))
|
const drumShift = computed(() => Math.round((pct.value / 100) * 72))
|
||||||
|
|
||||||
|
function readLock() {
|
||||||
|
if (!storeKey.value || typeof localStorage === 'undefined') return
|
||||||
|
try {
|
||||||
|
locked.value = localStorage.getItem(storeKey.value) === '1'
|
||||||
|
} catch {
|
||||||
|
locked.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLock() {
|
||||||
|
locked.value = !locked.value
|
||||||
|
if (!storeKey.value || typeof localStorage === 'undefined') return
|
||||||
|
try {
|
||||||
|
localStorage.setItem(storeKey.value, locked.value ? '1' : '0')
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
function clamp(value: number) {
|
function clamp(value: number) {
|
||||||
if (!Number.isFinite(value)) return props.min
|
if (!Number.isFinite(value)) return props.min
|
||||||
const step = props.step || 0.1
|
const step = props.step || 0.1
|
||||||
@@ -89,13 +146,14 @@ function nudge(delta: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onWheel(event: WheelEvent) {
|
function onWheel(event: WheelEvent) {
|
||||||
if (props.disabled) return
|
if (frozen.value) return
|
||||||
|
event.preventDefault()
|
||||||
const step = event.shiftKey ? props.coarseStep : props.fineStep
|
const step = event.shiftKey ? props.coarseStep : props.fineStep
|
||||||
nudge(event.deltaY < 0 ? step : -step)
|
nudge(event.deltaY < 0 ? step : -step)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKey(event: KeyboardEvent) {
|
function onKey(event: KeyboardEvent) {
|
||||||
if (props.disabled) return
|
if (frozen.value) return
|
||||||
const step = event.shiftKey ? props.coarseStep : props.fineStep
|
const step = event.shiftKey ? props.coarseStep : props.fineStep
|
||||||
if (event.key === 'ArrowUp' || event.key === 'ArrowRight') {
|
if (event.key === 'ArrowUp' || event.key === 'ArrowRight') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -113,7 +171,7 @@ function onKey(event: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onPointerDown(event: PointerEvent) {
|
function onPointerDown(event: PointerEvent) {
|
||||||
if (props.disabled || event.button !== 0) return
|
if (frozen.value || event.button !== 0) return
|
||||||
const el = root.value
|
const el = root.value
|
||||||
if (!el) return
|
if (!el) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -134,6 +192,16 @@ function onPointerDown(event: PointerEvent) {
|
|||||||
el.addEventListener('pointerup', onUp)
|
el.addEventListener('pointerup', onUp)
|
||||||
el.addEventListener('pointercancel', onUp)
|
el.addEventListener('pointercancel', onUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
readLock()
|
||||||
|
const el = root.value
|
||||||
|
if (!el) return
|
||||||
|
el.addEventListener('wheel', onWheel, { passive: false })
|
||||||
|
onBeforeUnmount(() => el.removeEventListener('wheel', onWheel))
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(storeKey, readLock)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
+24
-6
@@ -362,12 +362,11 @@
|
|||||||
<button
|
<button
|
||||||
v-if="imagePassMode === 'batch'"
|
v-if="imagePassMode === 'batch'"
|
||||||
type="button"
|
type="button"
|
||||||
class="w-full rounded-2xl border border-dashed px-4 py-2.5 text-left text-sm transition"
|
class="w-full scroll-mt-24 rounded-2xl border border-dashed px-4 py-2.5 text-left text-sm transition"
|
||||||
:class="imageBatchScript.trim()
|
:class="imageBatchScript.trim()
|
||||||
? 'border-amber-300/40 bg-amber-400/5 text-amber-100 hover:border-amber-300'
|
? 'border-amber-300/40 bg-amber-400/5 text-amber-100 hover:border-amber-300'
|
||||||
: 'border-white/15 text-zinc-300 hover:border-amber-300/40 hover:text-amber-100'"
|
: 'border-white/15 text-zinc-300 hover:border-amber-300/40 hover:text-amber-100'"
|
||||||
:disabled="editBusy"
|
@click.stop.prevent="openImageBatchModal"
|
||||||
@click="openImageBatchModal"
|
|
||||||
>
|
>
|
||||||
<span class="block font-semibold">{{ imageBatchScript.trim() ? 'Edit batch script' : 'Paste batch script' }}</span>
|
<span class="block font-semibold">{{ imageBatchScript.trim() ? 'Edit batch script' : 'Paste batch script' }}</span>
|
||||||
<span class="text-[11px] leading-snug text-zinc-400">{{ imageBatchButtonHint }}</span>
|
<span class="text-[11px] leading-snug text-zinc-400">{{ imageBatchButtonHint }}</span>
|
||||||
@@ -796,6 +795,7 @@
|
|||||||
<span class="mb-1 block text-zinc-300">Strength (denoise)</span>
|
<span class="mb-1 block text-zinc-300">Strength (denoise)</span>
|
||||||
<Thumbwheel
|
<Thumbwheel
|
||||||
v-model="v2Denoise"
|
v-model="v2Denoise"
|
||||||
|
lock-key="v2-denoise"
|
||||||
:min="IMAGE_V2_DENOISE_MIN"
|
:min="IMAGE_V2_DENOISE_MIN"
|
||||||
:max="IMAGE_V2_DENOISE_MAX"
|
:max="IMAGE_V2_DENOISE_MAX"
|
||||||
:step="IMAGE_V2_DENOISE_STEP"
|
:step="IMAGE_V2_DENOISE_STEP"
|
||||||
@@ -857,6 +857,7 @@
|
|||||||
<Thumbwheel
|
<Thumbwheel
|
||||||
v-model="v2Megapixels"
|
v-model="v2Megapixels"
|
||||||
aria-label="Megapixels"
|
aria-label="Megapixels"
|
||||||
|
lock-key="megapixels"
|
||||||
:min="IMAGE_SCALE_MP_MIN"
|
:min="IMAGE_SCALE_MP_MIN"
|
||||||
:max="IMAGE_SCALE_MP_MAX"
|
:max="IMAGE_SCALE_MP_MAX"
|
||||||
:step="IMAGE_SCALE_MP_STEP"
|
:step="IMAGE_SCALE_MP_STEP"
|
||||||
@@ -5879,23 +5880,38 @@ function reconstructImageBatchScript() {
|
|||||||
|
|
||||||
function openImageBatchModal() {
|
function openImageBatchModal() {
|
||||||
imageBatchDraft.value = reconstructImageBatchScript()
|
imageBatchDraft.value = reconstructImageBatchScript()
|
||||||
|
requestAnimationFrame(() => {
|
||||||
imageBatchOpen.value = true
|
imageBatchOpen.value = true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyImageBatch() {
|
function applyImageBatch(text?: string) {
|
||||||
const shots = parseImageBatchScript(imageBatchDraft.value)
|
const src = String(text ?? imageBatchDraft.value)
|
||||||
|
const shots = parseImageBatchScript(src)
|
||||||
if (!shots.length) {
|
if (!shots.length) {
|
||||||
toast('Paste at least one prompt. Put shot 1, shot 2 on their own lines.')
|
toast('Paste at least one prompt. Put shot 1, shot 2 on their own lines.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imagePassMode.value = 'batch'
|
imagePassMode.value = 'batch'
|
||||||
imageBatchScript.value = imageBatchDraft.value
|
imageBatchDraft.value = src
|
||||||
|
imageBatchScript.value = src
|
||||||
prompt.value = shots[0].prompt
|
prompt.value = shots[0].prompt
|
||||||
editPassQueue.value = shots.slice(1).map(item => ({ id: crypto.randomUUID(), prompt: item.prompt }))
|
editPassQueue.value = shots.slice(1).map(item => ({ id: crypto.randomUUID(), prompt: item.prompt }))
|
||||||
imageBatchOpen.value = false
|
imageBatchOpen.value = false
|
||||||
toast(shots.length === 1 ? 'Loaded 1 prompt' : `Loaded ${shots.length} prompts on the start still`)
|
toast(shots.length === 1 ? 'Loaded 1 prompt' : `Loaded ${shots.length} prompts on the start still`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function absorbPastedImageBatch() {
|
||||||
|
if (imagePassMode.value !== 'batch') return
|
||||||
|
if (!(studioMode.value === 'edit' || v2PassChaining.value)) return
|
||||||
|
if (editPassQueue.value.length) return
|
||||||
|
const shots = parseImageBatchScript(prompt.value)
|
||||||
|
if (shots.length < 2) return
|
||||||
|
imageBatchScript.value = prompt.value
|
||||||
|
prompt.value = shots[0].prompt
|
||||||
|
editPassQueue.value = shots.slice(1).map(item => ({ id: crypto.randomUUID(), prompt: item.prompt }))
|
||||||
|
}
|
||||||
|
|
||||||
function imagePassUnit() {
|
function imagePassUnit() {
|
||||||
return imagePassMode.value === 'batch' ? 'Batch' : 'Pass'
|
return imagePassMode.value === 'batch' ? 'Batch' : 'Pass'
|
||||||
}
|
}
|
||||||
@@ -5922,6 +5938,7 @@ async function editImage() {
|
|||||||
toast('Choose a library folder before editing.')
|
toast('Choose a library folder before editing.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
absorbPastedImageBatch()
|
||||||
const extraPasses = editPassQueue.value.map(item => ({ prompt: item.prompt.trim() }))
|
const extraPasses = editPassQueue.value.map(item => ({ prompt: item.prompt.trim() }))
|
||||||
const hideOut = hideThumbnail.value
|
const hideOut = hideThumbnail.value
|
||||||
try {
|
try {
|
||||||
@@ -6137,6 +6154,7 @@ async function editImageV2() {
|
|||||||
toast('Each extra pass needs a prompt.')
|
toast('Each extra pass needs a prompt.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
absorbPastedImageBatch()
|
||||||
const extraPasses = v2PassChaining.value ? editPassQueue.value.map(item => ({ prompt: item.prompt.trim() })) : []
|
const extraPasses = v2PassChaining.value ? editPassQueue.value.map(item => ({ prompt: item.prompt.trim() })) : []
|
||||||
const hideOut = hideThumbnail.value
|
const hideOut = hideThumbnail.value
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user