fix: clear subject conversion panel
This commit is contained in:
@@ -3769,7 +3769,7 @@ function SourceSubjectPipeline({
|
||||
>
|
||||
<div className="mb-1 flex items-center justify-between gap-2 text-[9.5px] text-white/30">
|
||||
<span>{frames.length} 张</span>
|
||||
<span>{filmstripDragging ? "松手加入" : "拖到转换层"}</span>
|
||||
<span>{filmstripDragging ? "松手加入" : "点击选择"}</span>
|
||||
</div>
|
||||
<div className="flex max-h-[410px] flex-col gap-1 overflow-y-auto pr-0.5 2xl:max-h-[500px]">
|
||||
{frames.map((frame, index) => {
|
||||
@@ -3777,11 +3777,6 @@ function SourceSubjectPipeline({
|
||||
return (
|
||||
<div
|
||||
key={frame.index}
|
||||
draggable
|
||||
onDragStart={(event) => {
|
||||
event.dataTransfer.setData(SOURCE_KEYFRAME_DRAG_TYPE, String(frame.index))
|
||||
event.dataTransfer.effectAllowed = "copy"
|
||||
}}
|
||||
className="relative"
|
||||
>
|
||||
<MediaAssetTile
|
||||
@@ -3795,11 +3790,11 @@ function SourceSubjectPipeline({
|
||||
previewPlacement="left"
|
||||
previewMaxWidth={320}
|
||||
previewClassName="p-2"
|
||||
selected={selected || allConversionFrameIndices.has(frame.index)}
|
||||
title={`${selected ? "已选 · 点击取消" : "点击选择"} · 拖到转换层生成主体套图`}
|
||||
selected={selected}
|
||||
title={selected ? "已选 · 点击取消" : "点击选择"}
|
||||
onClick={() => onToggleFrame(frame.index)}
|
||||
topLeft={<span className="rounded bg-black/72 px-1 font-mono text-[9px] text-white/70">{String(index + 1).padStart(2, "0")}</span>}
|
||||
topRight={<span className="rounded-full bg-black/72 p-0.5">{allConversionFrameIndices.has(frame.index) ? <Sparkles className="h-3 w-3 text-[#f5d98e]" /> : selected ? <Check className="h-3 w-3 text-emerald-200" /> : <Circle className="h-3 w-3 text-white/50" />}</span>}
|
||||
topRight={<span className="rounded-full bg-black/72 p-0.5">{selected ? <Check className="h-3 w-3 text-emerald-200" /> : <Circle className="h-3 w-3 text-white/50" />}</span>}
|
||||
onDelete={onDeleteFrame ? () => onDeleteFrame(frame.index) : undefined}
|
||||
deleting={deletingFrame === frame.index}
|
||||
deleteLabel={`删除参考帧 ${index + 1}`}
|
||||
@@ -3819,206 +3814,9 @@ function SourceSubjectPipeline({
|
||||
<div className="min-w-0">
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<SectionTitle icon={<Wand2 className="h-4 w-4" />} title="转换层" />
|
||||
<span className="rounded-full border border-cyan-200/20 bg-cyan-300/[0.08] px-2 py-0.5 text-[9.5px] text-cyan-50/70">
|
||||
{subjectModelBundleConfig(subjectModelBundle).detail}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={`max-h-[520px] min-h-[410px] overflow-y-auto rounded-md border p-2 transition 2xl:max-h-[600px] 2xl:min-h-[500px] ${
|
||||
agentDropActive ? "border-[#d6b36a]/80 bg-[#d6b36a]/10 ring-1 ring-[#d6b36a]/45" : "border-white/10 bg-black/32"
|
||||
}`}
|
||||
onDragEnter={(event) => {
|
||||
if (!Array.from(event.dataTransfer.types).includes(SOURCE_KEYFRAME_DRAG_TYPE)) return
|
||||
event.preventDefault()
|
||||
setAgentDropActive(true)
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
if (!Array.from(event.dataTransfer.types).includes(SOURCE_KEYFRAME_DRAG_TYPE)) return
|
||||
event.preventDefault()
|
||||
event.dataTransfer.dropEffect = "copy"
|
||||
}}
|
||||
onDragLeave={(event) => {
|
||||
const next = event.relatedTarget as Node | null
|
||||
if (next && event.currentTarget.contains(next)) return
|
||||
setAgentDropActive(false)
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault()
|
||||
setAgentDropActive(false)
|
||||
const frameIndex = Number(event.dataTransfer.getData(SOURCE_KEYFRAME_DRAG_TYPE))
|
||||
const frame = frames.find((item) => item.index === frameIndex)
|
||||
if (frame) addAgentReferenceFrame(frame)
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{SUBJECT_MODEL_BUNDLE_OPTIONS.map((option) => (
|
||||
<button
|
||||
key={option.value}
|
||||
type="button"
|
||||
onClick={() => setSubjectModelBundle(option.value)}
|
||||
className={`min-h-9 rounded-md border px-2 py-1 text-left transition ${
|
||||
subjectModelBundle === option.value
|
||||
? "border-[#d6b36a]/70 bg-[#d6b36a]/16 text-white"
|
||||
: "border-white/10 bg-black/28 text-white/50 hover:border-white/22 hover:text-white"
|
||||
}`}
|
||||
title={option.detail}
|
||||
>
|
||||
<span className="block text-[10.5px] font-semibold">{option.label}</span>
|
||||
<span className="mt-0.5 block truncate text-[8.5px] text-white/36">{option.detail}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 rounded-md border border-white/10 bg-black/24 p-2">
|
||||
<div className="mb-1.5 flex items-center justify-between gap-2">
|
||||
<span className="text-[10px] font-semibold text-white/62">参考图</span>
|
||||
<span className="rounded-full border border-white/10 bg-black/35 px-1.5 py-0.5 font-mono text-[9px] text-white/42">
|
||||
{agentReferenceFrames.length}/{RECONSTRUCTION_FRAME_LIMIT}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex min-h-[64px] items-center gap-1.5 overflow-x-auto pb-0.5">
|
||||
{agentReferenceFrames.map((frame, index) => (
|
||||
<div key={frame.index} className="relative shrink-0">
|
||||
<MediaAssetTile
|
||||
src={effectiveFrameUrl(job.id, frame)}
|
||||
alt={`转换层参考 ${index + 1}`}
|
||||
label={String(index + 1).padStart(2, "0")}
|
||||
meta={`${frame.timestamp.toFixed(1)}s`}
|
||||
className="aspect-[9/16] w-[42px] 2xl:w-[46px]"
|
||||
objectFit="contain"
|
||||
disablePreview
|
||||
topLeft={<span className="rounded bg-black/72 px-0.5 font-mono text-[8px] text-white/70">{String(index + 1).padStart(2, "0")}</span>}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeAgentReferenceFrame(frame.index)}
|
||||
className="absolute -right-1 -top-1 z-20 inline-flex h-4 w-4 items-center justify-center rounded-full border border-rose-100/35 bg-black/82 text-rose-100 transition hover:border-rose-100/70 hover:bg-rose-500/25"
|
||||
aria-label="移出转换层参考"
|
||||
title="移出转换层参考"
|
||||
>
|
||||
<Trash2 className="h-2.5 w-2.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{!agentReferenceFrames.length ? (
|
||||
<div className="flex min-h-[54px] flex-1 items-center justify-center rounded border border-dashed border-white/12 px-2 text-center text-[10px] leading-snug text-white/34">
|
||||
把左侧参考帧拖进来,再开始分析。
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void runSubjectAgentAnalyze()}
|
||||
disabled={subjectAgentBusy === "analyze" || !agentReferenceFrames.length}
|
||||
className="mt-2 inline-flex h-8 w-full items-center justify-center gap-1 rounded-md border border-cyan-200/25 bg-cyan-300/[0.08] px-3 text-[10.5px] font-semibold text-cyan-50/78 transition hover:border-cyan-200/45 hover:bg-cyan-300/[0.12] disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{subjectAgentBusy === "analyze" ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <MessageSquare className="h-3.5 w-3.5" />}
|
||||
开始分析
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 rounded-md border border-[#d6b36a]/25 bg-[#d6b36a]/[0.065] px-2.5 py-2">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<span className="rounded-full border border-[#d6b36a]/35 bg-black/28 px-2 py-0.5 text-[9.5px] font-semibold text-[#f4dd98]">
|
||||
方向:{reconstructionModeConfig(agentMode).label}
|
||||
</span>
|
||||
<span className="rounded-full border border-white/10 bg-black/28 px-2 py-0.5 text-[9.5px] font-semibold text-white/58">
|
||||
数量:{selectedSubjectViews.length} 张
|
||||
</span>
|
||||
<span className="rounded-full border border-white/10 bg-black/28 px-2 py-0.5 text-[9.5px] text-white/42">
|
||||
对话自动识别
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1.5 text-[9.5px] leading-snug text-white/42">
|
||||
在下方直接说“形象锁定 / 卡通重构 / 创意复刻 / 自主描述”和“生成几张”,系统会更新这里,不再单独点卡片或数量。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{agentAnalysis ? (
|
||||
<div className="mt-2 rounded-md border border-cyan-200/18 bg-cyan-300/[0.055] p-2 text-[10px] leading-snug text-cyan-50/72">
|
||||
<div className="mb-1 font-semibold text-cyan-50">AI 分析</div>
|
||||
<div>{agentAnalysis.summary_zh}</div>
|
||||
{agentAnalysis.questions.length ? (
|
||||
<div className="mt-1.5 text-cyan-50/56">{agentAnalysis.questions[0]}</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2 rounded-md border border-white/10 bg-black/24 px-2.5 py-2 text-[10px] leading-snug text-white/42">
|
||||
对话只处理主体生图需求:参考分析、保留/删除元素、数量、风格、服装统一和生成指令。
|
||||
</div>
|
||||
)}
|
||||
|
||||
{agentTraits.length ? (
|
||||
<div className="mt-2 flex flex-wrap gap-1">
|
||||
{agentTraits.map((trait) => {
|
||||
const selected = selectedAgentTraits.includes(trait)
|
||||
return (
|
||||
<button
|
||||
key={trait}
|
||||
type="button"
|
||||
onClick={() => toggleSubjectAgentTrait(trait)}
|
||||
className={`h-6 rounded-full border px-2 text-[9.5px] transition ${
|
||||
selected
|
||||
? "border-[#d6b36a]/70 bg-[#d6b36a]/16 text-white"
|
||||
: "border-white/10 bg-black/28 text-white/52 hover:border-[#d6b36a]/50 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{trait}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="mt-2 max-h-32 space-y-1.5 overflow-y-auto rounded-md border border-white/10 bg-black/24 p-2">
|
||||
{agentMessages.length ? agentMessages.slice(-6).map((message, index) => (
|
||||
<div key={`${message.created_at}-${index}`} className={`rounded px-2 py-1.5 text-[10px] leading-snug ${
|
||||
message.role === "user" ? "ml-6 bg-white/[0.07] text-white/70" : "mr-6 bg-cyan-300/[0.07] text-cyan-50/68"
|
||||
}`}>
|
||||
{message.content}
|
||||
</div>
|
||||
)) : (
|
||||
<div className="flex h-16 items-center justify-center text-center text-[10px] leading-snug text-white/32">
|
||||
分析后这里会保留当前项目的生图对话。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
value={agentInput}
|
||||
onChange={(event) => {
|
||||
setAgentInput(event.target.value)
|
||||
setAgentRequirement(event.target.value || job.subject_agent?.requirements_zh || "")
|
||||
}}
|
||||
placeholder="例如:保留透明骨骼男孩和蓝色头带,人物占画面 90%,服装每张完全一致,生成 6 张"
|
||||
rows={2}
|
||||
className="mt-2 min-h-[56px] w-full resize-none rounded-md border border-white/10 bg-black/35 px-2.5 py-2 text-[10.5px] leading-snug text-white outline-none placeholder:text-white/28 focus:border-cyan-300/50"
|
||||
/>
|
||||
<div className="mt-2 grid grid-cols-[1fr_auto] gap-1.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void sendSubjectAgentRequirement()}
|
||||
disabled={subjectAgentBusy === "message"}
|
||||
className="inline-flex h-8 items-center justify-center gap-1 rounded-md border border-white/10 bg-white/[0.06] px-3 text-[10.5px] font-semibold text-white/68 transition hover:border-white/25 hover:text-white disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{subjectAgentBusy === "message" ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Send className="h-3.5 w-3.5" />}
|
||||
更新要求
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void generateSubjectPack(agentMode)}
|
||||
disabled={subjectBusy || agentModeRunning || !canGenerateAgentPack}
|
||||
className="skg-primary-action inline-flex h-8 items-center justify-center gap-1 px-3 text-[10.5px] font-semibold transition disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{subjectBusyFor?.mode === agentMode || agentModeRunning ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Sparkles className="h-3.5 w-3.5" />}
|
||||
生成 {selectedSubjectViews.length} 张
|
||||
</button>
|
||||
</div>
|
||||
{lastSubjectProfile ? (
|
||||
<div className="mt-2 rounded border border-cyan-200/16 bg-cyan-300/[0.055] px-2 py-1.5 text-[9.5px] leading-snug text-cyan-50/62">
|
||||
上次锁定人设:{lastSubjectProfile.summary}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex min-h-[410px] items-center justify-center rounded-md border border-dashed border-white/12 bg-black/18 p-4 2xl:min-h-[500px]">
|
||||
<span className="text-[10.5px] text-white/28">转换层待重构</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user