/** Qwen-safe ~1 MP grid (÷32). Do not use native 2K bins on 16 GB. */ export const QWEN21_SIZE_TABLE = { '1:1': [1024, 1024], '16:9': [1536, 864], '9:16': [864, 1536], '4:3': [1152, 864], '3:4': [864, 1152], '3:2': [1248, 832], '2:3': [832, 1248], '21:9': [1536, 672], '9:21': [672, 1536] } export const QWEN21_ASPECT_IDS = new Set(['auto', ...Object.keys(QWEN21_SIZE_TABLE)]) function parseAspectRatio(aspect) { const m = String(aspect || '').trim().match(/^(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)$/) if (!m) return null const w = Number(m[1]), h = Number(m[2]) if (!(w > 0 && h > 0)) return null return w / h } function nearestQwen21Aspect(aspect) { const target = parseAspectRatio(aspect) if (target == null) return null let best = '1:1', bestDiff = Infinity for (const [key, [w, h]] of Object.entries(QWEN21_SIZE_TABLE)) { const diff = Math.abs(w / h - target) if (diff < bestDiff) { bestDiff = diff; best = key } } return best } export function resolveQwen21Size(aspect, source) { const key = String(aspect || 'auto').trim() if (QWEN21_SIZE_TABLE[key]) { const [width, height] = QWEN21_SIZE_TABLE[key] return { width, height } } if (key === 'auto') { if (source?.width && source?.height) { const nearest = nearestQwen21Aspect(`${source.width}:${source.height}`) if (nearest) { const [width, height] = QWEN21_SIZE_TABLE[nearest] return { width, height } } } return { width: 1024, height: 1024 } } const nearest = nearestQwen21Aspect(key) if (nearest) { const [width, height] = QWEN21_SIZE_TABLE[nearest] return { width, height } } console.warn(`[qwen21] unknown aspect "${key}"; using 1024×1024`) return { width: 1024, height: 1024 } }