auto-save 2026-05-14 02:02 (+3, ~5)
This commit is contained in:
@@ -2830,6 +2830,25 @@
|
||||
"type": "session-heartbeat",
|
||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 01:51 (~2)",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-14T01:57:34+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-14 01:57 (+1, ~4)",
|
||||
"hash": "11de581",
|
||||
"files_changed": 5
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T17:58:48Z",
|
||||
"type": "session-heartbeat",
|
||||
"message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 01:57 (+1, ~4)",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T18:03:11Z",
|
||||
"type": "session-heartbeat",
|
||||
"message": "Claude 会话活跃 · 最近命令:claude · 5 项未提交变更 · 最近提交:auto-save 2026-05-14 01:57 (+1, ~4)",
|
||||
"files_changed": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
320
.playwright-mcp/page-2026-05-13T17-57-36-343Z.yml
Normal file
320
.playwright-mcp/page-2026-05-13T17-57-36-343Z.yml
Normal file
@@ -0,0 +1,320 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e36] [cursor=pointer]:
|
||||
- button "Open Next.js Dev Tools" [ref=e37]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]:
|
||||
- button "Open issues overlay" [ref=e44]:
|
||||
- generic [ref=e45]:
|
||||
- generic [ref=e46]: "0"
|
||||
- generic [ref=e47]: "1"
|
||||
- generic [ref=e48]: Issue
|
||||
- button "Collapse issues badge" [ref=e49]:
|
||||
- img [ref=e50]
|
||||
- main [ref=e53]:
|
||||
- button "自动排版 · 保留每个节点的尺寸,重新排好间距和列布局" [ref=e55]:
|
||||
- img [ref=e56]
|
||||
- button "切到明亮主题" [ref=e62]:
|
||||
- img [ref=e63]
|
||||
- generic [ref=e69]:
|
||||
- generic [ref=e72]:
|
||||
- generic [ref=e73]:
|
||||
- img [ref=e74]
|
||||
- generic [ref=e79]: 分镜头编排
|
||||
- generic [ref=e80]: 0 分镜 · 0 元素
|
||||
- generic [ref=e81]: · 组织分镜画面 → 为生成视频做准备
|
||||
- button "展开编排" [disabled] [ref=e83]:
|
||||
- img [ref=e84]
|
||||
- text: 展开编排
|
||||
- application [ref=e87]:
|
||||
- generic [ref=e89]:
|
||||
- generic:
|
||||
- generic:
|
||||
- img:
|
||||
- group "Edge from input to keyframe" [ref=e90] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from input to asr" [ref=e93] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from asr to translate" [ref=e96] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from translate to rewrite" [ref=e99] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from keyframe to storyboard" [ref=e102] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from storyboard to videogen" [ref=e105] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from videogen to compose" [ref=e108] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from rewrite to compose" [ref=e111] [cursor=pointer]
|
||||
- generic:
|
||||
- group [ref=e114]:
|
||||
- generic [ref=e115]:
|
||||
- generic [ref=e116]:
|
||||
- button "再上传一个视频" [ref=e117]:
|
||||
- img [ref=e118]
|
||||
- button "64.5s" [ref=e120]:
|
||||
- generic [ref=e122]: 64.5s
|
||||
- button "72.4s" [ref=e124]:
|
||||
- generic [ref=e126]: 72.4s
|
||||
- button "64.5s" [ref=e128]:
|
||||
- generic [ref=e130]: 64.5s
|
||||
- button "71.4s" [ref=e132]:
|
||||
- generic [ref=e134]: 71.4s
|
||||
- button "72.4s" [ref=e136]:
|
||||
- generic [ref=e138]: 72.4s
|
||||
- button "71.4s" [ref=e140]:
|
||||
- generic [ref=e142]: 71.4s
|
||||
- button "71.4s" [ref=e144]:
|
||||
- generic [ref=e146]: 71.4s
|
||||
- button "71.4s" [ref=e148]:
|
||||
- generic [ref=e150]: 71.4s
|
||||
- button "71.4s" [ref=e152]:
|
||||
- generic [ref=e154]: 71.4s
|
||||
- button "71.4s" [ref=e156]:
|
||||
- generic [ref=e158]: 71.4s
|
||||
- button "8.0s" [ref=e160]:
|
||||
- generic [ref=e162]: 8.0s
|
||||
- button "8.0s" [ref=e164]:
|
||||
- generic [ref=e166]: 8.0s
|
||||
- button "8.0s" [ref=e168]:
|
||||
- generic [ref=e170]: 8.0s
|
||||
- button "8.0s" [ref=e172]:
|
||||
- generic [ref=e174]: 8.0s
|
||||
- button "…" [ref=e176]:
|
||||
- img [ref=e178]
|
||||
- generic [ref=e180]: …
|
||||
- button "…" [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e186]: …
|
||||
- button "…" [ref=e188]:
|
||||
- img [ref=e190]
|
||||
- generic [ref=e192]: …
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 1080×1920
|
||||
- generic: 64.5s
|
||||
- generic [ref=e193]:
|
||||
- generic [ref=e194]:
|
||||
- img [ref=e196]
|
||||
- generic [ref=e199]: 输入 · Input
|
||||
- generic [ref=e200]:
|
||||
- img [ref=e201]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e205]:
|
||||
- img [ref=e206]
|
||||
- generic [ref=e209]:
|
||||
- generic [ref=e210]: STEP 1 · 视频就绪 · 完成
|
||||
- textbox "再加一个 TK 链接" [ref=e211]
|
||||
- generic [ref=e212]:
|
||||
- button "+ 加链接" [disabled] [ref=e213]
|
||||
- button "再传一个" [ref=e214]:
|
||||
- img [ref=e215]
|
||||
- text: 再传一个
|
||||
- generic [ref=e218]:
|
||||
- generic [ref=e219]: 1080×1920 · 64.5s
|
||||
- generic [ref=e220]: 🔗 链接
|
||||
- button "重新解析" [ref=e221]
|
||||
- generic "拖动调整宽度" [ref=e223]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e224]
|
||||
- group [ref=e225]:
|
||||
- generic [ref=e226]:
|
||||
- generic [ref=e227]:
|
||||
- generic [ref=e228]:
|
||||
- button "frame 9 1.7s" [ref=e229]:
|
||||
- img "frame 9" [ref=e230]
|
||||
- generic [ref=e231]: 1.7s
|
||||
- button "📋" [ref=e232]
|
||||
- button "删除该关键帧" [ref=e233]:
|
||||
- img [ref=e234]
|
||||
- generic [ref=e237]:
|
||||
- button "frame 0 ✨ 24.7s" [ref=e238]:
|
||||
- img "frame 0" [ref=e239]
|
||||
- generic "已清洗" [ref=e241]: ✨
|
||||
- generic [ref=e242]: 24.7s
|
||||
- button "📋" [ref=e243]
|
||||
- button "删除该关键帧" [ref=e244]:
|
||||
- img [ref=e245]
|
||||
- generic [ref=e248]:
|
||||
- button "frame 1 33.6s" [ref=e249]:
|
||||
- img "frame 1" [ref=e250]
|
||||
- generic [ref=e251]: 33.6s
|
||||
- button "📋" [ref=e252]
|
||||
- button "删除该关键帧" [ref=e253]:
|
||||
- img [ref=e254]
|
||||
- generic [ref=e257]:
|
||||
- button "frame 2 37.7s" [ref=e258]:
|
||||
- img "frame 2" [ref=e259]
|
||||
- generic [ref=e260]: 37.7s
|
||||
- button "📋" [ref=e261]
|
||||
- button "删除该关键帧" [ref=e262]:
|
||||
- img [ref=e263]
|
||||
- generic [ref=e266]:
|
||||
- button "frame 3 39.4s" [ref=e267]:
|
||||
- img "frame 3" [ref=e268]
|
||||
- generic [ref=e269]: 39.4s
|
||||
- button "📋" [ref=e270]
|
||||
- button "删除该关键帧" [ref=e271]:
|
||||
- img [ref=e272]
|
||||
- generic [ref=e275]:
|
||||
- button "frame 4 1 43.1s" [ref=e276]:
|
||||
- img "frame 4" [ref=e277]
|
||||
- generic "1 个元素已抠图" [ref=e279]: "1"
|
||||
- generic [ref=e280]: 43.1s
|
||||
- button "📋" [ref=e281]
|
||||
- button "删除该关键帧" [ref=e282]:
|
||||
- img [ref=e283]
|
||||
- generic [ref=e286]:
|
||||
- button "frame 5 45.0s" [ref=e287]:
|
||||
- img "frame 5" [ref=e288]
|
||||
- generic [ref=e289]: 45.0s
|
||||
- button "📋" [ref=e290]
|
||||
- button "删除该关键帧" [ref=e291]:
|
||||
- img [ref=e292]
|
||||
- generic [ref=e295]:
|
||||
- button "frame 6 53.6s" [ref=e296]:
|
||||
- img "frame 6" [ref=e297]
|
||||
- generic [ref=e298]: 53.6s
|
||||
- button "📋" [ref=e299]
|
||||
- button "删除该关键帧" [ref=e300]:
|
||||
- img [ref=e301]
|
||||
- generic [ref=e304]:
|
||||
- button "frame 7 56.0s" [ref=e305]:
|
||||
- img "frame 7" [ref=e306]
|
||||
- generic [ref=e307]: 56.0s
|
||||
- button "📋" [ref=e308]
|
||||
- button "删除该关键帧" [ref=e309]:
|
||||
- img [ref=e310]
|
||||
- generic [ref=e313]:
|
||||
- button "frame 8 58.4s" [ref=e314]:
|
||||
- img "frame 8" [ref=e315]
|
||||
- generic [ref=e316]: 58.4s
|
||||
- button "📋" [ref=e317]
|
||||
- button "删除该关键帧" [ref=e318]:
|
||||
- img [ref=e319]
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e324]:
|
||||
- img [ref=e326]
|
||||
- generic [ref=e330]: 镜头拆解 · 元素提取
|
||||
- generic [ref=e331]:
|
||||
- img [ref=e332]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e336]:
|
||||
- img [ref=e337]
|
||||
- generic [ref=e340]:
|
||||
- generic [ref=e341]: STEP 2 · 0/10 入编排 · 完成
|
||||
- generic [ref=e342]:
|
||||
- text: 自动 10 张 ·
|
||||
- generic [ref=e343]: 1 已清洗
|
||||
- text: ·
|
||||
- generic [ref=e344]: 1/2 已抠图
|
||||
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
|
||||
- generic "拖动调整宽度" [ref=e346]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e347]
|
||||
- group [ref=e348]:
|
||||
- generic [ref=e349]:
|
||||
- generic [ref=e351]:
|
||||
- img [ref=e353]
|
||||
- generic [ref=e356]: 声音文案 · ASR
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e359]:
|
||||
- img [ref=e360]
|
||||
- generic [ref=e363]:
|
||||
- generic [ref=e364]: STEP 3 · 可选文案轨 · 待运行
|
||||
- generic [ref=e365]: Gemini 2.5 · 英文带时间戳分段
|
||||
- generic "拖动调整宽度" [ref=e367]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e368]
|
||||
- group [ref=e369]:
|
||||
- generic [ref=e370]:
|
||||
- generic [ref=e372]:
|
||||
- img [ref=e374]
|
||||
- generic [ref=e378]: 翻译理解 · Translate
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e381]:
|
||||
- img [ref=e382]
|
||||
- generic [ref=e385]:
|
||||
- generic [ref=e386]: STEP 4 · EN → ZH · 待运行
|
||||
- generic [ref=e387]: 中文翻译 · 段落级 · 实时输出
|
||||
- generic "拖动调整宽度" [ref=e389]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e390]
|
||||
- group [ref=e391]:
|
||||
- generic [ref=e392]:
|
||||
- generic [ref=e394]:
|
||||
- button "透明骷髅" [ref=e395]:
|
||||
- img "透明骷髅" [ref=e396]
|
||||
- button "📋" [ref=e397]
|
||||
- button "删除该提取图" [ref=e398]:
|
||||
- img [ref=e399]
|
||||
- generic [ref=e402]:
|
||||
- generic [ref=e404]:
|
||||
- img [ref=e406]
|
||||
- generic [ref=e411]: 元素改造 · Storyboard
|
||||
- generic [ref=e412]:
|
||||
- img [ref=e413]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e417]:
|
||||
- img [ref=e418]
|
||||
- generic [ref=e421]:
|
||||
- generic [ref=e422]: STEP 6 · 参考元素 → SKG 画面 · 完成
|
||||
- generic [ref=e423]:
|
||||
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
|
||||
- generic [ref=e424]: 已有 1 个提取元素 · 0 个分镜进入编排
|
||||
- button "进入分镜编排" [disabled] [ref=e425]
|
||||
- generic "拖动调整宽度" [ref=e427]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e428]
|
||||
- group [ref=e429]:
|
||||
- generic [ref=e430]:
|
||||
- generic [ref=e432]:
|
||||
- img [ref=e434]
|
||||
- generic [ref=e438]: 产品文案 · Rewrite
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e441]:
|
||||
- img [ref=e442]
|
||||
- generic [ref=e445]:
|
||||
- generic [ref=e446]: STEP 5 · 接 SKG 卖点 · 待运行
|
||||
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e447]
|
||||
- generic [ref=e448]: 下一冲刺接入
|
||||
- generic "拖动调整宽度" [ref=e450]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e451]
|
||||
- group [ref=e452]:
|
||||
- generic [ref=e454]:
|
||||
- generic [ref=e456]:
|
||||
- img [ref=e458]
|
||||
- generic [ref=e460]: 生成视频 · Video Gen
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e463]:
|
||||
- img [ref=e464]
|
||||
- generic [ref=e467]:
|
||||
- generic [ref=e468]: STEP 7 · 首帧 + 动作 prompt · 待运行
|
||||
- generic [ref=e469]:
|
||||
- generic [ref=e470]: Seedance
|
||||
- generic [ref=e471]: Kling
|
||||
- generic [ref=e472]: Veo 3
|
||||
- generic "拖动调整宽度" [ref=e474]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e475]
|
||||
- group [ref=e476]:
|
||||
- generic [ref=e477]:
|
||||
- generic [ref=e479]:
|
||||
- img [ref=e481]
|
||||
- generic [ref=e485]: 合成成品 · Compose
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e488]:
|
||||
- img [ref=e489]
|
||||
- generic [ref=e492]:
|
||||
- generic [ref=e493]: STEP 8 · ffmpeg + 字幕 · 待运行
|
||||
- generic [ref=e494]:
|
||||
- text: 视频片段 + 字幕 / TTS
|
||||
- text: → 最终 mp4 输出
|
||||
- generic "拖动调整宽度" [ref=e495]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e496]
|
||||
- img
|
||||
- generic "Control Panel" [ref=e497]:
|
||||
- button "Zoom In" [ref=e498] [cursor=pointer]:
|
||||
- img [ref=e499]
|
||||
- button "Zoom Out" [ref=e501] [cursor=pointer]:
|
||||
- img [ref=e502]
|
||||
- button "Fit View" [ref=e504] [cursor=pointer]:
|
||||
- img [ref=e505]
|
||||
- button "Toggle Interactivity" [ref=e507] [cursor=pointer]:
|
||||
- img [ref=e508]
|
||||
- img "Mini Map" [ref=e511]
|
||||
- region "Notifications alt+T":
|
||||
- list:
|
||||
- listitem [ref=e521]:
|
||||
- img [ref=e523]
|
||||
- generic [ref=e526]: 已自动排版 · 保留每个节点的尺寸
|
||||
- listitem [ref=e527]:
|
||||
- img [ref=e529]
|
||||
- generic [ref=e532]: 📥 视频已就绪 — 请点 Input 节点里的「点这里开始解析」按钮
|
||||
- alert [ref=e533]
|
||||
36
.playwright-mcp/page-2026-05-13T18-03-03-246Z.yml
Normal file
36
.playwright-mcp/page-2026-05-13T18-03-03-246Z.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=e7] [cursor=pointer]:
|
||||
- img [ref=e8]
|
||||
- main [ref=e14]:
|
||||
- button "自动排版 · 保留每个节点的尺寸,重新排好间距和列布局" [ref=e16]:
|
||||
- img [ref=e17]
|
||||
- application [ref=e24]:
|
||||
- group [ref=e27]:
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e35]: 输入 · Input
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e38]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e42]:
|
||||
- generic [ref=e43]: STEP 1 · 待运行
|
||||
- textbox "粘贴 TikTok 链接" [ref=e44]
|
||||
- generic [ref=e45]:
|
||||
- button "提交链接" [disabled] [ref=e46]
|
||||
- button "上传" [ref=e47]:
|
||||
- img [ref=e48]
|
||||
- text: 上传
|
||||
- generic "拖动调整宽度" [ref=e52]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e53]
|
||||
- img
|
||||
- generic "Control Panel" [ref=e54]:
|
||||
- button "Zoom In" [ref=e55] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- button "Zoom Out" [ref=e58] [cursor=pointer]:
|
||||
- img [ref=e59]
|
||||
- button "Fit View" [ref=e61] [cursor=pointer]:
|
||||
- img [ref=e62]
|
||||
- button "Toggle Interactivity" [ref=e64] [cursor=pointer]:
|
||||
- img [ref=e65]
|
||||
- img "Mini Map" [ref=e68]
|
||||
- region "Notifications alt+T"
|
||||
300
.playwright-mcp/page-2026-05-13T18-03-06-345Z.yml
Normal file
300
.playwright-mcp/page-2026-05-13T18-03-06-345Z.yml
Normal file
@@ -0,0 +1,300 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e6] [cursor=pointer]:
|
||||
- button "Open Next.js Dev Tools" [ref=e7]:
|
||||
- img [ref=e8]
|
||||
- generic [ref=e71]:
|
||||
- button "Open issues overlay" [ref=e72]:
|
||||
- generic [ref=e73]:
|
||||
- generic [ref=e74]: "0"
|
||||
- generic [ref=e75]: "1"
|
||||
- generic [ref=e76]: Issue
|
||||
- button "Collapse issues badge" [ref=e77]:
|
||||
- img [ref=e78]
|
||||
- main [ref=e14]:
|
||||
- button "自动排版 · 保留每个节点的尺寸,重新排好间距和列布局" [ref=e16]:
|
||||
- img [ref=e17]
|
||||
- button "切到明亮主题" [ref=e81]:
|
||||
- img [ref=e82]
|
||||
- generic [ref=e22]:
|
||||
- generic [ref=e90]:
|
||||
- generic [ref=e91]:
|
||||
- img [ref=e92]
|
||||
- generic [ref=e97]: 分镜头编排
|
||||
- generic [ref=e98]: 0 分镜 · 0 元素
|
||||
- generic [ref=e99]: · 组织分镜画面 → 为生成视频做准备
|
||||
- button "展开编排" [disabled] [ref=e101]:
|
||||
- img [ref=e102]
|
||||
- text: 展开编排
|
||||
- application [ref=e24]:
|
||||
- generic [ref=e26]:
|
||||
- generic:
|
||||
- generic:
|
||||
- img:
|
||||
- group "Edge from input to keyframe" [ref=e104] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from input to audio" [ref=e107] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from keyframe to storyboard" [ref=e110] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from storyboard to videogen" [ref=e113] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from videogen to compose" [ref=e116] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from audio to compose" [ref=e119] [cursor=pointer]
|
||||
- generic:
|
||||
- group [ref=e27]:
|
||||
- generic [ref=e28]:
|
||||
- generic [ref=e122]:
|
||||
- button "再上传一个视频" [ref=e123]:
|
||||
- img [ref=e124]
|
||||
- button "64.5s" [ref=e126]:
|
||||
- generic [ref=e128]: 64.5s
|
||||
- button "72.4s" [ref=e130]:
|
||||
- generic [ref=e132]: 72.4s
|
||||
- button "64.5s" [ref=e134]:
|
||||
- generic [ref=e136]: 64.5s
|
||||
- button "71.4s" [ref=e138]:
|
||||
- generic [ref=e140]: 71.4s
|
||||
- button "72.4s" [ref=e142]:
|
||||
- generic [ref=e144]: 72.4s
|
||||
- button "71.4s" [ref=e146]:
|
||||
- generic [ref=e148]: 71.4s
|
||||
- button "71.4s" [ref=e150]:
|
||||
- generic [ref=e152]: 71.4s
|
||||
- button "71.4s" [ref=e154]:
|
||||
- generic [ref=e156]: 71.4s
|
||||
- button "71.4s" [ref=e158]:
|
||||
- generic [ref=e160]: 71.4s
|
||||
- button "71.4s" [ref=e162]:
|
||||
- generic [ref=e164]: 71.4s
|
||||
- button "8.0s" [ref=e166]:
|
||||
- generic [ref=e168]: 8.0s
|
||||
- button "8.0s" [ref=e170]:
|
||||
- generic [ref=e172]: 8.0s
|
||||
- button "8.0s" [ref=e174]:
|
||||
- generic [ref=e176]: 8.0s
|
||||
- button "8.0s" [ref=e178]:
|
||||
- generic [ref=e180]: 8.0s
|
||||
- button "…" [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e186]: …
|
||||
- button "…" [ref=e188]:
|
||||
- img [ref=e190]
|
||||
- generic [ref=e192]: …
|
||||
- button "…" [ref=e194]:
|
||||
- img [ref=e196]
|
||||
- generic [ref=e198]: …
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 1080×1920
|
||||
- generic: 64.5s
|
||||
- generic [ref=e29]:
|
||||
- generic [ref=e30]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e35]: 输入 · Input
|
||||
- generic [ref=e36]:
|
||||
- img [ref=e199]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e38]:
|
||||
- img [ref=e39]
|
||||
- generic [ref=e42]:
|
||||
- generic [ref=e43]: STEP 1 · 视频就绪 · 完成
|
||||
- textbox "再加一个 TK 链接" [ref=e202]
|
||||
- generic [ref=e45]:
|
||||
- button "+ 加链接" [disabled] [ref=e203]
|
||||
- button "再传一个" [ref=e204]:
|
||||
- img [ref=e48]
|
||||
- text: 再传一个
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: 1080×1920 · 64.5s
|
||||
- generic [ref=e207]: 🔗 链接
|
||||
- button "重新解析" [ref=e208]
|
||||
- generic "拖动调整宽度" [ref=e52]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e53]
|
||||
- group [ref=e209]:
|
||||
- generic [ref=e210]:
|
||||
- generic [ref=e211]:
|
||||
- generic [ref=e212]:
|
||||
- button "frame 9 1.7s" [ref=e213]:
|
||||
- img "frame 9" [ref=e214]
|
||||
- generic [ref=e215]: 1.7s
|
||||
- button "📋" [ref=e216]
|
||||
- button "删除该关键帧" [ref=e217]:
|
||||
- img [ref=e218]
|
||||
- generic [ref=e221]:
|
||||
- button "frame 0 ✨ 24.7s" [ref=e222]:
|
||||
- img "frame 0" [ref=e223]
|
||||
- generic "已清洗" [ref=e225]: ✨
|
||||
- generic [ref=e226]: 24.7s
|
||||
- button "📋" [ref=e227]
|
||||
- button "删除该关键帧" [ref=e228]:
|
||||
- img [ref=e229]
|
||||
- generic [ref=e232]:
|
||||
- button "frame 1 33.6s" [ref=e233]:
|
||||
- img "frame 1" [ref=e234]
|
||||
- generic [ref=e235]: 33.6s
|
||||
- button "📋" [ref=e236]
|
||||
- button "删除该关键帧" [ref=e237]:
|
||||
- img [ref=e238]
|
||||
- generic [ref=e241]:
|
||||
- button "frame 2 37.7s" [ref=e242]:
|
||||
- img "frame 2" [ref=e243]
|
||||
- generic [ref=e244]: 37.7s
|
||||
- button "📋" [ref=e245]
|
||||
- button "删除该关键帧" [ref=e246]:
|
||||
- img [ref=e247]
|
||||
- generic [ref=e250]:
|
||||
- button "frame 3 39.4s" [ref=e251]:
|
||||
- img "frame 3" [ref=e252]
|
||||
- generic [ref=e253]: 39.4s
|
||||
- button "📋" [ref=e254]
|
||||
- button "删除该关键帧" [ref=e255]:
|
||||
- img [ref=e256]
|
||||
- generic [ref=e259]:
|
||||
- button "frame 4 1 43.1s" [ref=e260]:
|
||||
- img "frame 4" [ref=e261]
|
||||
- generic "1 个元素已抠图" [ref=e263]: "1"
|
||||
- generic [ref=e264]: 43.1s
|
||||
- button "📋" [ref=e265]
|
||||
- button "删除该关键帧" [ref=e266]:
|
||||
- img [ref=e267]
|
||||
- generic [ref=e270]:
|
||||
- button "frame 5 45.0s" [ref=e271]:
|
||||
- img "frame 5" [ref=e272]
|
||||
- generic [ref=e273]: 45.0s
|
||||
- button "📋" [ref=e274]
|
||||
- button "删除该关键帧" [ref=e275]:
|
||||
- img [ref=e276]
|
||||
- generic [ref=e279]:
|
||||
- button "frame 6 53.6s" [ref=e280]:
|
||||
- img "frame 6" [ref=e281]
|
||||
- generic [ref=e282]: 53.6s
|
||||
- button "📋" [ref=e283]
|
||||
- button "删除该关键帧" [ref=e284]:
|
||||
- img [ref=e285]
|
||||
- generic [ref=e288]:
|
||||
- button "frame 7 56.0s" [ref=e289]:
|
||||
- img "frame 7" [ref=e290]
|
||||
- generic [ref=e291]: 56.0s
|
||||
- button "📋" [ref=e292]
|
||||
- button "删除该关键帧" [ref=e293]:
|
||||
- img [ref=e294]
|
||||
- generic [ref=e297]:
|
||||
- button "frame 8 58.4s" [ref=e298]:
|
||||
- img "frame 8" [ref=e299]
|
||||
- generic [ref=e300]: 58.4s
|
||||
- button "📋" [ref=e301]
|
||||
- button "删除该关键帧" [ref=e302]:
|
||||
- img [ref=e303]
|
||||
- generic [ref=e306]:
|
||||
- generic [ref=e308]:
|
||||
- img [ref=e310]
|
||||
- generic [ref=e314]: 镜头拆解 · 元素提取
|
||||
- generic [ref=e315]:
|
||||
- img [ref=e316]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e320]:
|
||||
- img [ref=e321]
|
||||
- generic [ref=e324]:
|
||||
- generic [ref=e325]: STEP 2 · 0/10 入编排 · 完成
|
||||
- generic [ref=e326]:
|
||||
- text: 自动 10 张 ·
|
||||
- generic [ref=e327]: 1 已清洗
|
||||
- text: ·
|
||||
- generic [ref=e328]: 1/2 已抠图
|
||||
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
|
||||
- generic "拖动调整宽度" [ref=e330]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e331]
|
||||
- group [ref=e332]:
|
||||
- generic [ref=e333]:
|
||||
- generic [ref=e335]:
|
||||
- img [ref=e337]
|
||||
- generic [ref=e340]: 音频处理 · Audio
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e343]:
|
||||
- img [ref=e344]
|
||||
- generic [ref=e347]:
|
||||
- generic [ref=e348]: STEP 3 · ASR + 翻译 + 改写 · 待运行
|
||||
- generic [ref=e349]:
|
||||
- generic [ref=e350]:
|
||||
- generic [ref=e351]: ASR · 英文转录
|
||||
- generic [ref=e352]: Gemini 2.5 · 带时间戳分段
|
||||
- generic [ref=e353]:
|
||||
- generic [ref=e354]: 翻译 · EN → ZH
|
||||
- generic [ref=e355]: 中文翻译 · 段落级 · 实时输出
|
||||
- generic [ref=e356]:
|
||||
- generic [ref=e357]: 产品文案 · Rewrite
|
||||
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e358]
|
||||
- generic "拖动调整宽度" [ref=e360]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e361]
|
||||
- group [ref=e362]:
|
||||
- generic [ref=e363]:
|
||||
- generic [ref=e365]:
|
||||
- button "透明骷髅" [ref=e366]:
|
||||
- img "透明骷髅" [ref=e367]
|
||||
- button "📋" [ref=e368]
|
||||
- button "删除该提取图" [ref=e369]:
|
||||
- img [ref=e370]
|
||||
- generic [ref=e373]:
|
||||
- generic [ref=e375]:
|
||||
- img [ref=e377]
|
||||
- generic [ref=e382]: 元素改造 · Storyboard
|
||||
- generic [ref=e383]:
|
||||
- img [ref=e384]
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e388]:
|
||||
- img [ref=e389]
|
||||
- generic [ref=e392]:
|
||||
- generic [ref=e393]: STEP 6 · 参考元素 → SKG 画面 · 完成
|
||||
- generic [ref=e394]:
|
||||
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
|
||||
- generic [ref=e395]: 已有 1 个提取元素 · 0 个分镜进入编排
|
||||
- button "进入分镜编排" [disabled] [ref=e396]
|
||||
- generic "拖动调整宽度" [ref=e398]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e399]
|
||||
- group [ref=e400]:
|
||||
- generic [ref=e402]:
|
||||
- generic [ref=e404]:
|
||||
- img [ref=e406]
|
||||
- generic [ref=e408]: 生成视频 · Video Gen
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e411]:
|
||||
- img [ref=e412]
|
||||
- generic [ref=e415]:
|
||||
- generic [ref=e416]: STEP 7 · 首帧 + 动作 prompt · 待运行
|
||||
- generic [ref=e417]:
|
||||
- generic [ref=e418]: Seedance
|
||||
- generic [ref=e419]: Kling
|
||||
- generic [ref=e420]: Veo 3
|
||||
- generic "拖动调整宽度" [ref=e422]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e423]
|
||||
- group [ref=e424]:
|
||||
- generic [ref=e425]:
|
||||
- generic [ref=e427]:
|
||||
- img [ref=e429]
|
||||
- generic [ref=e433]: 合成成品 · Compose
|
||||
- button "钉住 · 锁定位置与尺寸" [ref=e436]:
|
||||
- img [ref=e437]
|
||||
- generic [ref=e440]:
|
||||
- generic [ref=e441]: STEP 8 · ffmpeg + 字幕 · 待运行
|
||||
- generic [ref=e442]:
|
||||
- text: 视频片段 + 字幕 / TTS
|
||||
- text: → 最终 mp4 输出
|
||||
- generic "拖动调整宽度" [ref=e443]
|
||||
- generic "拖动调整大小(宽 × 高)" [ref=e444]
|
||||
- img
|
||||
- generic "Control Panel" [ref=e54]:
|
||||
- button "Zoom In" [ref=e55] [cursor=pointer]:
|
||||
- img [ref=e56]
|
||||
- button "Zoom Out" [ref=e58] [cursor=pointer]:
|
||||
- img [ref=e59]
|
||||
- button "Fit View" [ref=e61] [cursor=pointer]:
|
||||
- img [ref=e62]
|
||||
- button "Toggle Interactivity" [ref=e64] [cursor=pointer]:
|
||||
- img [ref=e65]
|
||||
- img "Mini Map" [ref=e68]
|
||||
- region "Notifications alt+T":
|
||||
- list:
|
||||
- listitem [ref=e450]:
|
||||
- img [ref=e452]
|
||||
- generic [ref=e455]: 已自动排版 · 保留每个节点的尺寸
|
||||
- listitem [ref=e456]:
|
||||
- img [ref=e458]
|
||||
- generic [ref=e461]: 📥 视频已就绪 — 请点 Input 节点里的「点这里开始解析」按钮
|
||||
- alert [ref=e462]
|
||||
@@ -618,7 +618,7 @@ api/main.py
|
||||
<div class="flow-row">
|
||||
<div><strong>你看到的区域</strong><span>镜头拆解节点上方关键帧</span></div>
|
||||
<div><strong>主要源码</strong><span><code>KeyframeNode</code> 内嵌 <code>FrameLightbox</code>;后端 <code>/frames</code>、<code>/describe</code>、<code>/cleanup</code>。</span></div>
|
||||
<div><strong>适合怎么描述</strong><span>“关键帧详情面板在无限画布上怎么展示、缩放、跟随,以及清洗/识别/元素提取的操作顺序”。</span></div>
|
||||
<div><strong>适合怎么描述</strong><span>“关键帧详情面板在无限画布上怎么展示、缩放、跟随;缩略图 hover 原尺寸预览要贴缩略图上边缘,不是贴节点卡片上边缘”。</span></div>
|
||||
</div>
|
||||
<div class="flow-row">
|
||||
<div><strong>你看到的区域</strong><span>元素列表和提取图</span></div>
|
||||
@@ -628,7 +628,7 @@ api/main.py
|
||||
<div class="flow-row">
|
||||
<div><strong>你看到的区域</strong><span>元素改造 · Storyboard 节点</span></div>
|
||||
<div><strong>主要源码</strong><span><code>StoryboardNode</code>;上方元素缩略图来自所有已提取 cutouts。</span></div>
|
||||
<div><strong>适合怎么描述</strong><span>“这里是素材入口,不是最终视频编辑器;点击是否进入工作台要不要打断当前任务”。</span></div>
|
||||
<div><strong>适合怎么描述</strong><span>“这里是素材入口,不是最终视频编辑器;缩略图 hover 预览仍在无限画布里,并贴当前缩略图的上边缘”。</span></div>
|
||||
</div>
|
||||
<div class="flow-row">
|
||||
<div><strong>你看到的区域</strong><span>顶部分镜条</span></div>
|
||||
@@ -831,6 +831,18 @@ api/main.py
|
||||
<h2>变更记录</h2>
|
||||
<p>这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。</p>
|
||||
<div class="changelog">
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-14 · 缩略图 hover 原尺寸预览贴缩略图上边缘</h3>
|
||||
<span class="tag violet">Canvas</span>
|
||||
<span class="tag blue">HoverPreview</span>
|
||||
</header>
|
||||
<div class="body">
|
||||
<p><strong>问题:</strong>用户要的是“鼠标停在卡片缩略图上时,原尺寸图片/视频在该缩略图上方边缘展示,并且仍属于无限画布”;不能贴节点卡片上边缘,也不能放到页面 fixed 层。</p>
|
||||
<p><strong>改动:</strong><code>HoverPreview</code> 增加缩略图锚点坐标,预览层仍渲染在 ReactFlow 节点 DOM 内,但用缩略图的中心 x 和 top y 定位,预览底边贴缩略图上边缘;Input 视频、镜头拆解关键帧、元素改造 cutout、生成视频缩略图统一走该逻辑。</p>
|
||||
<p><strong>影响:</strong><code>web/components/nodes/hover-preview.tsx</code>、<code>web/components/nodes/index.tsx</code>、<code>docs/source-analysis.html</code>。</p>
|
||||
</div>
|
||||
</article>
|
||||
<article class="change">
|
||||
<header>
|
||||
<h3>2026-05-13 · 打开应用自动恢复历史 job</h3>
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
import { Toaster, toast } from "sonner"
|
||||
import { LayoutGrid } from "lucide-react"
|
||||
import {
|
||||
InputNode, KeyframeNode, ASRNode,
|
||||
TranslateNode, RewriteNode, StoryboardNode, VideoGenNode, ComposeNode, KeyframePanelNode,
|
||||
InputNode, KeyframeNode, AudioNode,
|
||||
StoryboardNode, VideoGenNode, ComposeNode, KeyframePanelNode,
|
||||
type NodeData,
|
||||
} from "@/components/nodes"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
@@ -26,9 +26,7 @@ import { VideoLightbox } from "@/components/video-lightbox"
|
||||
const NODE_TYPES = {
|
||||
input: InputNode,
|
||||
keyframe: KeyframeNode,
|
||||
asr: ASRNode,
|
||||
translate: TranslateNode,
|
||||
rewrite: RewriteNode,
|
||||
audio: AudioNode,
|
||||
storyboard: StoryboardNode,
|
||||
videogen: VideoGenNode,
|
||||
compose: ComposeNode,
|
||||
@@ -39,14 +37,12 @@ const KEYFRAME_PANEL_ID = "keyframe-detail-panel"
|
||||
|
||||
// 合并 input + download + split 为一个节点
|
||||
// 分叉:上路 input → keyframe → storyboard → videogen ↘
|
||||
// 下路 input → asr → translate → rewrite ──────→ storyboard / compose
|
||||
// 下路 input → audio ──────────────────────────→ compose
|
||||
const LAYOUT: Array<{ id: string; type: keyof typeof NODE_TYPES; x: number; y: number; w: number }> = [
|
||||
{ id: "input", type: "input", x: 40, y: 240, w: 320 },
|
||||
{ id: "keyframe", type: "keyframe", x: 460, y: 60, w: 360 },
|
||||
{ id: "asr", type: "asr", x: 460, y: 440, w: 320 },
|
||||
{ id: "translate", type: "translate", x: 840, y: 440, w: 320 },
|
||||
{ id: "audio", type: "audio", x: 460, y: 440, w: 320 },
|
||||
{ id: "storyboard", type: "storyboard", x: 880, y: 60, w: 360 },
|
||||
{ id: "rewrite", type: "rewrite", x: 1220, y: 440, w: 320 },
|
||||
{ id: "videogen", type: "videogen", x: 1260, y: 60, w: 280 },
|
||||
{ id: "compose", type: "compose", x: 1640, y: 240, w: 320 },
|
||||
]
|
||||
@@ -79,13 +75,11 @@ function loadNodePins(): string[] {
|
||||
|
||||
const EDGES_RAW: Array<[string, string]> = [
|
||||
["input", "keyframe"],
|
||||
["input", "asr"],
|
||||
["asr", "translate"],
|
||||
["translate", "rewrite"],
|
||||
["input", "audio"],
|
||||
["keyframe", "storyboard"],
|
||||
["storyboard", "videogen"],
|
||||
["videogen", "compose"],
|
||||
["rewrite", "compose"],
|
||||
["audio", "compose"],
|
||||
]
|
||||
|
||||
export default function Home() {
|
||||
@@ -527,9 +521,9 @@ export default function Home() {
|
||||
// 按管线列分组(顶 → 底):图层 1 输入 → 5 合成
|
||||
const COLUMNS: string[][] = [
|
||||
["input"],
|
||||
["keyframe", "asr"],
|
||||
["storyboard", "translate"],
|
||||
["videogen", "rewrite"],
|
||||
["keyframe", "audio"],
|
||||
["storyboard"],
|
||||
["videogen"],
|
||||
["compose"],
|
||||
]
|
||||
const GAP_X = 80
|
||||
|
||||
@@ -3,7 +3,7 @@ import { X } from "lucide-react"
|
||||
|
||||
/**
|
||||
* 视觉类节点统一大预览:
|
||||
* - **在 ReactFlow 节点 DOM 内**作为 absolute 元素,贴节点卡片上边缘
|
||||
* - **在 ReactFlow 节点 DOM 内**作为 absolute 元素,底边贴当前缩略图上方边缘
|
||||
* - 跟随 ReactFlow 画布 pan/zoom 一起变化(属于"无限画布"的一部分)
|
||||
* - 媒体按"自然像素分辨率"渲染,不做 max 尺寸限制
|
||||
* - 不 pinned 时:pointer-events-none,依赖调用方传入 visible
|
||||
@@ -20,6 +20,7 @@ interface Props {
|
||||
borderClass?: string
|
||||
visible?: boolean
|
||||
anchorX?: number
|
||||
anchorY?: number
|
||||
pinned?: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
@@ -30,6 +31,7 @@ export function HoverPreview({
|
||||
borderClass = "border-violet-300/55",
|
||||
visible = false,
|
||||
anchorX,
|
||||
anchorY,
|
||||
pinned = false,
|
||||
onClose,
|
||||
}: Props) {
|
||||
@@ -41,9 +43,10 @@ export function HoverPreview({
|
||||
<div
|
||||
className={`absolute transition-all duration-150 z-[120] ${visibilityCls}`}
|
||||
style={{
|
||||
bottom: "calc(100% + 8px)",
|
||||
top: typeof anchorY === "number" ? `${anchorY - 8}px` : undefined,
|
||||
bottom: typeof anchorY === "number" ? undefined : "calc(100% + 8px)",
|
||||
left: typeof anchorX === "number" ? `${anchorX}px` : "50%",
|
||||
transform: `translateX(-50%) scale(${shown ? 1 : 0.96})`,
|
||||
transform: `${typeof anchorY === "number" ? "translate(-50%, -100%)" : "translateX(-50%)"} scale(${shown ? 1 : 0.96})`,
|
||||
transformOrigin: "bottom center",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -85,15 +85,19 @@ function asrStatus(job: Job | null): NodeStatus {
|
||||
return "pending"
|
||||
}
|
||||
|
||||
type PreviewAnchor<T extends string | number> = { id: T; x: number }
|
||||
type PreviewAnchor<T extends string | number> = { id: T; x: number; y: number }
|
||||
|
||||
function canvasAnchorX(root: HTMLDivElement | null, target: HTMLElement) {
|
||||
if (!root) return 160
|
||||
function canvasThumbnailAnchor(root: HTMLDivElement | null, target: HTMLElement) {
|
||||
if (!root) return { x: 160, y: 0 }
|
||||
const rootRect = root.getBoundingClientRect()
|
||||
const targetRect = target.getBoundingClientRect()
|
||||
if (rootRect.width <= 0) return root.clientWidth / 2
|
||||
const ratio = (targetRect.left + targetRect.width / 2 - rootRect.left) / rootRect.width
|
||||
return ratio * root.clientWidth
|
||||
if (rootRect.width <= 0 || rootRect.height <= 0) return { x: root.clientWidth / 2, y: 0 }
|
||||
const xRatio = (targetRect.left + targetRect.width / 2 - rootRect.left) / rootRect.width
|
||||
const yRatio = (targetRect.top - rootRect.top) / rootRect.height
|
||||
return {
|
||||
x: xRatio * root.clientWidth,
|
||||
y: yRatio * root.clientHeight,
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -162,7 +166,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
isActive ? "border-violet-400 ring-2 ring-violet-400/60" : "border-white/25"
|
||||
}`}
|
||||
style={{ height: 160, aspectRatio: aspectStr }}
|
||||
onMouseEnter={(e) => setHoverPreviewJob({ id: j.id, x: canvasAnchorX(rootRef.current, e.currentTarget) })}
|
||||
onMouseEnter={(e) => setHoverPreviewJob({ id: j.id, ...canvasThumbnailAnchor(rootRef.current, e.currentTarget) })}
|
||||
onMouseLeave={() => setHoverPreviewJob(null)}
|
||||
>
|
||||
<button
|
||||
@@ -170,8 +174,8 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
// 单击:钉住 / 取消钉住大预览 + 切换 active(若需要)
|
||||
const x = canvasAnchorX(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewJob((prev) => (prev?.id === j.id ? null : { id: j.id, x }))
|
||||
const anchor = canvasThumbnailAnchor(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewJob((prev) => (prev?.id === j.id ? null : { id: j.id, ...anchor }))
|
||||
if (!isActive && ready) d.onSwitchJob(j.id)
|
||||
}}
|
||||
onDoubleClick={(e) => {
|
||||
@@ -221,6 +225,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
borderClass="border-violet-300/60"
|
||||
visible={!!hoverPreviewJob && !pinnedPreviewJob}
|
||||
anchorX={anchor.x}
|
||||
anchorY={anchor.y}
|
||||
pinned={!!pinnedPreviewJob}
|
||||
onClose={() => setPinnedPreviewJob(null)}
|
||||
/>
|
||||
@@ -465,14 +470,14 @@ export function KeyframeNode({ data, selected }: any) {
|
||||
? `${d.job.width}/${d.job.height}`
|
||||
: "16/9",
|
||||
}}
|
||||
onMouseEnter={(e) => setHoverPreviewFrame({ id: f.index, x: canvasAnchorX(rootRef.current, e.currentTarget) })}
|
||||
onMouseEnter={(e) => setHoverPreviewFrame({ id: f.index, ...canvasThumbnailAnchor(rootRef.current, e.currentTarget) })}
|
||||
onMouseLeave={() => setHoverPreviewFrame(null)}
|
||||
>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const x = canvasAnchorX(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewFrame((prev) => (prev?.id === f.index ? null : { id: f.index, x }))
|
||||
const anchor = canvasThumbnailAnchor(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewFrame((prev) => (prev?.id === f.index ? null : { id: f.index, ...anchor }))
|
||||
;(d.onOpenFramePanel ?? d.onExpandFrame)(f.index)
|
||||
}}
|
||||
title={`第 ${f.index + 1} 张 · ${f.timestamp.toFixed(1)}s · 单击钉住大预览 / 打开详情面板`}
|
||||
@@ -557,6 +562,7 @@ export function KeyframeNode({ data, selected }: any) {
|
||||
borderClass="border-orange-300/50"
|
||||
visible={!!hoverPreviewFrame && !pinnedPreviewFrame}
|
||||
anchorX={anchor.x}
|
||||
anchorY={anchor.y}
|
||||
pinned={!!pinnedPreviewFrame}
|
||||
onClose={() => setPinnedPreviewFrame(null)}
|
||||
/>
|
||||
@@ -884,6 +890,54 @@ export function RewriteNode({ data, selected }: any) {
|
||||
)
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
5b. AudioNode — 合并 ASR + 翻译 + 改写为一个"音频处理"节点
|
||||
============================================================ */
|
||||
export function AudioNode({ data, selected }: any) {
|
||||
const d: NodeData = data
|
||||
const job = d.job
|
||||
const transcript = job?.transcript ?? []
|
||||
const hasASR = transcript.length > 0
|
||||
const status: NodeStatus = !job
|
||||
? "pending"
|
||||
: job.status === "transcribing"
|
||||
? "running"
|
||||
: hasASR
|
||||
? "done"
|
||||
: "pending"
|
||||
return (
|
||||
<NodeShell
|
||||
type="ai" status={status}
|
||||
icon={<Mic className="h-4 w-4" />}
|
||||
title="音频处理 · Audio"
|
||||
subtitle={`STEP 3 · ASR + 翻译 + 改写${hasASR ? ` · ${transcript.length} 段` : ""}`}
|
||||
selected={selected}
|
||||
pinned={d.pinnedNodes?.has("audio")}
|
||||
onTogglePin={() => d.onToggleNodePin?.("audio")}
|
||||
>
|
||||
<div className="space-y-2 text-[11.5px]">
|
||||
<div className="rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/5 dark:border-white/5 px-2 py-1.5">
|
||||
<div className="text-[10px] uppercase tracking-widest text-[var(--text-faint)]">ASR · 英文转录</div>
|
||||
<div className="text-[var(--text-strong)] mt-0.5">Gemini 2.5 · 带时间戳分段</div>
|
||||
</div>
|
||||
<div className="rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/5 dark:border-white/5 px-2 py-1.5">
|
||||
<div className="text-[10px] uppercase tracking-widest text-[var(--text-faint)]">翻译 · EN → ZH</div>
|
||||
<div className="text-[var(--text-strong)] mt-0.5">中文翻译 · 段落级 · 实时输出</div>
|
||||
</div>
|
||||
<div className="rounded-md bg-white/40 dark:bg-white/[0.04] border border-black/5 dark:border-white/5 px-2 py-1.5">
|
||||
<div className="text-[10px] uppercase tracking-widest text-[var(--text-faint)]">产品文案 · Rewrite</div>
|
||||
<textarea
|
||||
placeholder="粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)"
|
||||
rows={2}
|
||||
disabled
|
||||
className="w-full text-[11.5px] mt-1 px-2 py-1 rounded-md bg-white/30 dark:bg-white/[0.03] border border-dashed border-black/15 dark:border-white/10 placeholder:text-[var(--text-faint)] text-[var(--text-strong)] resize-none opacity-70"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NodeShell>
|
||||
)
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
6. StoryboardNode — 元素改造 + 分镜编排入口
|
||||
============================================================ */
|
||||
@@ -952,14 +1006,14 @@ export function StoryboardNode({ data, selected }: any) {
|
||||
key={key}
|
||||
className="group relative shrink-0 rounded-md border border-violet-300/50 overflow-visible transition shadow-lg hover:-translate-y-0.5 bg-white"
|
||||
style={{ height: 160, aspectRatio: aspect }}
|
||||
onMouseEnter={(e) => setHoverPreviewCutout({ id: key, x: canvasAnchorX(rootRef.current, e.currentTarget) })}
|
||||
onMouseEnter={(e) => setHoverPreviewCutout({ id: key, ...canvasThumbnailAnchor(rootRef.current, e.currentTarget) })}
|
||||
onMouseLeave={() => setHoverPreviewCutout(null)}
|
||||
>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const x = canvasAnchorX(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewCutout((prev) => (prev?.id === key ? null : { id: key, x }))
|
||||
const anchor = canvasThumbnailAnchor(rootRef.current, e.currentTarget)
|
||||
setPinnedPreviewCutout((prev) => (prev?.id === key ? null : { id: key, ...anchor }))
|
||||
if (!d.selectedFrames.has(p.frameIdx)) d.onToggleFrame(p.frameIdx)
|
||||
d.onOpenStoryboard?.(p.frameIdx)
|
||||
d.onOpenWorkbench?.(p.frameIdx)
|
||||
@@ -1027,6 +1081,7 @@ export function StoryboardNode({ data, selected }: any) {
|
||||
borderClass="border-violet-300/60"
|
||||
visible={!!hoverPreviewCutout && !pinnedPreviewCutout}
|
||||
anchorX={anchor.x}
|
||||
anchorY={anchor.y}
|
||||
pinned={!!pinnedPreviewCutout}
|
||||
onClose={() => setPinnedPreviewCutout(null)}
|
||||
/>
|
||||
@@ -1105,7 +1160,7 @@ export function VideoGenNode({ data, selected }: any) {
|
||||
ready ? "border-emerald-300/60" : v.status === "failed" ? "border-rose-300/70" : "border-violet-300/55"
|
||||
}`}
|
||||
style={{ height: 160, aspectRatio: aspect }}
|
||||
onMouseEnter={(e) => setHoverPreviewVideo({ id: v.id, x: canvasAnchorX(rootRef.current, e.currentTarget) })}
|
||||
onMouseEnter={(e) => setHoverPreviewVideo({ id: v.id, ...canvasThumbnailAnchor(rootRef.current, e.currentTarget) })}
|
||||
onMouseLeave={() => setHoverPreviewVideo(null)}
|
||||
>
|
||||
<button
|
||||
@@ -1195,6 +1250,7 @@ export function VideoGenNode({ data, selected }: any) {
|
||||
borderClass={ready ? "border-emerald-300/60" : "border-rose-300/60"}
|
||||
visible
|
||||
anchorX={hoverPreviewVideo.x}
|
||||
anchorY={hoverPreviewVideo.y}
|
||||
/>
|
||||
)
|
||||
})()}
|
||||
|
||||
Reference in New Issue
Block a user