feat: customize style pro button colors
This commit is contained in:
parent
0869eefee3
commit
c433b714cd
@ -106,6 +106,47 @@ function modelLabel(model: AiModelOption) {
|
|||||||
return `${name}${id}${model.isHidden ? " · 前端隐藏" : ""}`;
|
return `${name}${id}${model.isHidden ? " · 前端隐藏" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ColorSettingField({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}) {
|
||||||
|
const shortHex = value.match(/^#([0-9a-fA-F]{3})$/);
|
||||||
|
const pickerValue = /^#[0-9a-fA-F]{6}$/.test(value)
|
||||||
|
? value
|
||||||
|
: shortHex
|
||||||
|
? `#${shortHex[1]
|
||||||
|
.split("")
|
||||||
|
.map((char) => `${char}${char}`)
|
||||||
|
.join("")}`
|
||||||
|
: "#000000";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>{label}</Label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
className="h-10 w-14 shrink-0 cursor-pointer p-1"
|
||||||
|
onChange={(event) => onChange(event.target.value)}
|
||||||
|
type="color"
|
||||||
|
value={pickerValue}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
className="font-mono"
|
||||||
|
maxLength={7}
|
||||||
|
onChange={(event) => onChange(event.target.value)}
|
||||||
|
placeholder="#2563eb"
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function StyleProSettingsClient() {
|
export function StyleProSettingsClient() {
|
||||||
const { data, mutate } = useSWR<SettingsResponse>(
|
const { data, mutate } = useSWR<SettingsResponse>(
|
||||||
`/api/admin/settings?key=${STYLE_PRO_CONFIG_KEY}`,
|
`/api/admin/settings?key=${STYLE_PRO_CONFIG_KEY}`,
|
||||||
@ -135,11 +176,6 @@ export function StyleProSettingsClient() {
|
|||||||
allImageModels.filter((model) => model.isHidden !== true),
|
allImageModels.filter((model) => model.isHidden !== true),
|
||||||
[allImageModels]
|
[allImageModels]
|
||||||
);
|
);
|
||||||
const hiddenImageModels = useMemo(
|
|
||||||
() => allImageModels.filter((model) => model.isHidden === true),
|
|
||||||
[allImageModels]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateConfig = (updates: Partial<StyleProConfig>) => {
|
const updateConfig = (updates: Partial<StyleProConfig>) => {
|
||||||
setConfig((current) => ({ ...current, ...updates }));
|
setConfig((current) => ({ ...current, ...updates }));
|
||||||
};
|
};
|
||||||
@ -304,7 +340,7 @@ export function StyleProSettingsClient() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>基础设置</CardTitle>
|
<CardTitle>基础设置</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
控制功能开关、风格化公开模型、实景化隐藏模型、默认提示词和生成规格。
|
控制功能开关、风格化公开模型、实景化模型链路、默认提示词和生成规格。
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-5 md:grid-cols-2">
|
<CardContent className="grid gap-5 md:grid-cols-2">
|
||||||
@ -356,9 +392,9 @@ export function StyleProSettingsClient() {
|
|||||||
|
|
||||||
<div className="space-y-4 rounded-xl border p-4 md:col-span-2">
|
<div className="space-y-4 rounded-xl border p-4 md:col-span-2">
|
||||||
<div>
|
<div>
|
||||||
<Label>实景化隐藏模型链路</Label>
|
<Label>实景化模型链路</Label>
|
||||||
<p className="mt-1 text-muted-foreground text-xs">
|
<p className="mt-1 text-muted-foreground text-xs">
|
||||||
前台只显示“实景化”模式,不展示模型名称;这里仅可选择已在 AI 模型中设置为“前端隐藏”的图片模型。第一阶段失败时第二阶段不会继续执行。
|
前台只显示“实景化”模式,不展示模型名称;这里可选择所有已启用的生图模型。第一阶段失败时第二阶段不会继续执行。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
@ -379,16 +415,16 @@ export function StyleProSettingsClient() {
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value={AUTO_MODEL_VALUE}>未配置</SelectItem>
|
<SelectItem value={AUTO_MODEL_VALUE}>未配置</SelectItem>
|
||||||
{config.realisticFirstModelId &&
|
{config.realisticFirstModelId &&
|
||||||
!hiddenImageModels.some(
|
!allImageModels.some(
|
||||||
(model) =>
|
(model) =>
|
||||||
model.modelId === config.realisticFirstModelId ||
|
model.modelId === config.realisticFirstModelId ||
|
||||||
model.id === config.realisticFirstModelId
|
model.id === config.realisticFirstModelId
|
||||||
) ? (
|
) ? (
|
||||||
<SelectItem value={config.realisticFirstModelId}>
|
<SelectItem value={config.realisticFirstModelId}>
|
||||||
当前保存值(未在隐藏模型列表中):{config.realisticFirstModelId}
|
当前保存值(未在模型列表中):{config.realisticFirstModelId}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
) : null}
|
) : null}
|
||||||
{hiddenImageModels.map((model) => {
|
{allImageModels.map((model) => {
|
||||||
const value = model.modelId || model.id;
|
const value = model.modelId || model.id;
|
||||||
return (
|
return (
|
||||||
<SelectItem key={model.id} value={value}>
|
<SelectItem key={model.id} value={value}>
|
||||||
@ -416,16 +452,16 @@ export function StyleProSettingsClient() {
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value={AUTO_MODEL_VALUE}>未配置</SelectItem>
|
<SelectItem value={AUTO_MODEL_VALUE}>未配置</SelectItem>
|
||||||
{config.realisticSecondModelId &&
|
{config.realisticSecondModelId &&
|
||||||
!hiddenImageModels.some(
|
!allImageModels.some(
|
||||||
(model) =>
|
(model) =>
|
||||||
model.modelId === config.realisticSecondModelId ||
|
model.modelId === config.realisticSecondModelId ||
|
||||||
model.id === config.realisticSecondModelId
|
model.id === config.realisticSecondModelId
|
||||||
) ? (
|
) ? (
|
||||||
<SelectItem value={config.realisticSecondModelId}>
|
<SelectItem value={config.realisticSecondModelId}>
|
||||||
当前保存值(未在隐藏模型列表中):{config.realisticSecondModelId}
|
当前保存值(未在模型列表中):{config.realisticSecondModelId}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
) : null}
|
) : null}
|
||||||
{hiddenImageModels.map((model) => {
|
{allImageModels.map((model) => {
|
||||||
const value = model.modelId || model.id;
|
const value = model.modelId || model.id;
|
||||||
return (
|
return (
|
||||||
<SelectItem key={model.id} value={value}>
|
<SelectItem key={model.id} value={value}>
|
||||||
@ -523,6 +559,50 @@ export function StyleProSettingsClient() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4 rounded-xl border p-4 md:col-span-2">
|
||||||
|
<div>
|
||||||
|
<Label>前台颜色</Label>
|
||||||
|
<p className="mt-1 text-muted-foreground text-xs">
|
||||||
|
控制风格化 PRO 页面里的分类标题、提示词按钮和选中按钮颜色,保存后前台立即按配置渲染。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
<ColorSettingField
|
||||||
|
label="标题文字颜色"
|
||||||
|
onChange={(titleTextColor) => updateConfig({ titleTextColor })}
|
||||||
|
value={config.titleTextColor}
|
||||||
|
/>
|
||||||
|
<ColorSettingField
|
||||||
|
label="按钮颜色"
|
||||||
|
onChange={(buttonBackgroundColor) =>
|
||||||
|
updateConfig({ buttonBackgroundColor })
|
||||||
|
}
|
||||||
|
value={config.buttonBackgroundColor}
|
||||||
|
/>
|
||||||
|
<ColorSettingField
|
||||||
|
label="按钮字体颜色"
|
||||||
|
onChange={(buttonTextColor) =>
|
||||||
|
updateConfig({ buttonTextColor })
|
||||||
|
}
|
||||||
|
value={config.buttonTextColor}
|
||||||
|
/>
|
||||||
|
<ColorSettingField
|
||||||
|
label="选中按钮颜色"
|
||||||
|
onChange={(selectedButtonBackgroundColor) =>
|
||||||
|
updateConfig({ selectedButtonBackgroundColor })
|
||||||
|
}
|
||||||
|
value={config.selectedButtonBackgroundColor}
|
||||||
|
/>
|
||||||
|
<ColorSettingField
|
||||||
|
label="选中按钮字体颜色"
|
||||||
|
onChange={(selectedButtonTextColor) =>
|
||||||
|
updateConfig({ selectedButtonTextColor })
|
||||||
|
}
|
||||||
|
value={config.selectedButtonTextColor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 md:col-span-2">
|
<div className="space-y-2 md:col-span-2">
|
||||||
<Label>默认系统提示词</Label>
|
<Label>默认系统提示词</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
@ -18,11 +18,11 @@ export async function GET() {
|
|||||||
() => null
|
() => null
|
||||||
);
|
);
|
||||||
const config = normalizeStyleProConfig(setting?.value);
|
const config = normalizeStyleProConfig(setting?.value);
|
||||||
const hiddenEntries = (
|
const realisticModelEntries = (
|
||||||
await getStyleProImageModelEntries({ includeHidden: true }).catch(
|
await getStyleProImageModelEntries({ includeHidden: true }).catch(
|
||||||
() => []
|
() => []
|
||||||
)
|
)
|
||||||
).filter((entry) => entry.model.isHidden === true);
|
);
|
||||||
const publicConfig = toPublicStyleProConfig(config);
|
const publicConfig = toPublicStyleProConfig(config);
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
@ -31,11 +31,11 @@ export async function GET() {
|
|||||||
...publicConfig,
|
...publicConfig,
|
||||||
realisticModeAvailable: Boolean(
|
realisticModeAvailable: Boolean(
|
||||||
findStyleProImageModelByRef(
|
findStyleProImageModelByRef(
|
||||||
hiddenEntries,
|
realisticModelEntries,
|
||||||
config.realisticFirstModelId
|
config.realisticFirstModelId
|
||||||
) &&
|
) &&
|
||||||
findStyleProImageModelByRef(
|
findStyleProImageModelByRef(
|
||||||
hiddenEntries,
|
realisticModelEntries,
|
||||||
config.realisticSecondModelId
|
config.realisticSecondModelId
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
@ -202,13 +202,9 @@ export async function POST(request: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageModelEntries = (
|
const imageModelEntries = await getStyleProImageModelEntries({
|
||||||
await getStyleProImageModelEntries({
|
includeHidden: generationMode === "realistic",
|
||||||
includeHidden: generationMode === "realistic",
|
});
|
||||||
})
|
|
||||||
).filter((entry) =>
|
|
||||||
generationMode === "realistic" ? entry.model.isHidden === true : true
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedModels =
|
const selectedModels =
|
||||||
generationMode === "realistic"
|
generationMode === "realistic"
|
||||||
|
|||||||
@ -24,14 +24,11 @@ export async function GET() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [entries, allHiddenCapableEntries, setting] = await Promise.all([
|
const [entries, realisticModelEntries, setting] = await Promise.all([
|
||||||
getStyleProImageModelEntries({ includeHidden: false }),
|
getStyleProImageModelEntries({ includeHidden: false }),
|
||||||
getStyleProImageModelEntries({ includeHidden: true }),
|
getStyleProImageModelEntries({ includeHidden: true }),
|
||||||
getSystemSetting({ key: STYLE_PRO_CONFIG_KEY }).catch(() => null),
|
getSystemSetting({ key: STYLE_PRO_CONFIG_KEY }).catch(() => null),
|
||||||
]);
|
]);
|
||||||
const hiddenEntries = allHiddenCapableEntries.filter(
|
|
||||||
(entry) => entry.model.isHidden === true
|
|
||||||
);
|
|
||||||
const config = normalizeStyleProConfig(setting?.value);
|
const config = normalizeStyleProConfig(setting?.value);
|
||||||
const configuredDefault = findStyleProImageModelByRef(
|
const configuredDefault = findStyleProImageModelByRef(
|
||||||
entries,
|
entries,
|
||||||
@ -43,11 +40,11 @@ export async function GET() {
|
|||||||
entries[0] ||
|
entries[0] ||
|
||||||
null;
|
null;
|
||||||
const realisticFirstModel = findStyleProImageModelByRef(
|
const realisticFirstModel = findStyleProImageModelByRef(
|
||||||
hiddenEntries,
|
realisticModelEntries,
|
||||||
config.realisticFirstModelId
|
config.realisticFirstModelId
|
||||||
);
|
);
|
||||||
const realisticSecondModel = findStyleProImageModelByRef(
|
const realisticSecondModel = findStyleProImageModelByRef(
|
||||||
hiddenEntries,
|
realisticModelEntries,
|
||||||
config.realisticSecondModelId
|
config.realisticSecondModelId
|
||||||
);
|
);
|
||||||
const realisticUnitCost =
|
const realisticUnitCost =
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import { Clock3, Loader2, LogOut, Sparkles, Upload } from "lucide-react";
|
import { Clock3, Loader2, LogOut, Sparkles, Upload } from "lucide-react";
|
||||||
import { signOut, useSession } from "next-auth/react";
|
import { signOut, useSession } from "next-auth/react";
|
||||||
import {
|
import {
|
||||||
|
type CSSProperties,
|
||||||
type KeyboardEvent as ReactKeyboardEvent,
|
type KeyboardEvent as ReactKeyboardEvent,
|
||||||
type PointerEvent as ReactPointerEvent,
|
type PointerEvent as ReactPointerEvent,
|
||||||
useCallback,
|
useCallback,
|
||||||
@ -1014,6 +1015,15 @@ export function StyleProWorkspace({
|
|||||||
? `还差:${missingRequiredCategories[0].title}`
|
? `还差:${missingRequiredCategories[0].title}`
|
||||||
: "";
|
: "";
|
||||||
const promptDisplayMode = config?.promptDisplayMode || "stacked";
|
const promptDisplayMode = config?.promptDisplayMode || "stacked";
|
||||||
|
const themeStyle = {
|
||||||
|
"--style-pro-title-color": config?.titleTextColor || "#2563eb",
|
||||||
|
"--style-pro-button-bg": config?.buttonBackgroundColor || "#ffffff",
|
||||||
|
"--style-pro-button-text": config?.buttonTextColor || "#374151",
|
||||||
|
"--style-pro-selected-button-bg":
|
||||||
|
config?.selectedButtonBackgroundColor || "#eff6ff",
|
||||||
|
"--style-pro-selected-button-text":
|
||||||
|
config?.selectedButtonTextColor || "#2563eb",
|
||||||
|
} as CSSProperties & Record<string, string>;
|
||||||
const renderPromptCategory = (
|
const renderPromptCategory = (
|
||||||
category: PublicStyleProCategory,
|
category: PublicStyleProCategory,
|
||||||
layout: "stacked" | "carousel" = "stacked"
|
layout: "stacked" | "carousel" = "stacked"
|
||||||
@ -1057,7 +1067,10 @@ export function StyleProWorkspace({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="style-pro-index-shell flex h-dvh w-full overflow-hidden bg-[#f4f4f4] text-[#171717]">
|
<div
|
||||||
|
className="style-pro-index-shell flex h-dvh w-full overflow-hidden bg-[#f4f4f4] text-[#171717]"
|
||||||
|
style={themeStyle}
|
||||||
|
>
|
||||||
<style>{`
|
<style>{`
|
||||||
.style-pro-index-shell .section-card {
|
.style-pro-index-shell .section-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -1066,27 +1079,27 @@ export function StyleProWorkspace({
|
|||||||
box-shadow: 0 12px 35px rgba(15, 23, 42, 0.05);
|
box-shadow: 0 12px 35px rgba(15, 23, 42, 0.05);
|
||||||
}
|
}
|
||||||
.style-pro-index-shell .subsection-title {
|
.style-pro-index-shell .subsection-title {
|
||||||
color: #2563eb;
|
color: var(--style-pro-title-color);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.style-pro-index-shell .option-btn {
|
.style-pro-index-shell .option-btn {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
background: #fff;
|
background: var(--style-pro-button-bg);
|
||||||
color: rgba(17, 24, 39, 0.78);
|
color: var(--style-pro-button-text);
|
||||||
transition: all 0.18s ease;
|
transition: all 0.18s ease;
|
||||||
}
|
}
|
||||||
.style-pro-index-shell .option-btn:hover {
|
.style-pro-index-shell .option-btn:hover {
|
||||||
background: #1c1c1e;
|
background: var(--style-pro-selected-button-bg);
|
||||||
border-color: #1c1c1e;
|
border-color: var(--style-pro-selected-button-text);
|
||||||
color: #fff;
|
color: var(--style-pro-selected-button-text);
|
||||||
}
|
}
|
||||||
.style-pro-index-shell .option-btn.active {
|
.style-pro-index-shell .option-btn.active {
|
||||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(37, 99, 235, 0.05));
|
background: var(--style-pro-selected-button-bg);
|
||||||
color: #2563eb;
|
color: var(--style-pro-selected-button-text);
|
||||||
border-color: #60a5fa;
|
border-color: var(--style-pro-selected-button-text);
|
||||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.12);
|
||||||
}
|
}
|
||||||
.style-pro-index-shell .prompt-carousel-scroll {
|
.style-pro-index-shell .prompt-carousel-scroll {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
|||||||
@ -32,6 +32,11 @@ export type StyleProConfig = {
|
|||||||
realisticSecondModelId: string;
|
realisticSecondModelId: string;
|
||||||
realisticFirstPrompt: string;
|
realisticFirstPrompt: string;
|
||||||
realisticSecondPrompt: string;
|
realisticSecondPrompt: string;
|
||||||
|
titleTextColor: string;
|
||||||
|
buttonBackgroundColor: string;
|
||||||
|
buttonTextColor: string;
|
||||||
|
selectedButtonBackgroundColor: string;
|
||||||
|
selectedButtonTextColor: string;
|
||||||
defaultPrompt: string;
|
defaultPrompt: string;
|
||||||
negativePrompt: string;
|
negativePrompt: string;
|
||||||
aspectRatio: string;
|
aspectRatio: string;
|
||||||
@ -436,6 +441,11 @@ export const defaultStyleProConfig: StyleProConfig = {
|
|||||||
realisticSecondModelId: "",
|
realisticSecondModelId: "",
|
||||||
realisticFirstPrompt: DEFAULT_REALISTIC_FIRST_PROMPT,
|
realisticFirstPrompt: DEFAULT_REALISTIC_FIRST_PROMPT,
|
||||||
realisticSecondPrompt: DEFAULT_REALISTIC_SECOND_PROMPT,
|
realisticSecondPrompt: DEFAULT_REALISTIC_SECOND_PROMPT,
|
||||||
|
titleTextColor: "#2563eb",
|
||||||
|
buttonBackgroundColor: "#ffffff",
|
||||||
|
buttonTextColor: "#374151",
|
||||||
|
selectedButtonBackgroundColor: "#eff6ff",
|
||||||
|
selectedButtonTextColor: "#2563eb",
|
||||||
defaultPrompt: DEFAULT_PROMPT,
|
defaultPrompt: DEFAULT_PROMPT,
|
||||||
negativePrompt: "低清晰度,畸变,文字乱码,主体变形,产品结构错误,多余物体,水印",
|
negativePrompt: "低清晰度,畸变,文字乱码,主体变形,产品结构错误,多余物体,水印",
|
||||||
aspectRatio: "auto",
|
aspectRatio: "auto",
|
||||||
@ -455,6 +465,11 @@ function cleanString(value: unknown, fallback = "", maxLength = 4000) {
|
|||||||
return (text || fallback).slice(0, maxLength);
|
return (text || fallback).slice(0, maxLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanHexColor(value: unknown, fallback: string) {
|
||||||
|
const text = cleanString(value, fallback, 32);
|
||||||
|
return /^#(?:[0-9a-fA-F]{3}){1,2}$/.test(text) ? text : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
function cleanId(value: unknown, fallback: string) {
|
function cleanId(value: unknown, fallback: string) {
|
||||||
const text = cleanString(value, fallback, 80)
|
const text = cleanString(value, fallback, 80)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
@ -563,6 +578,23 @@ export function normalizeStyleProConfig(value: unknown): StyleProConfig {
|
|||||||
fallback.realisticSecondPrompt,
|
fallback.realisticSecondPrompt,
|
||||||
8000
|
8000
|
||||||
),
|
),
|
||||||
|
titleTextColor: cleanHexColor(raw.titleTextColor, fallback.titleTextColor),
|
||||||
|
buttonBackgroundColor: cleanHexColor(
|
||||||
|
raw.buttonBackgroundColor,
|
||||||
|
fallback.buttonBackgroundColor
|
||||||
|
),
|
||||||
|
buttonTextColor: cleanHexColor(
|
||||||
|
raw.buttonTextColor,
|
||||||
|
fallback.buttonTextColor
|
||||||
|
),
|
||||||
|
selectedButtonBackgroundColor: cleanHexColor(
|
||||||
|
raw.selectedButtonBackgroundColor,
|
||||||
|
fallback.selectedButtonBackgroundColor
|
||||||
|
),
|
||||||
|
selectedButtonTextColor: cleanHexColor(
|
||||||
|
raw.selectedButtonTextColor,
|
||||||
|
fallback.selectedButtonTextColor
|
||||||
|
),
|
||||||
defaultPrompt: cleanString(raw.defaultPrompt, fallback.defaultPrompt, 8000),
|
defaultPrompt: cleanString(raw.defaultPrompt, fallback.defaultPrompt, 8000),
|
||||||
negativePrompt: cleanString(raw.negativePrompt, fallback.negativePrompt, 4000),
|
negativePrompt: cleanString(raw.negativePrompt, fallback.negativePrompt, 4000),
|
||||||
aspectRatio: cleanString(raw.aspectRatio, fallback.aspectRatio, 32),
|
aspectRatio: cleanString(raw.aspectRatio, fallback.aspectRatio, 32),
|
||||||
@ -637,6 +669,11 @@ export function toPublicStyleProConfig(
|
|||||||
aspectRatio: config.aspectRatio,
|
aspectRatio: config.aspectRatio,
|
||||||
imageSize: config.imageSize,
|
imageSize: config.imageSize,
|
||||||
promptDisplayMode: config.promptDisplayMode,
|
promptDisplayMode: config.promptDisplayMode,
|
||||||
|
titleTextColor: config.titleTextColor,
|
||||||
|
buttonBackgroundColor: config.buttonBackgroundColor,
|
||||||
|
buttonTextColor: config.buttonTextColor,
|
||||||
|
selectedButtonBackgroundColor: config.selectedButtonBackgroundColor,
|
||||||
|
selectedButtonTextColor: config.selectedButtonTextColor,
|
||||||
realisticModeAvailable: Boolean(
|
realisticModeAvailable: Boolean(
|
||||||
config.realisticFirstModelId && config.realisticSecondModelId
|
config.realisticFirstModelId && config.realisticSecondModelId
|
||||||
),
|
),
|
||||||
|
|||||||
@ -5,23 +5,8 @@ import {
|
|||||||
getImageCreditCostFromConfig,
|
getImageCreditCostFromConfig,
|
||||||
getImageModelCreditDiscountLabel,
|
getImageModelCreditDiscountLabel,
|
||||||
} from "@/lib/ai/image-model-pricing";
|
} from "@/lib/ai/image-model-pricing";
|
||||||
import { isImageGenerationModelDescriptor } from "@/lib/ai/model-classification";
|
|
||||||
import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
|
import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
|
||||||
|
|
||||||
const STYLE_PRO_SUPPORTED_PROVIDER_TYPES = new Set([
|
|
||||||
"gemini",
|
|
||||||
"grsai-gpt-image",
|
|
||||||
"alibaba-image",
|
|
||||||
"bfl-image",
|
|
||||||
"ruxa-image",
|
|
||||||
"fal-image",
|
|
||||||
"openai-image",
|
|
||||||
"stepfun-image",
|
|
||||||
"midjourney",
|
|
||||||
"runway",
|
|
||||||
"krea",
|
|
||||||
]);
|
|
||||||
|
|
||||||
export type StyleProModelEntry = Awaited<
|
export type StyleProModelEntry = Awaited<
|
||||||
ReturnType<typeof getEnabledAiModelsWithProvider>
|
ReturnType<typeof getEnabledAiModelsWithProvider>
|
||||||
>[number];
|
>[number];
|
||||||
@ -39,15 +24,7 @@ export type PublicStyleProModelOption = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function isStyleProImageModel(entry: StyleProModelEntry) {
|
export function isStyleProImageModel(entry: StyleProModelEntry) {
|
||||||
const { model, provider } = entry;
|
return Boolean(entry.model.supportsImageGen);
|
||||||
if (!model.supportsImageGen) return false;
|
|
||||||
if (!STYLE_PRO_SUPPORTED_PROVIDER_TYPES.has(provider.type)) return false;
|
|
||||||
return isImageGenerationModelDescriptor({
|
|
||||||
providerType: provider.type,
|
|
||||||
modelId: model.modelId,
|
|
||||||
capabilities: model.capabilities,
|
|
||||||
supportsImageGen: model.supportsImageGen,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStyleProImageModelEntries(options?: {
|
export async function getStyleProImageModelEntries(options?: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user