import { useEffect, useMemo, useState, type ReactNode } from "react"; import { App, Button, Input, Segmented, Select } from "antd"; import { AppModal } from "@/components/ui/product/app-modal"; import { Braces, Image, Layers3, Save, Sparkles } from "lucide-react"; import { StyleAssetBindingModal } from "@/components/canvas/style-asset-binding-modal"; import { createStyleProfileSnapshot, styleProfileValidationMessage, type StyleProfileSnapshot } from "@/lib/canvas/style-profile"; type EditorSection = "identity" | "prompt" | "execution"; type StyleProfileEditorModalProps = { open: boolean; initialProfile: StyleProfileSnapshot | null; saving?: boolean; onClose: () => void; onSave: (profile: StyleProfileSnapshot, applyToProject: boolean) => void; }; export function StyleProfileEditorModal({ open, initialProfile, saving = false, onClose, onSave }: StyleProfileEditorModalProps) { const { message, modal } = App.useApp(); const [section, setSection] = useState("identity"); const [draft, setDraft] = useState(initialProfile); const [baseline, setBaseline] = useState(""); const [assetsOpen, setAssetsOpen] = useState(false); useEffect(() => { if (!open) return; const next = initialProfile ? createStyleProfileSnapshot(initialProfile) : null; setDraft(next); setBaseline(next ? JSON.stringify(next) : ""); setSection("identity"); setAssetsOpen(false); }, [initialProfile, open]); const validationMessage = useMemo(() => draft ? styleProfileValidationMessage(draft) : "风格草稿不存在", [draft]); if (!draft) return null; const update = (patch: Partial) => setDraft((current) => current ? { ...current, ...patch } : current); const requestClose = () => { if (JSON.stringify(draft) === baseline) { onClose(); return; } modal.confirm({ title: "放弃未保存的风格修改?", content: "名称、Prompt、封面和执行配置中的改动都会丢失。", okText: "放弃修改", cancelText: "继续编辑", okButtonProps: { danger: true }, onOk: onClose }); }; const submit = (applyToProject: boolean) => { if (validationMessage) { message.error(validationMessage); return; } onSave(createStyleProfileSnapshot({ ...draft, source: "user" }), applyToProject); }; return ( <>

风格编辑器

{draft.title || "未命名风格"} · 我的风格

{section === "identity" ? : null} {section === "prompt" ? : null} {section === "execution" ? setAssetsOpen(true)} /> : null}

{validationMessage || "保存后进入“我的风格”;应用时会把当前版本复制为项目快照"}

setAssetsOpen(false)} onApply={(profile) => { setDraft(profile); setAssetsOpen(false); }} /> ); } function StylePreview({ profile }: { profile: StyleProfileSnapshot }) { return ( ); } function IdentityFields({ profile, onChange }: EditorFieldsProps) { const selection = profile.selection || {}; const setSelection = (key: string, value: string) => onChange({ selection: { ...selection, [key]: value } }); return (
onChange({ title: event.target.value })} /> onChange({ description: event.target.value })} /> onChange({ coverUrl: event.target.value })} /> setSelection("world", event.target.value)} /> setSelection("medium", event.target.value)} /> setSelection("tone", event.target.value)} /> setSelection("character", event.target.value)} />
); } function PromptFields({ profile, onChange }: EditorFieldsProps) { return (
onChange({ prompt: event.target.value })} /> onChange({ negativePrompt: event.target.value })} />
); } function ExecutionFields({ profile, onChange, onEditAssets }: EditorFieldsProps & { onEditAssets: () => void }) { const enabled = profile.assets.filter((asset) => asset.enabled !== false); return (
onChange({ executionPolicy: executionPolicy as StyleProfileSnapshot["executionPolicy"] })} />

执行资产

{profile.assets.length} 个已绑定,{enabled.length} 个已启用

{profile.assets.length ?
{profile.assets.map((asset) =>
{asset.title}{asset.kind.toUpperCase()} · {asset.status === "validated" ? "已验证" : asset.status === "unavailable" ? "不可用" : "待验证"}
)}
:
尚未绑定执行资产,当前风格将只通过 Prompt 执行
}

LoRA 与参考图已支持绑定、验证和兼容判断;具体生成渠道没有原生适配器时,兼容策略会降级到 Prompt,严格策略会阻止生成。

); } type EditorFieldsProps = { profile: StyleProfileSnapshot; onChange: (patch: Partial) => void }; function SectionHeading({ title, description }: { title: string; description: string }) { return

{title}

{description}

; } function Field({ label, className = "", children }: { label: string; className?: string; children: ReactNode }) { return ; } function Metric({ label, value }: { label: string; value: string }) { return
{label}
{value}
; } function EditorTab({ id, panelId, active, icon, label, onClick }: { id: string; panelId: string; active: boolean; icon: ReactNode; label: string; onClick: () => void }) { return ( ); }