Restore automatic output size that matches the input still.
Keep 16:9, 9:16, and square as overrides, but default aspect back to Auto so MiniMax follows the source image. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+44
-15
@@ -127,7 +127,7 @@
|
||||
<div class="mt-4 space-y-4">
|
||||
<div>
|
||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Aspect ratio / resolution</p>
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
<div class="grid grid-cols-2 gap-2 lg:grid-cols-4">
|
||||
<button
|
||||
v-for="option in aspects"
|
||||
:key="option.id"
|
||||
@@ -137,10 +137,10 @@
|
||||
@click="aspect = option.id"
|
||||
>
|
||||
<span class="block font-semibold">{{ option.label }}</span>
|
||||
<span class="text-xs text-zinc-400">{{ option.hint }}</span>
|
||||
<span class="text-xs text-zinc-400">{{ option.id === 'auto' ? autoHint : option.hint }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">Output {{ outputSize.width }} × {{ outputSize.height }}</p>
|
||||
<p class="mt-2 text-xs text-zinc-500">Output {{ outputSize.width }} × {{ outputSize.height }}{{ aspect === 'auto' ? ' · matching the still' : '' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-2 text-xs uppercase tracking-wider text-zinc-500">Steps</p>
|
||||
@@ -555,7 +555,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type AspectId = '16:9' | '9:16' | '1:1'
|
||||
type AspectId = 'auto' | '16:9' | '9:16' | '1:1'
|
||||
|
||||
interface LibraryFolder {
|
||||
id: string
|
||||
@@ -621,13 +621,14 @@ interface RetryDraft {
|
||||
scheduler?: string
|
||||
}
|
||||
|
||||
const PRESETS: Record<AspectId, { width: number; height: number }> = {
|
||||
const PRESETS: Record<Exclude<AspectId, 'auto'>, { width: number; height: number }> = {
|
||||
'16:9': { width: 1344, height: 768 },
|
||||
'9:16': { width: 768, height: 1344 },
|
||||
'1:1': { width: 960, height: 960 }
|
||||
}
|
||||
|
||||
const aspects = [
|
||||
{ id: 'auto' as const, label: 'Automatic', hint: 'Match the still' },
|
||||
{ id: '16:9' as const, label: '16:9 Landscape', hint: '1344 × 768' },
|
||||
{ id: '9:16' as const, label: '9:16 Portrait / Shorts', hint: '768 × 1344' },
|
||||
{ id: '1:1' as const, label: '1:1 Square', hint: '960 × 960' }
|
||||
@@ -641,7 +642,7 @@ const dragging = ref(false)
|
||||
const prompt = ref('')
|
||||
const clipName = ref('')
|
||||
const outputDownloadName = ref('aigen')
|
||||
const aspect = ref<AspectId>('16:9')
|
||||
const aspect = ref<AspectId>('auto')
|
||||
const imageWidth = ref(0)
|
||||
const imageHeight = ref(0)
|
||||
const turbo = ref(true)
|
||||
@@ -747,16 +748,44 @@ const comfyBadge = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
function normalizeAspect(raw?: string, width?: number, height?: number): AspectId {
|
||||
if (raw === '16:9' || raw === '9:16' || raw === '1:1') return raw
|
||||
if (width && height) {
|
||||
if (width === height) return '1:1'
|
||||
if (height > width) return '9:16'
|
||||
}
|
||||
return '16:9'
|
||||
function snap32(value: number) {
|
||||
return Math.max(32, Math.round(value / 32) * 32)
|
||||
}
|
||||
|
||||
const outputSize = computed(() => PRESETS[aspect.value])
|
||||
function autoResolution(srcWidth: number, srcHeight: number) {
|
||||
const ratio = srcWidth / Math.max(srcHeight, 1)
|
||||
let width: number
|
||||
let height: number
|
||||
if (ratio >= 1) {
|
||||
height = 768
|
||||
width = Math.min(1344, height * ratio)
|
||||
} else {
|
||||
width = 768
|
||||
height = Math.min(1344, width / ratio)
|
||||
}
|
||||
width = snap32(width)
|
||||
height = snap32(height)
|
||||
if (width > 1344) width = 1344
|
||||
if (height > 1344) height = 1344
|
||||
return { width: Math.max(32, width), height: Math.max(32, height) }
|
||||
}
|
||||
|
||||
function normalizeAspect(raw?: string): AspectId {
|
||||
if (raw === 'auto' || raw === '16:9' || raw === '9:16' || raw === '1:1') return raw
|
||||
return 'auto'
|
||||
}
|
||||
|
||||
const outputSize = computed(() => {
|
||||
if (aspect.value !== 'auto') return PRESETS[aspect.value]
|
||||
if (imageWidth.value && imageHeight.value) return autoResolution(imageWidth.value, imageHeight.value)
|
||||
return { width: 1344, height: 768 }
|
||||
})
|
||||
|
||||
const autoHint = computed(() => {
|
||||
if (!imageWidth.value || !imageHeight.value) return 'Match the still'
|
||||
const size = autoResolution(imageWidth.value, imageHeight.value)
|
||||
return `${size.width} × ${size.height} · from still`
|
||||
})
|
||||
const frameCount = computed(() => Math.max(5, Math.floor(duration.value * fps.value)))
|
||||
const cfgPct = computed(() => ((cfg.value - CFG_MIN) / (CFG_MAX - CFG_MIN)) * 100)
|
||||
const currentClip = computed(() => clips.value.find(clip => clip.id === currentClipId.value))
|
||||
@@ -913,7 +942,7 @@ function restoreScheduler(raw?: string) {
|
||||
function restoreAdvanced(item: { turbo: boolean; aspect?: string; width?: number; height?: number; cfg?: number; fps?: number; samplerName?: string; scheduler?: string }) {
|
||||
cfgTouched.value = true
|
||||
turbo.value = item.turbo
|
||||
aspect.value = normalizeAspect(item.aspect, item.width, item.height)
|
||||
aspect.value = normalizeAspect(item.aspect)
|
||||
if (typeof item.cfg === 'number') {
|
||||
cfg.value = clampCfg(item.cfg)
|
||||
} else {
|
||||
|
||||
@@ -111,7 +111,7 @@ export default defineEventHandler(async (event) => {
|
||||
folderLocked,
|
||||
name: clipName,
|
||||
prompt,
|
||||
aspect: fields.aspect || '16:9',
|
||||
aspect: fields.aspect || 'auto',
|
||||
width,
|
||||
height,
|
||||
steps,
|
||||
@@ -177,7 +177,7 @@ export default defineEventHandler(async (event) => {
|
||||
seed,
|
||||
width,
|
||||
height,
|
||||
aspect: fields.aspect || '16:9',
|
||||
aspect: fields.aspect || 'auto',
|
||||
cfg,
|
||||
fps,
|
||||
sampler_name: samplerName,
|
||||
|
||||
@@ -85,10 +85,14 @@ function jpegSize(buf: Buffer) {
|
||||
return null
|
||||
}
|
||||
|
||||
export function resolveOutputSize(aspect: string | undefined, _image: Buffer, fallbackWidth?: number, fallbackHeight?: number) {
|
||||
export function resolveOutputSize(aspect: string | undefined, image: Buffer, fallbackWidth?: number, fallbackHeight?: number) {
|
||||
if (aspect && ASPECT_PRESETS[aspect]) {
|
||||
return ASPECT_PRESETS[aspect]
|
||||
}
|
||||
const size = imageDimensions(image)
|
||||
if (size?.width && size?.height) {
|
||||
return autoResolution(size.width, size.height)
|
||||
}
|
||||
if (Number(fallbackWidth) && Number(fallbackHeight)) {
|
||||
return { width: snap32(Number(fallbackWidth)), height: snap32(Number(fallbackHeight)) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user