diff --git a/web/src/components/image-settings-panel.tsx b/web/src/components/image-settings-panel.tsx index 72249081e..e6126d9eb 100644 --- a/web/src/components/image-settings-panel.tsx +++ b/web/src/components/image-settings-panel.tsx @@ -68,7 +68,7 @@ export function ImageSettingsPanel({ config, onConfigChange, theme, showTitle = ? [] : usesResolutionPicker && activeResolution ? resolutionOptions.filter((item) => item.tier === activeResolution.tier).map((item) => ({ value: item.ratio, label: item.ratio, size: item.size, width: item.width, height: item.height, icon: item.width === item.height ? "square" : item.width > item.height ? "landscape" : "portrait" })) - : aspectOptions.filter((item) => imageOptionAllowed(profile, item)); + : imageAspectOptions(profile); const selectedAspect = availableAspects.find((item) => imageOptionValue(profile, item) === activeSize || item.value === activeSize) || availableAspects.find((item) => item.label === activeRatio); const dimensions = readSizeDimensions(activeSize, selectedAspect || aspectOptions[0]); const activeQualityOptions = profile.quality.values.map((value) => qualityOptions.find((item) => item.value === value) || { value, label: value }); @@ -199,6 +199,34 @@ function imageOptionAllowed(profile: ImageCapabilityConfig, option: AspectOption return [option.value, option.size, option.width && option.height ? `${option.width}x${option.height}` : ""].filter(Boolean).some((value) => profile.size.values.includes(String(value))); } +// 宽高比选项直接取模型配置 values(与创作页面一致),不在白名单里的比例(如 8:1)也能显示。 +function imageAspectOptions(profile: ImageCapabilityConfig): AspectOption[] { + if (profile.size.parameter === "none") return []; + const values = profile.size.values.filter((value) => value.trim().toLowerCase() !== "auto"); + if (!values.length) return profile.size.allowCustom ? aspectOptions.filter((item) => item.value !== "auto") : []; + return values.map((value) => { + const known = aspectOptions.find((item) => (item.size || item.value) === value || item.value === value); + if (known) return known; + const parts = ratioParts(value); + return { value, label: value, size: value, width: parts?.width || 0, height: parts?.height || 0, icon: "custom" }; + }); +} + +function ratioParts(value: string) { + const pixel = value.trim().match(/^(\d+)x(\d+)$/i); + if (pixel) { + const divisor = gcd(Number(pixel[1]), Number(pixel[2])); + return { width: Number(pixel[1]) / divisor, height: Number(pixel[2]) / divisor }; + } + const ratio = value.trim().match(/^(\d+):(\d+)$/); + if (!ratio) return undefined; + return { width: Number(ratio[1]), height: Number(ratio[2]) }; +} + +function gcd(a: number, b: number): number { + return b ? gcd(b, a % b) : a; +} + function imageOptionValue(profile: ImageCapabilityConfig, option: AspectOption) { const candidates = [option.size, option.value, option.width && option.height ? `${option.width}x${option.height}` : ""].filter(Boolean).map(String); return candidates.find((value) => profile.size.values.includes(value)) || option.size || option.value || "auto"; diff --git a/web/src/lib/model-selection.ts b/web/src/lib/model-selection.ts index 7621a0f7d..0d6b513e8 100644 --- a/web/src/lib/model-selection.ts +++ b/web/src/lib/model-selection.ts @@ -110,14 +110,17 @@ export function resolveCompatibleModel(config: AiConfig, selected: string, requi // 同显示名分组的模型族:尺寸/比例/分辨率选项取组内全部模型配置的并集, // 让用户能看到并选择任意成员支持的能力,选中后由兼容路由落到具体模型。 +// 质量、透明背景等其余能力取当前选中(路由后)模型的配置,与创作页面一致。 export function mergedImageCapabilityConfig(config: AiConfig, selected: string): ImageCapabilityConfig { const options = selectableModelsByCapability(config, "image"); const group = options.length ? groupModelsByDisplayName(config, options).find((item) => item.models.includes(selected)) : undefined; const models = group?.models.length ? group.models : [selected]; + const selectedProfile = modelCapabilityConfigFor(config, selected).image; const profiles = models.map((model) => modelCapabilityConfigFor(config, model).image).filter((profile): profile is ImageCapabilityConfig => Boolean(profile)); - if (profiles.length <= 1) return profiles[0] || defaultImageCapabilityConfig(); + if (profiles.length <= 1) return selectedProfile || defaultImageCapabilityConfig(); const values = Array.from(new Set(profiles.flatMap((profile) => profile.size.values))); - return { ...profiles[0], size: { ...profiles[0].size, values } }; + const base = selectedProfile || profiles[0]; + return { ...base, size: { ...base.size, values } }; } // 切换模型后初始化图片参数为该模型能力默认值,避免旧参数在目标模型族不兼容导致无法切换。