diff --git a/core/frontend/src/App.tsx b/core/frontend/src/App.tsx index 9213892..f231dcc 100644 --- a/core/frontend/src/App.tsx +++ b/core/frontend/src/App.tsx @@ -18,6 +18,7 @@ import type { User, UserPreference } from "./types"; +import { publicModelDisplayName } from "./model-display"; import { CornerMarks, Decorations, Sidebar, ToastLike } from "./components/app-shell"; import { AccountPage, @@ -996,7 +997,7 @@ export function App() { m.capability === "text" && m.status === "active")} loading={loading} navigate={navigate} diff --git a/core/frontend/src/model-display.ts b/core/frontend/src/model-display.ts new file mode 100644 index 0000000..891f579 --- /dev/null +++ b/core/frontend/src/model-display.ts @@ -0,0 +1,26 @@ +import type { ModelConfig } from "./types"; + +/** 普通用户界面的临时品牌映射。后续接入模型自动反馈/配置化前,只在此处维护。 */ +const PUBLIC_MODEL_NAME: Record = { + "gpt-image": "AirShelf Image", + "gpt-image-2": "AirShelf Image", + "gemini-3.1-pro-preview": "AirShelf Script" +}; + +export function publicModelRouteName(routeKey: string, fallback = routeKey) { + return PUBLIC_MODEL_NAME[routeKey] || fallback; +} + +export const imageModelPickerOptions = [ + { id: "volcano", label: publicModelRouteName("volcano", "火山 Seedream") }, + { id: "gpt-image", label: publicModelRouteName("gpt-image") } +]; + +/** 普通用户界面的动态模型下拉统一调用这里。 */ +export function publicModelDisplayName( + model: Pick | null | undefined, + fallback = "默认模型" +) { + if (!model) return fallback; + return publicModelRouteName(model.name, model.display_name?.trim() || model.name || fallback); +} diff --git a/core/frontend/src/routes/ai-tools.tsx b/core/frontend/src/routes/ai-tools.tsx index decb991..703a030 100644 --- a/core/frontend/src/routes/ai-tools.tsx +++ b/core/frontend/src/routes/ai-tools.tsx @@ -29,6 +29,7 @@ import { } from "lucide-react"; import type { AITask, Asset, ImageConversation, ImageConversationTask, ModelConfig, ModelEntity, Product, WorkbenchTask } from "../types"; import { api } from "../api"; +import { imageModelPickerOptions } from "../model-display"; import { ModelLibrary } from "../components/model-library"; import { SkeletonRows } from "../components/loading"; import { ConfirmModal, MediaLightbox } from "../components/overlays"; @@ -533,10 +534,6 @@ const MODEL_COUNT_OPTIONS = ["1", "2", "4"]; // 生图模型选择(写入 localStorage,下次进页面读回)。默认火山。 const GEN_MODEL_KEY = "airshelf:imgwb:gen_model"; -const GEN_MODEL_OPTIONS: { value: string; label: string }[] = [ - { value: "volcano", label: "火山 Seedream" }, - { value: "gpt-image", label: "gpt-image-2" }, -]; const COVER_COUNT_OPTIONS = ["4", "8", "12"]; /* 图片创作 · 空态提示词建议 chip(基线 image-optimize EXAMPLES) */ @@ -672,11 +669,12 @@ export function ImageWorkbenchPage({ const blockNonIntegerKey = (event: React.KeyboardEvent) => { if ([".", "+", "-", "e", "E"].includes(event.key)) event.preventDefault(); }; - // 生图模型选择:默认火山(内衣等敏感品类不被审核拦,还原也更好),可切 gpt-image-2。持久化到 localStorage。 + // 生图模型选择:保存后端路由键;展示名由共享映射统一提供。 const [genModel, setGenModel] = useState(() => { try { return localStorage.getItem(GEN_MODEL_KEY) || "volcano"; } catch { return "volcano"; } }); useEffect(() => { try { localStorage.setItem(GEN_MODEL_KEY, genModel); } catch { /* 忽略 */ } }, [genModel]); + const selectedImageModelLabel = imageModelPickerOptions.find((option) => option.id === genModel)?.label || imageModelPickerOptions[0].label; const [count, setCount] = useState(mode === "image" ? "4" : "4"); // 模特单选(长度恒 0/1);平台改回多选(P0③:可选多个平台,各出一组结果) const [pickedIds, setPickedIds] = useState([]); @@ -1651,8 +1649,8 @@ export function ImageWorkbenchPage({
o.value === genModel)?.label || "火山 Seedream"} - options={GEN_MODEL_OPTIONS.map((o) => ({ id: o.value, label: o.label }))} + value={selectedImageModelLabel} + options={imageModelPickerOptions} onSelect={setGenModel} /> o.value === genModel)?.label || "火山 Seedream"} - options={GEN_MODEL_OPTIONS.map((o) => ({ id: o.value, label: o.label }))} + value={selectedImageModelLabel} + options={imageModelPickerOptions} onSelect={setGenModel} />
diff --git a/core/frontend/src/routes/pipeline.tsx b/core/frontend/src/routes/pipeline.tsx index 452e92f..864b6d1 100644 --- a/core/frontend/src/routes/pipeline.tsx +++ b/core/frontend/src/routes/pipeline.tsx @@ -3,6 +3,7 @@ import type { ChangeEvent, CSSProperties, PointerEvent as ReactPointerEvent } fr import { Play } from "lucide-react"; import { api, ApiError } from "../api"; import type { Asset, BillingSummary, ExportPoll, ModelConfig, Product, Project, StoryboardShot, Team, TimelineSavePayload, User } from "../types"; +import { publicModelDisplayName } from "../model-display"; import type { Notice, Page } from "./route-config"; import { money, stageOrder, statusPill } from "./stage-config"; import { CornerMarks, Decorations, Sidebar, ToastLike } from "../components/app-shell"; @@ -1262,7 +1263,7 @@ export function PipelinePage(props: { // 模型选择小按钮(输入框下方)· 自建 restraint 下拉(幽灵触发 + popover 菜单),不再用原生 select + inline const [modelMenuOpen, setModelMenuOpen] = useState(false); const activeModel = textModels?.find((m) => m.id === activeScriptModelId); - const activeModelName = activeModel?.display_name || activeModel?.name || "默认模型"; + const activeModelName = publicModelDisplayName(activeModel); useEffect(() => { if (!modelMenuOpen) return; const close = (e: MouseEvent) => { if (!(e.target as HTMLElement).closest(".chat-model-pick")) setModelMenuOpen(false); }; @@ -2842,7 +2843,7 @@ export function PipelinePage(props: { {/* 模型选择小按钮(输入框下方,对齐 ChatGPT/Lovart)· 复用 restraint chip 下拉,向上展开 */} {textModels && textModels.length > 0 ? (
- @@ -2851,7 +2852,7 @@ export function PipelinePage(props: {
{ setScriptModelId(m.id); setModelMenuOpen(false); }} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setScriptModelId(m.id); setModelMenuOpen(false); } }}> - {m.display_name || m.name} + {publicModelDisplayName(m)}
))}