From 7797de4438ad70d32f3001daf26e0c8e9bcc909a Mon Sep 17 00:00:00 2001 From: kang Date: Thu, 14 May 2026 13:43:52 +0800 Subject: [PATCH] auto-save 2026-05-14 13:43 (~4) --- .memory/worklog.json | 26 +++++++++++++------------- api/main.py | 4 +++- web/app/page.tsx | 35 +++++++++++++++++++++++++++++------ web/components/lightbox.tsx | 5 +++-- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 4f3d137..390f4c3 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,18 +1,5 @@ { "entries": [ - { - "files_changed": 5, - "hash": "e9b25be", - "message": "auto-save 2026-05-13 09:14 (+3, ~2)", - "ts": "2026-05-13T09:15:03+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "message": "启动 Claude 接力会话 · 已载入 Claude 最近会话,等待下一条指令 · 分支 HEAD · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 09:14 (+3, ~2)", - "ts": "2026-05-13T01:17:33Z", - "type": "assistant-session" - }, { "files_changed": 2, "hash": "0599cd8", @@ -3276,6 +3263,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 13:32 (~4)", "files_changed": 1 + }, + { + "ts": "2026-05-14T13:38:22+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 13:38 (~1)", + "hash": "346591b", + "files_changed": 1 + }, + { + "ts": "2026-05-14T05:38:40Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 13:38 (~1)", + "files_changed": 1 } ] } diff --git a/api/main.py b/api/main.py index 3f8a770..6d1fef3 100644 --- a/api/main.py +++ b/api/main.py @@ -3701,7 +3701,9 @@ def generate_storyboard_video(job_id: str, idx: int, req: GenerateStoryboardVide subject_ref_paths = [p for p in (storyboard_ref_path(job_id, r) for r in req.subject_images[:8]) if p] reference_ref_paths = [] seen_ref_paths: set[str] = {str(ref_path)} - for p in [*subject_ref_paths, *product_ref_paths]: + # Product fusion is sensitive to object drift. Send product references before + # extra character references so the rigid SKG device keeps its real shape. + for p in [*product_ref_paths, *subject_ref_paths]: key = str(p) if key not in seen_ref_paths: reference_ref_paths.append(p) diff --git a/web/app/page.tsx b/web/app/page.tsx index b1f5776..1cece28 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -51,6 +51,29 @@ const FRAME_QUALITY_LABELS: Record = { ultra: "极准", } +const PRODUCT_FUSION_WEARING_PROMPT = [ + "Product placement must be physically correct:", + "The SKG device is a rigid opaque white U-shaped neck massager, not a soft scarf, necklace, cable, collar, sticker, implant, or transparent body part.", + "It must stay OUTSIDE the transparent skin shell. It should rest on the external surface around the back of the neck and upper shoulders, like a wearable collar-shaped device.", + "The curved bridge sits behind the neck. The two open arms come forward along the left and right sides of the neck, above the collarbones and near the upper trapezius.", + "The inner metal massage pads face the back/side of the neck. The outer glossy plastic shell remains visible and opaque.", + "Hands may hold the two ends while putting it on, then release or lightly press the side control button. Hands must not hide the device silhouette.", + "The product should occlude the transparent skin where it is in front, cast a small contact shadow, and keep realistic perspective and scale.", + "Keep a tiny readable separation between product edge and skeleton/skin whenever needed so it never looks embedded.", +].join("\n") + +const PRODUCT_FUSION_PRODUCT_IDENTITY_PROMPT = [ + "Product identity is strict:", + "The four SKG product reference images are the single source of truth for the object. Preserve the same white U-shaped body, rounded arms, inner massage pads/nodes, side buttons, seams, glossy plastic material, thickness, proportions, and viewing angles.", + "Do not redesign, stylize, simplify, melt, inflate, shrink, recolor, add logos/text/screens/wires/extra parts, or turn it into a generic neckband/headphone/medical brace.", + "If the product and character conflict, prioritize preserving the product shape and place it externally on the neck rather than merging it into the character.", +].join("\n") + +const PRODUCT_FUSION_NEGATIVE_PROMPT = [ + "Hard negatives for product fusion:", + "no product passing through the neck, no product inside the transparent body, no x-ray blending, no transparent product, no product becoming bones or skin, no product fused with spine/ribs/throat, no clipping through shoulders, no floating device, no melted device, no deformed U-shape, no wrong body part, no necklace/scarf/headphones/brace, no random replacement product.", +].join("\n") + // 合并 input + download + split 为一个节点 // 分叉:上路 input → visual lab ↘ // 下路 input → audio ──────────────────────────→ compose @@ -540,16 +563,16 @@ export default function Home() { `产品角度图 2:${labelOf(productRefs[1], "SKG 产品侧面/斜侧视角")}。`, `产品角度图 3:${labelOf(productRefs[2], "SKG 产品背面/细节视角")}。`, `产品角度图 4:${labelOf(productRefs[3], "SKG 产品补充/底部或佩戴视角")}。`, - "产品使用部位:这是颈部/肩颈按摩仪,只能自然佩戴或贴合在脖子、后颈、颈肩交界处;不要放到手臂、腰、腿、胸口、眼部或背景里。", - "比例尺寸:产品应符合真实颈部按摩仪大小,U 形结构环绕后颈但不能巨大化、缩小成饰品、嵌入身体、悬浮或穿透透明人体。", - "镜头语言:严格按描述里的出场方式、场景、景别、运镜、产品进入方式、佩戴贴合动作、使用过程和收尾方式执行。", + PRODUCT_FUSION_PRODUCT_IDENTITY_PROMPT, + PRODUCT_FUSION_WEARING_PROMPT, + "Camera direction: follow the scene description, but always stage the product as a separate external wearable object. Show the hands placing or adjusting the device on the outside of the neck; do not imply the product is merged with the transparent body.", `场景/使用/享受描述:${shot.action_text.trim()}`, TRANSPARENT_HUMAN_VIDEO_PROMPT, - "融合要求:产品必须自然出现在透明骨架人动作中,尺寸可信,透视一致,只贴合手部拿取和后颈/颈肩使用区域,不能悬浮、漂移、融化、扭曲或变成其他物体。", + "Fusion rule: this is product placement, not body fusion. The product is an opaque physical device worn outside the body with believable contact shadow, occlusion, scale, and perspective.", "连续性:镜头必须完整连贯,中间不要跳切,不换角色,不换产品,不突然改变场景。", - "产品一致性:严格保持 SKG 产品外观、颜色、材质、U 形结构、按摩触点、按键和比例;四张产品角度图是产品身份真源。", "场景要求:背景、空间、光线和阴影要自然统一,不要出现水印、平台 UI、字幕或竞品包装。", "商业质感:真实拍摄感、干净高级、产品清楚可辨、人物动作自然、镜头稳定。", + PRODUCT_FUSION_NEGATIVE_PROMPT, "禁止:文字、水印、随机品牌、非 SKG 产品、医学治疗承诺、夸张病症、恐怖元素、产品位置漂移、透明衣服但非透明身体。", TRANSPARENT_HUMAN_NEGATIVE_PROMPT, ].join("\n") @@ -559,7 +582,7 @@ export default function Home() { first_image: null, last_image: null, product_images: productRefs, - subject_image: primarySubject, + subject_image: null, subject_images: subjectRefs, scene_image: null, product_image: productRefs[0] ?? null, diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index 1375781..9342de5 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -6,10 +6,11 @@ import { frameUrl, cleanedFrameUrl, apiAssetUrl, describeFrame, cleanupFrame, applyCleanedFrame, discardCleanedFrame, addElement, updateElement, deleteElement, generateSceneAsset, generateSubjectAssets, updateStoryboard, copyProductLibraryAsset, - listCharacterLibrary, copyCharacterLibraryAssets, characterLibraryImageUrl, - type AssetBackground, type AssetSize, type CharacterLibraryItem, type KeyFrame, type Job, type ImageRef, type ProductFusionShot, type SceneAssetRole, type SceneStyle, type SubjectKind, + listCharacterLibrary, copyCharacterLibraryAssets, characterLibraryImageUrl, listProductLibrary, + type AssetBackground, type AssetSize, type CharacterLibraryItem, type KeyFrame, type Job, type ImageRef, type ProductFusionShot, type ProductLibraryItem, type SceneAssetRole, type SceneStyle, type SubjectKind, } from "@/lib/api" import { TRANSPARENT_HUMAN_FRAME_STANDARD, TRANSPARENT_HUMAN_UI_SUMMARY } from "@/lib/workflow-target" +import { HoverPreview } from "@/components/nodes/hover-preview" import { toast } from "sonner" interface Props {