diff --git a/.memory/worklog.json b/.memory/worklog.json index f8bc027..72de503 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,32 +1,5 @@ { "entries": [ - { - "files_changed": 3, - "hash": "de3cef4", - "message": "auto-save 2026-05-15 11:51 (~3)", - "ts": "2026-05-15T11:51:33+08:00", - "type": "commit" - }, - { - "files_changed": 2, - "message": "Codex 会话活跃 · 最近命令:codex · 2 项未提交变更 · 最近提交:auto-save 2026-05-15 11:51 (~3)", - "ts": "2026-05-15T03:54:44Z", - "type": "session-heartbeat" - }, - { - "files_changed": 2, - "hash": "d98f518", - "message": "auto-save 2026-05-15 11:56 (~2)", - "ts": "2026-05-15T11:57:04+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "hash": "5676c9a", - "message": "auto-save 2026-05-15 12:02 (~1)", - "ts": "2026-05-15T12:02:34+08:00", - "type": "commit" - }, { "files_changed": 1, "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-15 12:02 (~1)", @@ -3264,6 +3237,32 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-17 19:37 (~4)", "files_changed": 1 + }, + { + "ts": "2026-05-17T19:48:24+08:00", + "type": "commit", + "message": "auto-save 2026-05-17 19:48 (~4)", + "hash": "9cfb633", + "files_changed": 4 + }, + { + "ts": "2026-05-17T11:48:28Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 1 项未提交变更 · 最近提交:auto-save 2026-05-17 19:48 (~4)", + "files_changed": 1 + }, + { + "ts": "2026-05-17T19:53:03+08:00", + "type": "commit", + "message": "feat: standardize product asset inputs", + "hash": "fc48499", + "files_changed": 3 + }, + { + "ts": "2026-05-17T11:58:29Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 3 项未提交变更 · 最近提交:feat: standardize product asset inputs", + "files_changed": 3 } ] } diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 2cb40b7..4e9b9af 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -589,7 +589,7 @@ web/next.config.mjsNext.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator,并移除 Next 16 已不支持的 eslint 顶层配置,避免本地 dev 出现配置 Issue 提示。 web/app/globals.css全局主题变量、登录页视觉样式、ReactFlow 样式引用,以及本地开发态 nextjs-portal 遮挡隐藏规则。 web/app/page.tsx产品工作台主状态:jobs、activeJobId、生成任务状态;主渲染为全屏素材输入列 + 信息流广告复刻工作表;“开始”编排状态只负责在下载完成后自动触发 triggerTranscribe,不再默认触发抽帧、Vision 扫描或分镜初稿保存;底部吸附音频条不再从主界面渲染。 - web/components/ad-recreation-board.tsx信息流广告复刻工作表:左侧素材输入;右侧展示视频下载状态、默认折叠的音频文案依据,以及统一的音频解析结果面板;面板顶部是一行讲话人/节奏/背景音摘要,下方左侧为原视频播放器、右侧为逐句时间轴,底部横向音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后自动识别正面/左右 45 度/厚度/内侧触点/背底等视角,并标注背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停可放大预览;缺视角补图失败时保留重试入口。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入、参考帧/关键元素和 6 个候选视频槽。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后复用现有生视频接口提交 Seedance 候选。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 + web/components/ad-recreation-board.tsx信息流广告复刻工作表:左侧素材输入;右侧展示视频下载状态、默认折叠的音频文案依据,以及统一的音频解析结果面板;面板顶部是一行讲话人/节奏/背景音摘要,下方左侧为原视频播放器、右侧为逐句时间轴,底部横向音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后自动识别正面/左右 45 度/厚度/内侧触点/背底等视角,并标注背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入、参考帧/关键元素和 6 个候选视频槽。单条生成会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图,不会把全部产品图提交给生视频模型,然后复用现有生视频接口提交 Seedance 候选。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 web/app/login/page.tsx生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。 web/app/login/layout.tsx登录路由专属 layout:覆盖全站默认网页标题和描述为空,避免 /login 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。 web/components/login/oasis-canvas.tsx登录页全屏动态视觉层:用 iframe 直接承载下载包 web/public/oasis-source/index.html 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 postMessage 转发给 iframe,避免登录面板或输入框遮挡时草地失去鼠标响应。 @@ -964,6 +964,18 @@ SubjectAsset {

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-17 · 产品图悬停预览改为大尺寸顶层浮层

+ UI + Workflow +
+
+

问题:产品素材池底部图片悬停时,放大预览仍在滚动容器内部,容易被产品区或分镜框架裁掉;预览尺寸也偏小,不利于检查肩颈产品厚度、触点和左右差异。

+

改动:ProductReferenceCard 将悬停预览从卡片内绝对定位改为 createPortal(document.body) 渲染的固定浮层,宽度提升到最多 380px,并根据鼠标位置自动避开视口底部和右侧。

+

影响:web/components/ad-recreation-board.tsxdocs/source-analysis.html。后续产品图检查应继续使用该顶层预览,避免把预览重新放回带 overflow 的滚动容器里。

+
+

2026-05-17 · 产品图上传统一生成 AI 工作副本

diff --git a/web/components/ad-recreation-board.tsx b/web/components/ad-recreation-board.tsx index c222ffe..e7330f1 100644 --- a/web/components/ad-recreation-board.tsx +++ b/web/components/ad-recreation-board.tsx @@ -1,6 +1,7 @@ "use client" -import { type ReactNode, type RefObject, useEffect, useMemo, useRef, useState } from "react" +import { type MouseEvent as ReactMouseEvent, type ReactNode, type RefObject, useEffect, useMemo, useRef, useState } from "react" +import { createPortal } from "react-dom" import { AlertTriangle, Check, ChevronDown, Circle, Film, FileText, Image as ImageIcon, Link2, Loader2, Mic, Package, PanelRight, Play, Plus, Scissors, Sparkles, Trash2, Upload, Wand2, @@ -1515,20 +1516,55 @@ function ProductReferenceCard({ const tagLabels = item.useTags.map((tag) => PRODUCT_USE_TAG_LABELS[tag]).filter(Boolean) const assetWarnings = item.assetMeta?.warnings ?? [] const assetActions = item.assetMeta?.actions ?? [] - return ( -
-
- {productViewLabel(item.view)} -
- -
+ const [previewPos, setPreviewPos] = useState<{ left: number; top: number } | null>(null) + + function updatePreviewPosition(event: ReactMouseEvent) { + const margin = 16 + const previewWidth = Math.min(380, window.innerWidth - margin * 2) + const previewHeight = previewWidth + 118 + let left = event.clientX + 18 + let top = event.clientY + 18 + if (left + previewWidth > window.innerWidth - margin) { + left = event.clientX - previewWidth - 18 + } + if (top + previewHeight > window.innerHeight - margin) { + top = window.innerHeight - previewHeight - margin + } + setPreviewPos({ + left: Math.max(margin, left), + top: Math.max(margin, top), + }) + } + + const preview = previewPos && typeof document !== "undefined" + ? createPortal( +
+ +
{productViewLabel(item.view)} · {productBackgroundLabel(item.background)} · {tagLabels.join(" / ")}
{item.note} {item.risk ? <>
风险:{item.risk} : null} {assetWarnings.length ? <>
规格:{assetWarnings.join(";")} : null}
-
+
, + document.body, + ) + : null + + return ( +
+
setPreviewPos(null)} + > + {productViewLabel(item.view)} + {preview} {item.source === "ai" ? "AI" : "图"}