fix: move generation confirm into composer
This commit is contained in:
2
RULES.md
2
RULES.md
@@ -11,7 +11,7 @@
|
||||
- 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解
|
||||
- 风格:`04-Dark-Gallery-Ambient`(路径:`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md`)
|
||||
- 第一冲刺:步骤 1-4(下载 / 拆轨 / 关键帧 / ASR+翻译)
|
||||
- 当前产品方向(2026-05-20 再确认):信息流广告快速复刻默认进入“三字段候选生成”工作流。主界面为“左侧素材输入列 + 右侧信息流复刻工作表”;工作台以 1800x1000 为基准操作画布,不同显示器或浏览器宽度下保持同一框架,并按常见桌面宽度落到预设缩放档位(0.72/0.76/0.8/0.86/0.92/1/1.06/1.16/1.24/1.34/1.48/1.6),保留适度左右呼吸感,缩放后高度小于视口时上下居中,必要时允许纵向滚动,不通过 `xl/2xl` 断点重排核心操作区。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动两条路:音频文案路提取原音频文案/字幕,并分析讲话人、语速节奏、背景音乐/环境声/音效;视频视觉路自动抽取参考帧。源视频工作区主体链路改为“上方参考帧池 + 转换层、下方主体元素结果栏”:参考帧池竖向排列;转换层是轻量对话式生图确认区,参考图可通过左侧缩略图 `+`、参考帧拖拽、胶片拖拽或本地图片拖入进入转换层,用户选择 GPT/Gemini 套件后先分析参考图;识别结果里的特征 chip 只作为“保留元素”本地选择,点亮=保留、再点取消,点击不立即请求模型,随下一条发送消息提交;用户再在下方发送区发送复刻/创新/卡通和画面要求,界面只保留生成要求输入框、张数控件和待确认入口,不展示当前要求摘要、保留元素副本、收起记录计数或重复模型确认话术,生成数量通过发送区旁边的张数控件控制;后端返回英文出图 prompt 后必须弹窗确认,用户点确认才生成对应数量的统一多角度套图。主体元素结果栏在转换层下方横向展示套图输出、文件夹分组、单张重生、删除和 hover 预览,空态只保留紧凑提示,不再挤占右侧整列。旧下方“相似主体 / 主体模板库”不再作为主路径。波形下方的画面胶片只是临时预览,点击只跳转原视频时间点,双击或拖进参考帧池才正式加入关键帧,已加入的胶片直接显示“已添加”。产品图上传后独立形成产品资产包,自动识别视角/结构/比例并补缺角度。分镜工作台按逐句时间轴默认只露“文案 / 场景一句话 / 人物+产品+动作”,产品素材池、批量控制、三字段、视频候选和高级区都必须可折叠;视频候选无内容时默认不占大面积,有候选时默认只显示迷你缩略条,展开后才显示 4-grid。单条默认生成 4 个视频候选,顶部支持整片批量生成候选;首尾帧、视觉规划、产品出现方式和旧 6 字段保留在“高级”抽屉与后端 quick-plan 自动展开中,不能再作为客户默认闸门。
|
||||
- 当前产品方向(2026-05-20 再确认):信息流广告快速复刻默认进入“三字段候选生成”工作流。主界面为“左侧素材输入列 + 右侧信息流复刻工作表”;工作台以 1800x1000 为基准操作画布,不同显示器或浏览器宽度下保持同一框架,并按常见桌面宽度落到预设缩放档位(0.72/0.76/0.8/0.86/0.92/1/1.06/1.16/1.24/1.34/1.48/1.6),保留适度左右呼吸感,缩放后高度小于视口时上下居中,必要时允许纵向滚动,不通过 `xl/2xl` 断点重排核心操作区。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动两条路:音频文案路提取原音频文案/字幕,并分析讲话人、语速节奏、背景音乐/环境声/音效;视频视觉路自动抽取参考帧。源视频工作区主体链路改为“上方参考帧池 + 转换层、下方主体元素结果栏”:参考帧池竖向排列;转换层是轻量对话式生图确认区,参考图可通过左侧缩略图 `+`、参考帧拖拽、胶片拖拽或本地图片拖入进入转换层,用户选择 GPT/Gemini 套件后先分析参考图;识别结果里的特征 chip 只作为“保留元素”本地选择,点亮=保留、再点取消,点击不立即请求模型,随下一条发送消息提交;用户再在下方发送区发送复刻/创新/卡通和画面要求,界面只保留生成要求输入框、张数控件和提示词就绪状态,不展示当前要求摘要、保留元素副本、收起记录计数或重复模型确认话术,生成数量通过发送区旁边的张数控件控制;后端返回英文出图 prompt 后不再自动弹窗,发送区主按钮直接切换为“确认生成 N 张”,用户点击才生成对应数量的统一多角度套图。主体元素结果栏在转换层下方横向展示套图输出、文件夹分组、单张重生、删除和 hover 预览,空态只保留紧凑提示,不再挤占右侧整列。旧下方“相似主体 / 主体模板库”不再作为主路径。波形下方的画面胶片只是临时预览,点击只跳转原视频时间点,双击或拖进参考帧池才正式加入关键帧,已加入的胶片直接显示“已添加”。产品图上传后独立形成产品资产包,自动识别视角/结构/比例并补缺角度。分镜工作台按逐句时间轴默认只露“文案 / 场景一句话 / 人物+产品+动作”,产品素材池、批量控制、三字段、视频候选和高级区都必须可折叠;视频候选无内容时默认不占大面积,有候选时默认只显示迷你缩略条,展开后才显示 4-grid。单条默认生成 4 个视频候选,顶部支持整片批量生成候选;首尾帧、视觉规划、产品出现方式和旧 6 字段保留在“高级”抽屉与后端 quick-plan 自动展开中,不能再作为客户默认闸门。
|
||||
|
||||
## 部署事实
|
||||
- 平台:VPS `76.13.31.179`(Ubuntu 24.04 / Docker Compose / Coolify Traefik)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3404,7 +3404,6 @@ function SourceSubjectPipeline({
|
||||
const [agentSelectedTraits, setAgentSelectedTraits] = useState<string[]>(() => job.subject_agent?.selected_traits ?? [])
|
||||
const [agentInput, setAgentInput] = useState("")
|
||||
const [subjectAgentBusy, setSubjectAgentBusy] = useState<"analyze" | "message" | null>(null)
|
||||
const [promptConfirmOpen, setPromptConfirmOpen] = useState(false)
|
||||
const [promptMemoryByMode, setPromptMemoryByMode] = useState<Record<SubjectReconstructionMode, string[]>>(() => loadSubjectPromptMemory(job.id))
|
||||
const [cartoonStyle] = useState<CartoonReconstructionStyle>("3d_animation")
|
||||
const [subjectBusyFor, setSubjectBusyFor] = useState<{ jobId: string; jobLabel: string; mode: SubjectReconstructionMode; viewCount: number; sourceCount: number; profileLabel: string; modelLabel: string } | null>(null)
|
||||
@@ -3508,7 +3507,6 @@ function SourceSubjectPipeline({
|
||||
setAgentSelectedTraits(job.subject_agent?.selected_traits ?? [])
|
||||
setAgentInput("")
|
||||
setSubjectAgentBusy(null)
|
||||
setPromptConfirmOpen(false)
|
||||
setPromptMemoryByMode(loadSubjectPromptMemory(job.id))
|
||||
setLastSubjectProfile(null)
|
||||
setSubjectBusyFor(null)
|
||||
@@ -3911,7 +3909,7 @@ function SourceSubjectPipeline({
|
||||
setAgentSelectedTraits(nextAgent.selected_traits ?? [])
|
||||
}
|
||||
setAgentInput("")
|
||||
setPromptConfirmOpen(true)
|
||||
toast.success("出图提示词已生成,可以直接确认生成。")
|
||||
} catch (e) {
|
||||
toast.error("生图要求更新失败:" + (e instanceof Error ? e.message : String(e)))
|
||||
} finally {
|
||||
@@ -3935,12 +3933,17 @@ function SourceSubjectPipeline({
|
||||
const effectiveAgentViews = subjectViewsForQuantity(effectiveAgentQuantity)
|
||||
const effectivePrompt = (agentPrompt || subjectAgent?.generation_prompt_en || "").trim()
|
||||
const effectiveRequirement = (subjectAgent?.requirements_zh || agentRequirement).trim()
|
||||
const agentInputText = agentInput.trim()
|
||||
const agentInputConfirmsGeneration = /^(确认)?(生成|出图|开始生成|确认出图)(\s*\d+\s*张?)?$/.test(agentInputText)
|
||||
const canGenerateAgentPack = effectiveAgentMode === "custom"
|
||||
? Boolean(effectiveRequirement || agentReferenceFrames.length)
|
||||
: agentReferenceFrames.length > 0
|
||||
const agentModeRunning = runningActorModes.has(effectiveAgentMode)
|
||||
const showInlineGenerateAction = Boolean(effectivePrompt) && (!agentInputText || agentInputConfirmsGeneration) && !agentSelectedTraitsDirty
|
||||
const sendDisabled = !!subjectAgentBusy || (!agentInputText && !agentRequirement.trim() && !selectedAgentTraits.length && !agentSelectedTraitsDirty)
|
||||
const generateDisabled = !canGenerateAgentPack || subjectBusy || agentModeRunning
|
||||
const confirmSubjectGeneration = () => {
|
||||
setPromptConfirmOpen(false)
|
||||
setAgentInput("")
|
||||
void generateSubjectPack(effectiveAgentMode, agentReferenceFrameIndices, effectiveAgentViews)
|
||||
}
|
||||
|
||||
@@ -4206,15 +4209,10 @@ function SourceSubjectPipeline({
|
||||
生成要求
|
||||
</span>
|
||||
{effectivePrompt ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPromptConfirmOpen(true)}
|
||||
disabled={!canGenerateAgentPack || subjectBusy || agentModeRunning}
|
||||
className="inline-flex h-6 items-center gap-1 rounded-md border border-[#d6b36a]/28 bg-[#d6b36a]/[0.075] px-2 text-[9px] font-semibold text-[#f4dc88] transition hover:border-[#d6b36a]/48 hover:bg-[#d6b36a]/12 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
<span className="inline-flex h-6 items-center gap-1 rounded-md border border-[#d6b36a]/24 bg-[#d6b36a]/[0.07] px-2 text-[9px] font-semibold text-[#f4dc88]">
|
||||
<Check className="h-3 w-3" />
|
||||
待确认 · {effectiveAgentViews.length} 张
|
||||
</button>
|
||||
提示词就绪 · {effectiveAgentViews.length} 张
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-[9px] text-white/34">
|
||||
{reconstructionModeConfig(effectiveAgentMode).label} · {effectiveAgentQuantity} 张
|
||||
@@ -4226,7 +4224,7 @@ function SourceSubjectPipeline({
|
||||
value={agentInput}
|
||||
onChange={(event) => setAgentInput(event.target.value)}
|
||||
placeholder="直接写要怎么生成,或补充要改什么。"
|
||||
className="h-28 w-full resize-none rounded border border-transparent bg-transparent px-2 py-2 text-[11px] leading-relaxed text-white outline-none transition placeholder:text-white/24 focus:border-cyan-200/45"
|
||||
className="h-40 w-full resize-none rounded border border-transparent bg-transparent px-2 py-2 text-[11px] leading-relaxed text-white outline-none transition placeholder:text-white/24 focus:border-cyan-200/45"
|
||||
/>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<div className="flex h-10 shrink-0 items-center overflow-hidden rounded-md border border-white/10 bg-black/35">
|
||||
@@ -4269,12 +4267,18 @@ function SourceSubjectPipeline({
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void sendSubjectAgentRequirement()}
|
||||
disabled={!!subjectAgentBusy || (!agentInput.trim() && !agentRequirement.trim() && !selectedAgentTraits.length && !agentSelectedTraitsDirty)}
|
||||
onClick={showInlineGenerateAction ? confirmSubjectGeneration : () => void sendSubjectAgentRequirement()}
|
||||
disabled={showInlineGenerateAction ? generateDisabled : sendDisabled}
|
||||
className="skg-primary-action inline-flex h-10 flex-1 items-center justify-center gap-2 px-3 text-[11.5px] font-semibold transition disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{subjectAgentBusy === "message" ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||
发送消息
|
||||
{subjectAgentBusy === "message" ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : showInlineGenerateAction ? (
|
||||
<Sparkles className="h-4 w-4" />
|
||||
) : (
|
||||
<Send className="h-4 w-4" />
|
||||
)}
|
||||
{showInlineGenerateAction ? `确认生成 ${effectiveAgentViews.length} 张` : "发送消息"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4395,60 +4399,6 @@ function SourceSubjectPipeline({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{promptConfirmOpen && effectivePrompt && typeof document !== "undefined" ? createPortal(
|
||||
<div className="fixed inset-0 z-[10020] flex items-center justify-center bg-black/72 p-4 backdrop-blur-sm">
|
||||
<div className="w-full max-w-2xl rounded-xl border border-white/14 bg-[#11140f] p-4 shadow-[0_28px_90px_rgba(0,0,0,0.72)]">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-white">确认出图提示词</div>
|
||||
<div className="mt-1 text-[11px] text-white/46">
|
||||
{subjectModelLabel(subjectModelBundle)} · {reconstructionModeConfig(effectiveAgentMode).label} · {effectiveAgentViews.length} 张
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPromptConfirmOpen(false)}
|
||||
className="rounded-md border border-white/10 bg-black/30 px-2 py-1 text-[11px] text-white/56 transition hover:border-white/24 hover:text-white"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
{effectiveRequirement ? (
|
||||
<div className="mt-3 rounded-md border border-white/10 bg-black/24 px-3 py-2">
|
||||
<div className="text-[10px] font-semibold text-white/54">用户要求</div>
|
||||
<p className="mt-1 text-[11px] leading-relaxed text-white/72">{effectiveRequirement}</p>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-3 rounded-md border border-[#d6b36a]/24 bg-[#d6b36a]/[0.07] px-3 py-2">
|
||||
<div className="mb-1 text-[10px] font-semibold text-[#f4dc88]">最终英文提示词</div>
|
||||
<textarea
|
||||
readOnly
|
||||
value={effectivePrompt}
|
||||
className="h-44 w-full resize-none rounded-md border border-white/10 bg-black/30 px-2 py-2 font-mono text-[11px] leading-relaxed text-white/76 outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 flex items-center justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPromptConfirmOpen(false)}
|
||||
className="skg-secondary-action inline-flex h-9 items-center justify-center px-4 text-[12px] font-semibold"
|
||||
>
|
||||
先不生成
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={confirmSubjectGeneration}
|
||||
disabled={!canGenerateAgentPack || subjectBusy || agentModeRunning}
|
||||
className="skg-primary-action inline-flex h-9 items-center justify-center gap-1.5 px-4 text-[12px] font-semibold transition disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
<Sparkles className="h-4 w-4" />
|
||||
确定生成 {effectiveAgentViews.length} 张
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user