diff --git a/.memory/worklog.json b/.memory/worklog.json index 53699bd..9e410f3 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,19 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "f8cd466", - "message": "auto-save 2026-05-12 21:50 (~1)", - "ts": "2026-05-12T21:50:16+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "hash": "e6ef193", - "message": "auto-save 2026-05-12 21:55 (~1)", - "ts": "2026-05-12T21:56:09+08:00", - "type": "commit" - }, { "files_changed": 1, "hash": "1fc0ae9", @@ -3335,6 +3321,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 2 项未提交变更 · 最近提交:auto-save 2026-05-14 07:34 (~1)", "files_changed": 2 + }, + { + "ts": "2026-05-14T07:40:07+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 07:39 (~4)", + "hash": "c263af2", + "files_changed": 4 + }, + { + "ts": "2026-05-13T23:43:15Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 2 项未提交变更 · 最近提交:auto-save 2026-05-14 07:39 (~4)", + "files_changed": 2 } ] } diff --git a/docs/source-analysis.html b/docs/source-analysis.html index f7d3d27..fb2e876 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -625,7 +625,7 @@ api/main.py
你看到的区域关键帧素材审核面板
-
主要源码FrameLightbox;按“原图/清洗、主体资产、场景图、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,场景图页左侧显示全部关键帧并可勾选场景参考,产品融合页左侧改为 6 行镜头表:产品图、白底人物图、人物图上的产品区域、场景图和描述词一一对应;右侧承载当前镜头秒数、GPT Image 2 / Seedance 固定模型、AI 描述草稿、单条生成和批量排队。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立主体图;场景图依赖主体资产,右侧通过地点、生成方式、风格和参考要素拼出可编辑 prompt,再按当前关键帧生成去主体原场景、相似新场景或同构换风格。相关接口包括 cleanupFrameaddElementgenerateSubjectAssetsgenerateSceneAssetlistProductLibrarycopyProductLibraryAssetcreateProductFusionGuide
+
主要源码FrameLightbox;按“原图/清洗、主体资产、场景图、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,场景图页左侧显示全部关键帧并可勾选场景参考,产品融合页左侧改为 6 行镜头表:产品图、白底人物图、人物图上的产品区域、场景图和描述词一一对应;产品融合槽位的“粘贴”优先使用应用内 clipboard,也支持选中槽位后 Cmd+V 粘贴系统图片。右侧承载当前镜头秒数、GPT Image 2 / Seedance 固定模型、AI 描述草稿、单条生成和批量排队。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立主体图;场景图依赖主体资产,右侧通过地点、生成方式、风格和参考要素拼出可编辑 prompt,再按当前关键帧生成去主体原场景、相似新场景或同构换风格。相关接口包括 cleanupFrameaddElementgenerateSubjectAssetsgenerateSceneAssetlistProductLibrarycopyProductLibraryAssetcreateProductFusionGuidegenerateProductFusionDescriptions
适合怎么描述“这一组关键帧如何共同生成一个统一主体包;某张关键帧的水印、去主体场景图、产品融合镜头组和质量风险应该如何审核”。
@@ -765,6 +765,7 @@ SubjectAsset { 产品图库GET /product-library/skglistProductLibrary读取内置 SKG 白底图库 manifest,返回产品标题、品类、尺寸、白底评分和预览图 URL。 产品图入库到 jobPOST /jobs/{id}/assets/product-librarycopyProductLibraryAsset把一个内置产品图库条目复制为当前 job 的普通 asset,返回 ImageRef(kind="asset"),用于画面工作台产品融合和分镜产品参考组。 产品融合引导图POST /jobs/{id}/product-fusion/guidecreateProductFusionGuide读取产品图和白底人物图,按用户在人物图上画出的 product_region 合成一张位置引导图;前端固定显示图片模型为 GPT Image 2,返回普通 asset 作为 Seedance 首帧。 + 产品融合描述词POST /jobs/{id}/product-fusion/descriptionsgenerateProductFusionDescriptions为 6 行产品融合镜头生成动作描述草稿;有 LLM 配置时用 REWRITE_MODEL 生成 JSON,无配置或失败时回退到本地镜头模板。 分镜保存PUT /frames/{idx}/storyboardupdateStoryboard保存 4 图槽、时长和改造说明。 生图POST /frames/{idx}/generategenerateImage基于关键帧或已选生成图做 image-to-image,目前可用。 @@ -883,10 +884,22 @@ SubjectAsset {

问题:只把产品图作为参考图无法解决尺寸和位置融合,模型不知道产品应该放在人物或场景里的哪个区域。

改动:FrameLightbox 的“产品融合”页改为 6 行镜头组。每行绑定产品图、白底人物图、手动画出的产品区域、场景图、描述词和视频秒数;图片槽支持上传和粘贴,产品图也可从内置 SKG 白底图库选用。右侧固定显示图片模型 GPT Image 2 和视频模型 Seedance,支持 AI 草拟 6 条动作描述、单条生成和批量排队。

-

后端:新增 POST /jobs/{job_id}/product-fusion/guide。它把产品图按 product_region 合成到白底人物图上,生成普通 asset 引导图;前端再把该引导图作为 Seedance 首帧,并把产品图、人物图、场景图作为参考图提交。

+

后端:新增 POST /jobs/{job_id}/product-fusion/guidePOST /jobs/{job_id}/product-fusion/descriptions。前者把产品图按 product_region 合成到白底人物图上,生成普通 asset 引导图;后者用 LLM 或本地模板生成 6 条动作描述草稿。前端再把引导图作为 Seedance 首帧,并把产品图、人物图、场景图作为参考图提交。

影响:api/main.pyweb/lib/api.tsweb/app/page.tsxweb/components/lightbox.tsxweb/components/nodes/index.tsxweb/components/dashboard.tsxdocs/source-analysis.html

+
+
+

2026-05-14 · 产品融合槽位接入应用内剪贴板

+ FrameLightbox + Clipboard +
+
+

问题:卡片上的“复制”写入的是应用内 ImageRef 剪贴板,而产品融合槽位只监听系统剪贴板文件,导致复制后的素材无法在产品融合里粘贴。

+

改动:NodeDataFrameLightbox 新增 clipboard 传递链路;产品融合三类槽位的“粘贴”按钮优先使用应用内剪贴板,应用剪贴板为空时再提示可选中槽位后 Cmd+V 粘贴系统图片。

+

影响:web/app/page.tsxweb/components/nodes/index.tsxweb/components/dashboard.tsxweb/components/lightbox.tsxdocs/source-analysis.html

+
+

2026-05-14 · 增加产品融合和 SKG 内置白底图库

diff --git a/web/app/page.tsx b/web/app/page.tsx index 76f6910..5a6b641 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -650,11 +650,12 @@ export default function Home() { if (typeof idx === "number") setStoryboardFrame(idx) setWorkbenchOpen(true) }, + clipboard, onCopyImage: handleCopyImage, onGenerateProductFusionVideo: handleGenerateProductFusionVideo, pinnedNodes, onToggleNodePin: handleToggleNodePin, - }), [job, jobs, activeJobId, submitting, analyzing, frameTargets, frameCounts, frameQualities, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleUpload, handleAnalyze, handleAnalyzeJob, handleFrameTargetChange, handleFrameCountChange, handleFrameQualityChange, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, setJob, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, handleCopyImage, handleGenerateProductFusionVideo, pinnedNodes, handleToggleNodePin]) + }), [job, jobs, activeJobId, submitting, analyzing, frameTargets, frameCounts, frameQualities, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleUpload, handleAnalyze, handleAnalyzeJob, handleFrameTargetChange, handleFrameCountChange, handleFrameQualityChange, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, setJob, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, clipboard, handleCopyImage, handleGenerateProductFusionVideo, pinnedNodes, handleToggleNodePin]) // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag) const savedSizes = useMemo(() => loadNodeSizes(), []) diff --git a/web/components/dashboard.tsx b/web/components/dashboard.tsx index 279a76a..853aaca 100644 --- a/web/components/dashboard.tsx +++ b/web/components/dashboard.tsx @@ -327,6 +327,7 @@ export const Dashboard = forwardRef(function Dashboard({ data.onCloseExpandedFrame() setExpanded(new Set([key])) }} + clipboard={data.clipboard} onCopyImage={data.onCopyImage} onGenerateProductFusionVideo={data.onGenerateProductFusionVideo} /> diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index bd5d5fb..45150df 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -21,6 +21,7 @@ interface Props { onToggleSelect: (idx: number) => void onJobUpdate?: (job: Job) => void onSwitchPanel?: (key: string) => void + clipboard?: ImageRef | null onCopyImage?: (ref: ImageRef) => void onGenerateProductFusionVideo?: (frameIdx: number, shot: ProductFusionShot) => Promise | void embedded?: boolean @@ -131,7 +132,7 @@ const normalizeFusionShots = (shots?: ProductFusionShot[] | null): ProductFusion return base.map((item, i) => ({ ...item, ...(shots[i] ?? {}), id: shots[i]?.id || item.id })) } -export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, onChange, onToggleSelect, onJobUpdate, onSwitchPanel, onCopyImage, onGenerateProductFusionVideo, embedded = false }: Props) { +export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, onChange, onToggleSelect, onJobUpdate, onSwitchPanel, clipboard, onCopyImage, onGenerateProductFusionVideo, embedded = false }: Props) { const [describing, setDescribing] = useState(false) const [cleaningFrameIds, setCleaningFrameIds] = useState>(new Set()) const [batchCleaning, setBatchCleaning] = useState(false) @@ -982,10 +983,18 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index e5d243d..80cbaac 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -72,6 +72,7 @@ export interface NodeData { onDeleteCutout?: (frameIdx: number, elementId: string, cutoutId: string) => void // 删元素提取图 onOpenStoryboard?: (frameIdx: number) => void // 打开分镜头编排专属面板 onOpenWorkbench?: (frameIdx?: number) => void // 展开顶部分镜编排内嵌面板 + clipboard?: ImageRef | null onCopyImage?: (ref: ImageRef) => void // 复制图片到全局剪贴板(粘贴到分镜头编排插槽) onGenerateProductFusionVideo?: (frameIdx: number, shot: ProductFusionShot) => Promise | void pinnedNodes?: Set // 已钉住的节点 id 集合 — 钉住后位置 + 尺寸锁定 @@ -1957,6 +1958,7 @@ export function KeyframePanelNode({ data }: any) { onChange={d.onExpandFrame} onToggleSelect={d.onToggleFrame} onJobUpdate={d.onJobUpdate} + clipboard={d.clipboard} onCopyImage={d.onCopyImage} onGenerateProductFusionVideo={d.onGenerateProductFusionVideo} />