auto-save 2026-05-14 00:02 (+3, ~4)
This commit is contained in:
@@ -2605,6 +2605,13 @@
|
||||
"type": "session-heartbeat",
|
||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 23:51 (~1)",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T23:57:28+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-13 23:57 (~2)",
|
||||
"hash": "12daaa2",
|
||||
"files_changed": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
120
.playwright-mcp/page-2026-05-13T15-59-43-994Z.yml
Normal file
120
.playwright-mcp/page-2026-05-13T15-59-43-994Z.yml
Normal file
@@ -0,0 +1,120 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- main [ref=e3]:
|
||||
- button "切到明亮主题" [ref=e5]:
|
||||
- img [ref=e6]
|
||||
- application [ref=e14]:
|
||||
- generic [ref=e16]:
|
||||
- generic:
|
||||
- generic:
|
||||
- img:
|
||||
- group "Edge from input to keyframe" [ref=e17] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from input to asr" [ref=e20] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from asr to translate"
|
||||
- img:
|
||||
- group "Edge from translate to rewrite" [ref=e23] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from keyframe to storyboard" [ref=e26] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from rewrite to storyboard" [ref=e29] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from storyboard to videogen" [ref=e32] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from videogen to compose" [ref=e35] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from rewrite to compose" [ref=e38] [cursor=pointer]
|
||||
- generic:
|
||||
- group [ref=e41]:
|
||||
- generic [ref=e43]:
|
||||
- generic [ref=e44]:
|
||||
- img [ref=e46]
|
||||
- generic [ref=e49]: 输入 · Input
|
||||
- generic [ref=e52]:
|
||||
- generic [ref=e53]: STEP 1 · 待运行
|
||||
- textbox "粘贴 TikTok 链接" [ref=e54]
|
||||
- generic [ref=e55]:
|
||||
- button "提交链接" [disabled] [ref=e56]
|
||||
- button "上传" [ref=e57]:
|
||||
- img [ref=e58]
|
||||
- text: 上传
|
||||
- group [ref=e64]:
|
||||
- generic [ref=e66]:
|
||||
- generic [ref=e68]:
|
||||
- img [ref=e70]
|
||||
- generic [ref=e74]: 镜头拆解 · 元素提取
|
||||
- generic [ref=e77]:
|
||||
- generic [ref=e78]: STEP 2 · 等待抽取 · 待运行
|
||||
- generic [ref=e79]: 等待解析(默认 5 张)
|
||||
- group [ref=e83]:
|
||||
- generic [ref=e84]:
|
||||
- generic [ref=e86]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e91]: 声音文案 · ASR
|
||||
- generic [ref=e94]:
|
||||
- generic [ref=e95]: STEP 3 · 可选文案轨 · 待运行
|
||||
- generic [ref=e96]: Gemini 2.5 · 英文带时间戳分段
|
||||
- group [ref=e100]:
|
||||
- generic [ref=e101]:
|
||||
- generic [ref=e103]:
|
||||
- img [ref=e105]
|
||||
- generic [ref=e109]: 翻译理解 · Translate
|
||||
- generic [ref=e112]:
|
||||
- generic [ref=e113]: STEP 4 · EN → ZH · 待运行
|
||||
- generic [ref=e114]: 中文翻译 · 段落级 · 实时输出
|
||||
- group [ref=e118]:
|
||||
- generic [ref=e120]:
|
||||
- generic [ref=e122]:
|
||||
- img [ref=e124]
|
||||
- generic [ref=e129]: 元素改造 · Storyboard
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: STEP 6 · 参考元素 → SKG 画面 · 待运行
|
||||
- generic [ref=e134]:
|
||||
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
|
||||
- generic [ref=e135]: 已有 0 个提取元素 · 0 个分镜进入编排
|
||||
- button "进入分镜编排" [disabled] [ref=e136]
|
||||
- group [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e143]:
|
||||
- img [ref=e145]
|
||||
- generic [ref=e149]: 产品文案 · Rewrite
|
||||
- generic [ref=e152]:
|
||||
- generic [ref=e153]: STEP 5 · 接 SKG 卖点 · 待运行
|
||||
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e154]
|
||||
- generic [ref=e155]: 下一冲刺接入
|
||||
- group [ref=e159]:
|
||||
- generic [ref=e161]:
|
||||
- generic [ref=e163]:
|
||||
- img [ref=e165]
|
||||
- generic [ref=e167]: 生成视频 · Video Gen
|
||||
- generic [ref=e170]:
|
||||
- generic [ref=e171]: STEP 7 · 首帧 + 动作 prompt · 待运行
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Seedance
|
||||
- generic [ref=e174]: Kling
|
||||
- generic [ref=e175]: Veo 3
|
||||
- group [ref=e179]:
|
||||
- generic [ref=e180]:
|
||||
- generic [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e188]: 合成成品 · Compose
|
||||
- generic [ref=e191]:
|
||||
- generic [ref=e192]: STEP 8 · ffmpeg + 字幕 · 待运行
|
||||
- generic [ref=e193]:
|
||||
- text: 视频片段 + 字幕 / TTS
|
||||
- text: → 最终 mp4 输出
|
||||
- img
|
||||
- generic "Control Panel" [ref=e196]:
|
||||
- button "Zoom In" [ref=e197] [cursor=pointer]:
|
||||
- img [ref=e198]
|
||||
- button "Zoom Out" [ref=e200] [cursor=pointer]:
|
||||
- img [ref=e201]
|
||||
- button "Fit View" [ref=e203] [cursor=pointer]:
|
||||
- img [ref=e204]
|
||||
- button "Toggle Interactivity" [ref=e206] [cursor=pointer]:
|
||||
- img [ref=e207]
|
||||
- img "Mini Map" [ref=e210]
|
||||
- region "Notifications alt+T"
|
||||
- button "Open Next.js Dev Tools" [ref=e225] [cursor=pointer]:
|
||||
- img [ref=e226]
|
||||
- alert [ref=e231]
|
||||
335
.playwright-mcp/page-2026-05-13T15-59-48-897Z.yml
Normal file
335
.playwright-mcp/page-2026-05-13T15-59-48-897Z.yml
Normal file
@@ -0,0 +1,335 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- main [ref=e3]:
|
||||
- button "切到明亮主题" [ref=e5]:
|
||||
- img [ref=e6]
|
||||
- generic [ref=e12]:
|
||||
- generic [ref=e234]:
|
||||
- generic [ref=e235]:
|
||||
- img [ref=e236]
|
||||
- generic [ref=e241]: 分镜头编排
|
||||
- generic [ref=e242]: 1 分镜 · 3 元素
|
||||
- generic [ref=e243]: · 组织分镜画面 → 为生成视频做准备
|
||||
- button "展开编排" [ref=e245]:
|
||||
- img [ref=e246]
|
||||
- text: 展开编排
|
||||
- application [ref=e14]:
|
||||
- generic [ref=e16]:
|
||||
- generic:
|
||||
- generic:
|
||||
- img:
|
||||
- group "Edge from input to keyframe" [ref=e17] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from input to asr" [ref=e20] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from asr to translate"
|
||||
- img:
|
||||
- group "Edge from translate to rewrite" [ref=e23] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from keyframe to storyboard" [ref=e26] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from rewrite to storyboard" [ref=e29] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from storyboard to videogen" [ref=e32] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from videogen to compose" [ref=e35] [cursor=pointer]
|
||||
- img:
|
||||
- group "Edge from rewrite to compose" [ref=e38] [cursor=pointer]
|
||||
- generic:
|
||||
- group [ref=e41]:
|
||||
- generic [ref=e42]:
|
||||
- generic [ref=e248]:
|
||||
- button "再上传一个视频" [ref=e249]:
|
||||
- img [ref=e250]
|
||||
- button "72.4s" [ref=e251]:
|
||||
- generic [ref=e253]: 72.4s
|
||||
- button "64.5s" [ref=e254]:
|
||||
- generic [ref=e256]: 64.5s
|
||||
- button "71.4s" [ref=e257]:
|
||||
- generic [ref=e259]: 71.4s
|
||||
- button "72.4s" [ref=e260]:
|
||||
- generic [ref=e262]: 72.4s
|
||||
- button "64.5s" [ref=e263]:
|
||||
- generic [ref=e265]: 64.5s
|
||||
- button "71.4s" [ref=e266]:
|
||||
- generic [ref=e268]: 71.4s
|
||||
- button "71.4s" [ref=e269]:
|
||||
- generic [ref=e271]: 71.4s
|
||||
- button "71.4s" [ref=e272]:
|
||||
- generic [ref=e274]: 71.4s
|
||||
- button "71.4s" [ref=e275]:
|
||||
- generic [ref=e277]: 71.4s
|
||||
- button "71.4s" [ref=e278]:
|
||||
- generic [ref=e280]: 71.4s
|
||||
- button "8.0s" [ref=e281]:
|
||||
- generic [ref=e283]: 8.0s
|
||||
- button "8.0s" [ref=e284]:
|
||||
- generic [ref=e286]: 8.0s
|
||||
- button "8.0s" [ref=e287]:
|
||||
- generic [ref=e289]: 8.0s
|
||||
- button "8.0s" [ref=e290]:
|
||||
- generic [ref=e292]: 8.0s
|
||||
- button "…" [ref=e293]:
|
||||
- img [ref=e295]
|
||||
- generic [ref=e297]: …
|
||||
- button "…" [ref=e298]:
|
||||
- img [ref=e300]
|
||||
- generic [ref=e302]: …
|
||||
- button "…" [ref=e303]:
|
||||
- img [ref=e305]
|
||||
- generic [ref=e307]: …
|
||||
- generic [ref=e43]:
|
||||
- generic [ref=e44]:
|
||||
- img [ref=e46]
|
||||
- generic [ref=e49]: 输入 · Input
|
||||
- img [ref=e308]
|
||||
- generic [ref=e52]:
|
||||
- generic [ref=e53]: STEP 1 · 视频就绪 · 完成
|
||||
- textbox "再加一个 TK 链接" [ref=e311]
|
||||
- generic [ref=e55]:
|
||||
- button "+ 加链接" [disabled] [ref=e312]
|
||||
- button "再传一个" [ref=e313]:
|
||||
- img [ref=e58]
|
||||
- text: 再传一个
|
||||
- generic [ref=e314]:
|
||||
- generic [ref=e315]: 576×1024 · 72.4s
|
||||
- generic [ref=e316]: 📎 上传
|
||||
- button "重新解析" [ref=e317]
|
||||
- group [ref=e64]:
|
||||
- generic [ref=e65]:
|
||||
- generic [ref=e318]:
|
||||
- generic [ref=e319]:
|
||||
- button "frame 6 1.3s" [ref=e320]:
|
||||
- img "frame 6" [ref=e321]
|
||||
- generic [ref=e322]: 1.3s
|
||||
- button "📋" [ref=e323]
|
||||
- button "删除该关键帧" [ref=e324]:
|
||||
- img [ref=e325]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 7
|
||||
- generic: 1.33s
|
||||
- generic [ref=e328]:
|
||||
- button "frame 5 3 7.4s" [ref=e329]:
|
||||
- img "frame 5" [ref=e330]
|
||||
- generic "3 个元素已抠图" [ref=e332]: "3"
|
||||
- generic [ref=e333]: 7.4s
|
||||
- button "📋" [ref=e334]
|
||||
- button "删除该关键帧" [ref=e335]:
|
||||
- img [ref=e336]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 6
|
||||
- generic: 7.39s
|
||||
- generic [ref=e339]:
|
||||
- button "frame 0 11.7s" [ref=e340]:
|
||||
- img "frame 0" [ref=e341]
|
||||
- generic [ref=e342]: 11.7s
|
||||
- button "📋" [ref=e343]
|
||||
- button "删除该关键帧" [ref=e344]:
|
||||
- img [ref=e345]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 1
|
||||
- generic: 11.68s
|
||||
- generic [ref=e348]:
|
||||
- button "frame 7 18.0s" [ref=e349]:
|
||||
- img "frame 7" [ref=e350]
|
||||
- generic [ref=e351]: 18.0s
|
||||
- button "📋" [ref=e352]
|
||||
- button "删除该关键帧" [ref=e353]:
|
||||
- img [ref=e354]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 8
|
||||
- generic: 18.00s
|
||||
- generic [ref=e357]:
|
||||
- button "frame 1 23.4s" [ref=e358]:
|
||||
- img "frame 1" [ref=e359]
|
||||
- generic [ref=e360]: 23.4s
|
||||
- button "📋" [ref=e361]
|
||||
- button "删除该关键帧" [ref=e362]:
|
||||
- img [ref=e363]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 2
|
||||
- generic: 23.37s
|
||||
- generic [ref=e366]:
|
||||
- button "frame 2 32.7s" [ref=e367]:
|
||||
- img "frame 2" [ref=e368]
|
||||
- generic [ref=e369]: 32.7s
|
||||
- button "📋" [ref=e370]
|
||||
- button "删除该关键帧" [ref=e371]:
|
||||
- img [ref=e372]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 3
|
||||
- generic: 32.72s
|
||||
- generic [ref=e375]:
|
||||
- button "frame 3 49.1s" [ref=e376]:
|
||||
- img "frame 3" [ref=e377]
|
||||
- generic [ref=e378]: 49.1s
|
||||
- button "📋" [ref=e379]
|
||||
- button "删除该关键帧" [ref=e380]:
|
||||
- img [ref=e381]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 4
|
||||
- generic: 49.08s
|
||||
- generic [ref=e384]:
|
||||
- button "frame 8 52.8s" [ref=e385]:
|
||||
- img "frame 8" [ref=e386]
|
||||
- generic [ref=e387]: 52.8s
|
||||
- button "📋" [ref=e388]
|
||||
- button "删除该关键帧" [ref=e389]:
|
||||
- img [ref=e390]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 9
|
||||
- generic: 52.80s
|
||||
- generic [ref=e393]:
|
||||
- button "frame 9 55.5s" [ref=e394]:
|
||||
- img "frame 9" [ref=e395]
|
||||
- generic [ref=e396]: 55.5s
|
||||
- button "📋" [ref=e397]
|
||||
- button "删除该关键帧" [ref=e398]:
|
||||
- img [ref=e399]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 10
|
||||
- generic: 55.50s
|
||||
- generic [ref=e402]:
|
||||
- button "frame 4 65.4s" [ref=e403]:
|
||||
- img "frame 4" [ref=e404]
|
||||
- generic [ref=e405]: 65.4s
|
||||
- button "📋" [ref=e406]
|
||||
- button "删除该关键帧" [ref=e407]:
|
||||
- img [ref=e408]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 5
|
||||
- generic: 65.43s
|
||||
- generic [ref=e66]:
|
||||
- generic [ref=e68]:
|
||||
- img [ref=e70]
|
||||
- generic [ref=e74]: 镜头拆解 · 元素提取
|
||||
- img [ref=e411]
|
||||
- generic [ref=e77]:
|
||||
- generic [ref=e78]: STEP 2 · 1/10 入编排 · 完成
|
||||
- generic [ref=e79]:
|
||||
- text: 自动 10 张 ·
|
||||
- generic [ref=e414]: 0 已清洗
|
||||
- text: ·
|
||||
- generic [ref=e415]: 3/3 已抠图
|
||||
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
|
||||
- group [ref=e83]:
|
||||
- generic [ref=e84]:
|
||||
- generic [ref=e86]:
|
||||
- img [ref=e88]
|
||||
- generic [ref=e91]: 声音文案 · ASR
|
||||
- generic [ref=e94]:
|
||||
- generic [ref=e95]: STEP 3 · 可选文案轨 · 待运行
|
||||
- generic [ref=e96]: Gemini 2.5 · 英文带时间戳分段
|
||||
- group [ref=e100]:
|
||||
- generic [ref=e101]:
|
||||
- generic [ref=e103]:
|
||||
- img [ref=e105]
|
||||
- generic [ref=e109]: 翻译理解 · Translate
|
||||
- generic [ref=e112]:
|
||||
- generic [ref=e113]: STEP 4 · EN → ZH · 待运行
|
||||
- generic [ref=e114]: 中文翻译 · 段落级 · 实时输出
|
||||
- group [ref=e118]:
|
||||
- generic [ref=e119]:
|
||||
- generic [ref=e416]:
|
||||
- generic [ref=e417]:
|
||||
- button "病人骨骼" [ref=e418]:
|
||||
- img "病人骨骼" [ref=e419]
|
||||
- button "📋" [ref=e420]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 6
|
||||
- generic: 7.39s
|
||||
- generic [ref=e421]:
|
||||
- button "医生骨骼" [ref=e422]:
|
||||
- img "医生骨骼" [ref=e423]
|
||||
- button "📋" [ref=e424]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 6
|
||||
- generic: 7.39s
|
||||
- generic [ref=e425]:
|
||||
- button "检查台" [ref=e426]:
|
||||
- img "检查台" [ref=e427]
|
||||
- button "📋" [ref=e428]
|
||||
- generic:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: 分镜 6
|
||||
- generic: 7.39s
|
||||
- generic [ref=e120]:
|
||||
- generic [ref=e122]:
|
||||
- img [ref=e124]
|
||||
- generic [ref=e129]: 元素改造 · Storyboard
|
||||
- img [ref=e429]
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: STEP 6 · 参考元素 → SKG 画面 · 1 分镜 · 完成
|
||||
- generic [ref=e134]:
|
||||
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
|
||||
- generic [ref=e135]: 已有 3 个提取元素 · 1 个分镜进入编排
|
||||
- button "进入分镜编排" [ref=e136]
|
||||
- group [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e143]:
|
||||
- img [ref=e145]
|
||||
- generic [ref=e149]: 产品文案 · Rewrite
|
||||
- generic [ref=e152]:
|
||||
- generic [ref=e153]: STEP 5 · 接 SKG 卖点 · 待运行
|
||||
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e154]
|
||||
- generic [ref=e155]: 下一冲刺接入
|
||||
- group [ref=e159]:
|
||||
- generic [ref=e161]:
|
||||
- generic [ref=e163]:
|
||||
- img [ref=e165]
|
||||
- generic [ref=e167]: 生成视频 · Video Gen
|
||||
- generic [ref=e170]:
|
||||
- generic [ref=e171]: STEP 7 · 首帧 + 动作 prompt · 待运行
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Seedance
|
||||
- generic [ref=e174]: Kling
|
||||
- generic [ref=e175]: Veo 3
|
||||
- group [ref=e179]:
|
||||
- generic [ref=e180]:
|
||||
- generic [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e188]: 合成成品 · Compose
|
||||
- generic [ref=e191]:
|
||||
- generic [ref=e192]: STEP 8 · ffmpeg + 字幕 · 待运行
|
||||
- generic [ref=e193]:
|
||||
- text: 视频片段 + 字幕 / TTS
|
||||
- text: → 最终 mp4 输出
|
||||
- img
|
||||
- generic "Control Panel" [ref=e196]:
|
||||
- button "Zoom In" [ref=e197] [cursor=pointer]:
|
||||
- img [ref=e198]
|
||||
- button "Zoom Out" [ref=e200] [cursor=pointer]:
|
||||
- img [ref=e201]
|
||||
- button "Fit View" [ref=e203] [cursor=pointer]:
|
||||
- img [ref=e204]
|
||||
- button "Toggle Interactivity" [ref=e206] [cursor=pointer]:
|
||||
- img [ref=e207]
|
||||
- img "Mini Map" [ref=e210]
|
||||
- region "Notifications alt+T"
|
||||
- button "Open Next.js Dev Tools" [ref=e225] [cursor=pointer]:
|
||||
- img [ref=e226]
|
||||
- alert [ref=e231]
|
||||
@@ -39,17 +39,29 @@ const KEYFRAME_PANEL_ID = "keyframe-detail-panel"
|
||||
// 合并 input + download + split 为一个节点
|
||||
// 分叉:上路 input → keyframe → storyboard → videogen ↘
|
||||
// 下路 input → asr → translate → rewrite ──────→ storyboard / compose
|
||||
const LAYOUT: Array<{ id: string; type: keyof typeof NODE_TYPES; x: number; y: number }> = [
|
||||
{ id: "input", type: "input", x: 40, y: 240 },
|
||||
{ id: "keyframe", type: "keyframe", x: 460, y: 60 },
|
||||
{ id: "asr", type: "asr", x: 460, y: 440 },
|
||||
{ id: "translate", type: "translate", x: 840, y: 440 },
|
||||
{ id: "storyboard", type: "storyboard", x: 880, y: 60 },
|
||||
{ id: "rewrite", type: "rewrite", x: 1220, y: 440 },
|
||||
{ id: "videogen", type: "videogen", x: 1260, y: 60 },
|
||||
{ id: "compose", type: "compose", x: 1640, y: 240 },
|
||||
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: "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 },
|
||||
]
|
||||
|
||||
const NODE_WIDTHS_KEY = "skg-tk:node-widths:v1"
|
||||
|
||||
function loadNodeWidths(): Record<string, number> {
|
||||
if (typeof window === "undefined") return {}
|
||||
try {
|
||||
const raw = window.localStorage.getItem(NODE_WIDTHS_KEY)
|
||||
return raw ? JSON.parse(raw) : {}
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const EDGES_RAW: Array<[string, string]> = [
|
||||
["input", "keyframe"],
|
||||
["input", "asr"],
|
||||
@@ -433,6 +445,7 @@ export default function Home() {
|
||||
}), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleSwitchJob, setJob, handleDeleteFrame, handleDeleteGenerated, handleDeleteVideo, handleCopyImage])
|
||||
|
||||
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag)
|
||||
const savedWidths = useMemo(() => loadNodeWidths(), [])
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(
|
||||
LAYOUT.map((n) => ({
|
||||
id: n.id,
|
||||
@@ -440,8 +453,21 @@ export default function Home() {
|
||||
position: { x: n.x, y: n.y },
|
||||
data: nodeData,
|
||||
draggable: true,
|
||||
width: savedWidths[n.id] ?? n.w,
|
||||
style: { width: savedWidths[n.id] ?? n.w },
|
||||
})),
|
||||
)
|
||||
|
||||
// 持久化每个节点宽度到 localStorage(KeyframePanelNode 自己管尺寸,不写回)
|
||||
useEffect(() => {
|
||||
const widths: Record<string, number> = {}
|
||||
for (const n of nodes) {
|
||||
if (n.id === KEYFRAME_PANEL_ID) continue
|
||||
const w = (n.style?.width ?? n.width) as number | string | undefined
|
||||
if (typeof w === "number") widths[n.id] = Math.round(w)
|
||||
}
|
||||
try { window.localStorage.setItem(NODE_WIDTHS_KEY, JSON.stringify(widths)) } catch {}
|
||||
}, [nodes])
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(
|
||||
EDGES_RAW.map(([from, to], i) => ({
|
||||
id: `e${i}`, source: from, target: to, animated: false, type: "default",
|
||||
|
||||
@@ -103,7 +103,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
|
||||
const inputLocked = isDownloading || d.submitting
|
||||
|
||||
return (
|
||||
<div className="relative" style={{ width: 320 }}>
|
||||
<div className="relative" style={{ width: "100%" }}>
|
||||
{/* 多视频缩略图浮条 — 「+」在最左,job 按时间倒序(最新靠左高亮),统一高度 64,宽度按视频原比例,一行横滚 */}
|
||||
{!videoExpanded && d.jobs.length > 0 && (
|
||||
<div
|
||||
@@ -371,7 +371,7 @@ export function KeyframeNode({ data, selected }: any) {
|
||||
const aspectStr = d.job && d.job.height > 0 ? `${d.job.width}/${d.job.height}` : "9/16"
|
||||
|
||||
return (
|
||||
<div className="relative" style={{ width: KEYFRAME_WIDTH }}>
|
||||
<div className="relative" style={{ width: "100%" }}>
|
||||
{/* 缩略图浮条(节点上方,最多 5 个一行,多行向上扩展) */}
|
||||
{frames.length > 0 && jobId && (
|
||||
<div
|
||||
@@ -840,7 +840,7 @@ export function StoryboardNode({ data, selected }: any) {
|
||||
const aspect = job && job.height > 0 ? `${job.width}/${job.height}` : "9/16"
|
||||
|
||||
return (
|
||||
<div className="relative" style={{ width: IMAGEGEN_WIDTH }}>
|
||||
<div className="relative" style={{ width: "100%" }}>
|
||||
{/* 节点上方:所有元素 crop 图(编排输入素材)· 跟 keyframe 节点样式一致 */}
|
||||
{elementCrops.length > 0 && job && (
|
||||
<div
|
||||
@@ -976,7 +976,7 @@ export function VideoGenNode({ data, selected }: any) {
|
||||
return e
|
||||
}
|
||||
return (
|
||||
<div className="relative" style={{ width: 280 }}>
|
||||
<div className="relative" style={{ width: "100%" }}>
|
||||
{videos.length > 0 && (
|
||||
<div
|
||||
className="absolute left-0 right-0 grid grid-cols-3 gap-1.5"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { type ReactNode } from "react"
|
||||
import { Handle, Position } from "@xyflow/react"
|
||||
import { CheckCircle2, Loader2, AlertCircle } from "lucide-react"
|
||||
import { ResizeRight } from "./resize-handle"
|
||||
|
||||
export type NodeKind = "input" | "process" | "ai" | "output"
|
||||
export type NodeStatus = "pending" | "running" | "done" | "failed"
|
||||
@@ -74,6 +75,7 @@ export function NodeShell({
|
||||
</div>
|
||||
|
||||
{hasSource && <Handle type="source" position={Position.Right} />}
|
||||
<ResizeRight />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
27
web/components/nodes/resize-handle.tsx
Normal file
27
web/components/nodes/resize-handle.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
"use client"
|
||||
import { NodeResizeControl } from "@xyflow/react"
|
||||
|
||||
/** 节点右边缘 resize 把手:高度跟随节点,宽度可拖(240–1200px)。平时透明,hover 显紫色细条。 */
|
||||
export function ResizeRight({ minWidth = 240, maxWidth = 1200 }: { minWidth?: number; maxWidth?: number }) {
|
||||
return (
|
||||
<NodeResizeControl
|
||||
position="right"
|
||||
minWidth={minWidth}
|
||||
maxWidth={maxWidth}
|
||||
style={{
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
width: 8,
|
||||
height: "100%",
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: "translateX(50%)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-full h-full hover:bg-violet-400/50 active:bg-violet-400/80 transition rounded-r"
|
||||
style={{ cursor: "ew-resize" }}
|
||||
/>
|
||||
</NodeResizeControl>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user