auto-save 2026-05-14 00:25 (+6, ~5)

This commit is contained in:
2026-05-14 00:26:10 +08:00
parent 540107d505
commit abeff424f6
11 changed files with 1262 additions and 57 deletions

View File

@@ -2645,6 +2645,19 @@
"message": "auto-save 2026-05-14 00:14 (+10, ~3)",
"hash": "6cd0713",
"files_changed": 13
},
{
"ts": "2026-05-14T00:20:30+08:00",
"type": "commit",
"message": "auto-save 2026-05-14 00:20 (+4, ~4)",
"hash": "540107d",
"files_changed": 8
},
{
"ts": "2026-05-13T16:23:09Z",
"type": "session-heartbeat",
"message": "Claude 会话活跃 · 最近命令claude · 5 项未提交变更 · 最近提交auto-save 2026-05-14 00:20 (+4, ~4)",
"files_changed": 5
}
]
}

View File

@@ -0,0 +1,20 @@
- generic [active] [ref=e1]:
- main [ref=e3]:
- button "切到明亮主题" [ref=e5]:
- img [ref=e6]
- application [ref=e14]:
- img
- generic "Control Panel" [ref=e17]:
- button "Zoom In" [ref=e18] [cursor=pointer]:
- img [ref=e19]
- button "Zoom Out" [ref=e21] [cursor=pointer]:
- img [ref=e22]
- button "Fit View" [ref=e24] [cursor=pointer]:
- img [ref=e25]
- button "Toggle Interactivity" [ref=e27] [cursor=pointer]:
- img [ref=e28]
- img "Mini Map" [ref=e31]
- region "Notifications alt+T"
- button "Open Next.js Dev Tools" [ref=e38] [cursor=pointer]:
- img [ref=e39]
- alert [ref=e44]

View File

@@ -0,0 +1,351 @@
- generic [active] [ref=e1]:
- main [ref=e3]:
- button "切到明亮主题" [ref=e5]:
- img [ref=e6]
- generic [ref=e12]:
- generic [ref=e47]:
- generic [ref=e48]:
- img [ref=e49]
- generic [ref=e54]: 分镜头编排
- generic [ref=e55]: 1 分镜 · 3 元素
- generic [ref=e56]: · 组织分镜画面 → 为生成视频做准备
- button "展开编排" [ref=e58]:
- img [ref=e59]
- text: 展开编排
- application [ref=e14]:
- generic [ref=e16]:
- generic:
- generic:
- img:
- group "Edge from input to keyframe" [ref=e61] [cursor=pointer]
- img:
- group "Edge from input to asr" [ref=e64] [cursor=pointer]
- img:
- group "Edge from asr to translate"
- img:
- group "Edge from translate to rewrite" [ref=e67] [cursor=pointer]
- img:
- group "Edge from keyframe to storyboard" [ref=e70] [cursor=pointer]
- img:
- group "Edge from rewrite to storyboard" [ref=e73] [cursor=pointer]
- img:
- group "Edge from storyboard to videogen" [ref=e76] [cursor=pointer]
- img:
- group "Edge from videogen to compose" [ref=e79] [cursor=pointer]
- img:
- group "Edge from rewrite to compose" [ref=e82] [cursor=pointer]
- generic:
- group [ref=e85]:
- generic [ref=e86]:
- generic [ref=e87]:
- button "再上传一个视频" [ref=e88]:
- img [ref=e89]
- button "72.4s" [ref=e90]:
- generic [ref=e92]: 72.4s
- button "64.5s" [ref=e93]:
- generic [ref=e95]: 64.5s
- button "71.4s" [ref=e96]:
- generic [ref=e98]: 71.4s
- button "72.4s" [ref=e99]:
- generic [ref=e101]: 72.4s
- button "64.5s" [ref=e102]:
- generic [ref=e104]: 64.5s
- button "71.4s" [ref=e105]:
- generic [ref=e107]: 71.4s
- button "71.4s" [ref=e108]:
- generic [ref=e110]: 71.4s
- button "71.4s" [ref=e111]:
- generic [ref=e113]: 71.4s
- button "71.4s" [ref=e114]:
- generic [ref=e116]: 71.4s
- button "71.4s" [ref=e117]:
- generic [ref=e119]: 71.4s
- button "8.0s" [ref=e120]:
- generic [ref=e122]: 8.0s
- button "8.0s" [ref=e123]:
- generic [ref=e125]: 8.0s
- button "8.0s" [ref=e126]:
- generic [ref=e128]: 8.0s
- button "8.0s" [ref=e129]:
- generic [ref=e131]: 8.0s
- button "…" [ref=e132]:
- img [ref=e134]
- generic [ref=e136]:
- button "…" [ref=e137]:
- img [ref=e139]
- generic [ref=e141]:
- button "…" [ref=e142]:
- img [ref=e144]
- generic [ref=e146]:
- generic [ref=e147]:
- generic [ref=e148]:
- img [ref=e150]
- generic [ref=e153]: 输入 · Input
- img [ref=e155]
- generic [ref=e159]:
- generic [ref=e160]: STEP 1 · 视频就绪 · 完成
- textbox "再加一个 TK 链接" [ref=e161]
- generic [ref=e162]:
- button "+ 加链接" [disabled] [ref=e163]
- button "再传一个" [ref=e164]:
- img [ref=e165]
- text: 再传一个
- generic [ref=e168]:
- generic [ref=e169]: 576×1024 · 72.4s
- generic [ref=e170]: 📎 上传
- button "重新解析" [ref=e171]
- generic "拖动调整宽度" [ref=e173]
- generic "拖动调整大小(宽 × 高)" [ref=e174]
- group [ref=e175]:
- generic [ref=e176]:
- generic [ref=e177]:
- generic [ref=e178]:
- button "frame 6 1.3s" [ref=e179]:
- img "frame 6" [ref=e180]
- generic [ref=e181]: 1.3s
- button "📋" [ref=e182]
- button "删除该关键帧" [ref=e183]:
- img [ref=e184]
- generic:
- generic:
- generic:
- generic: 分镜 7
- generic: 1.33s
- generic [ref=e187]:
- button "frame 5 3 7.4s" [ref=e188]:
- img "frame 5" [ref=e189]
- generic "3 个元素已抠图" [ref=e191]: "3"
- generic [ref=e192]: 7.4s
- button "📋" [ref=e193]
- button "删除该关键帧" [ref=e194]:
- img [ref=e195]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e198]:
- button "frame 0 11.7s" [ref=e199]:
- img "frame 0" [ref=e200]
- generic [ref=e201]: 11.7s
- button "📋" [ref=e202]
- button "删除该关键帧" [ref=e203]:
- img [ref=e204]
- generic:
- generic:
- generic:
- generic: 分镜 1
- generic: 11.68s
- generic [ref=e207]:
- button "frame 7 18.0s" [ref=e208]:
- img "frame 7" [ref=e209]
- generic [ref=e210]: 18.0s
- button "📋" [ref=e211]
- button "删除该关键帧" [ref=e212]:
- img [ref=e213]
- generic:
- generic:
- generic:
- generic: 分镜 8
- generic: 18.00s
- generic [ref=e216]:
- button "frame 1 23.4s" [ref=e217]:
- img "frame 1" [ref=e218]
- generic [ref=e219]: 23.4s
- button "📋" [ref=e220]
- button "删除该关键帧" [ref=e221]:
- img [ref=e222]
- generic:
- generic:
- generic:
- generic: 分镜 2
- generic: 23.37s
- generic [ref=e225]:
- button "frame 2 32.7s" [ref=e226]:
- img "frame 2" [ref=e227]
- generic [ref=e228]: 32.7s
- button "📋" [ref=e229]
- button "删除该关键帧" [ref=e230]:
- img [ref=e231]
- generic:
- generic:
- generic:
- generic: 分镜 3
- generic: 32.72s
- generic [ref=e234]:
- button "frame 3 49.1s" [ref=e235]:
- img "frame 3" [ref=e236]
- generic [ref=e237]: 49.1s
- button "📋" [ref=e238]
- button "删除该关键帧" [ref=e239]:
- img [ref=e240]
- generic:
- generic:
- generic:
- generic: 分镜 4
- generic: 49.08s
- generic [ref=e243]:
- button "frame 8 52.8s" [ref=e244]:
- img "frame 8" [ref=e245]
- generic [ref=e246]: 52.8s
- button "📋" [ref=e247]
- button "删除该关键帧" [ref=e248]:
- img [ref=e249]
- generic:
- generic:
- generic:
- generic: 分镜 9
- generic: 52.80s
- generic [ref=e252]:
- button "frame 9 55.5s" [ref=e253]:
- img "frame 9" [ref=e254]
- generic [ref=e255]: 55.5s
- button "📋" [ref=e256]
- button "删除该关键帧" [ref=e257]:
- img [ref=e258]
- generic:
- generic:
- generic:
- generic: 分镜 10
- generic: 55.50s
- generic [ref=e261]:
- button "frame 4 65.4s" [ref=e262]:
- img "frame 4" [ref=e263]
- generic [ref=e264]: 65.4s
- button "📋" [ref=e265]
- button "删除该关键帧" [ref=e266]:
- img [ref=e267]
- generic:
- generic:
- generic:
- generic: 分镜 5
- generic: 65.43s
- generic [ref=e270]:
- generic [ref=e272]:
- img [ref=e274]
- generic [ref=e278]: 镜头拆解 · 元素提取
- img [ref=e280]
- generic [ref=e284]:
- generic [ref=e285]: STEP 2 · 1/10 入编排 · 完成
- generic [ref=e286]:
- text: 自动 10 张 ·
- generic [ref=e287]: 0 已清洗
- text: ·
- generic [ref=e288]: 3/3 已抠图
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
- generic "拖动调整宽度" [ref=e290]
- generic "拖动调整大小(宽 × 高)" [ref=e291]
- group [ref=e292]:
- generic [ref=e293]:
- generic [ref=e295]:
- img [ref=e297]
- generic [ref=e300]: 声音文案 · ASR
- generic [ref=e303]:
- generic [ref=e304]: STEP 3 · 可选文案轨 · 待运行
- generic [ref=e305]: Gemini 2.5 · 英文带时间戳分段
- generic "拖动调整宽度" [ref=e307]
- generic "拖动调整大小(宽 × 高)" [ref=e308]
- group [ref=e309]:
- generic [ref=e310]:
- generic [ref=e312]:
- img [ref=e314]
- generic [ref=e318]: 翻译理解 · Translate
- generic [ref=e321]:
- generic [ref=e322]: STEP 4 · EN → ZH · 待运行
- generic [ref=e323]: 中文翻译 · 段落级 · 实时输出
- generic "拖动调整宽度" [ref=e325]
- generic "拖动调整大小(宽 × 高)" [ref=e326]
- group [ref=e327]:
- generic [ref=e328]:
- generic [ref=e329]:
- generic [ref=e330]:
- button "病人骨骼" [ref=e331]:
- img "病人骨骼" [ref=e332]
- button "📋" [ref=e333]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e334]:
- button "医生骨骼" [ref=e335]:
- img "医生骨骼" [ref=e336]
- button "📋" [ref=e337]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e338]:
- button "检查台" [ref=e339]:
- img "检查台" [ref=e340]
- button "📋" [ref=e341]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e342]:
- generic [ref=e344]:
- img [ref=e346]
- generic [ref=e351]: 元素改造 · Storyboard
- img [ref=e353]
- generic [ref=e357]:
- generic [ref=e358]: STEP 6 · 参考元素 → SKG 画面 · 1 分镜 · 完成
- generic [ref=e359]:
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
- generic [ref=e360]: 已有 3 个提取元素 · 1 个分镜进入编排
- button "进入分镜编排" [ref=e361]
- generic "拖动调整宽度" [ref=e363]
- generic "拖动调整大小(宽 × 高)" [ref=e364]
- group [ref=e365]:
- generic [ref=e366]:
- generic [ref=e368]:
- img [ref=e370]
- generic [ref=e374]: 产品文案 · Rewrite
- generic [ref=e377]:
- generic [ref=e378]: STEP 5 · 接 SKG 卖点 · 待运行
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e379]
- generic [ref=e380]: 下一冲刺接入
- generic "拖动调整宽度" [ref=e382]
- generic "拖动调整大小(宽 × 高)" [ref=e383]
- group [ref=e384]:
- generic [ref=e386]:
- generic [ref=e388]:
- img [ref=e390]
- generic [ref=e392]: 生成视频 · Video Gen
- generic [ref=e395]:
- generic [ref=e396]: STEP 7 · 首帧 + 动作 prompt · 待运行
- generic [ref=e397]:
- generic [ref=e398]: Seedance
- generic [ref=e399]: Kling
- generic [ref=e400]: Veo 3
- generic "拖动调整宽度" [ref=e402]
- generic "拖动调整大小(宽 × 高)" [ref=e403]
- group [ref=e404]:
- generic [ref=e405]:
- generic [ref=e407]:
- img [ref=e409]
- generic [ref=e413]: 合成成品 · Compose
- generic [ref=e416]:
- generic [ref=e417]: STEP 8 · ffmpeg + 字幕 · 待运行
- generic [ref=e418]:
- text: 视频片段 + 字幕 / TTS
- text: → 最终 mp4 输出
- generic "拖动调整宽度" [ref=e419]
- generic "拖动调整大小(宽 × 高)" [ref=e420]
- img
- generic "Control Panel" [ref=e17]:
- button "Zoom In" [ref=e18] [cursor=pointer]:
- img [ref=e19]
- button "Zoom Out" [ref=e21] [cursor=pointer]:
- img [ref=e22]
- button "Fit View" [ref=e24] [cursor=pointer]:
- img [ref=e25]
- button "Toggle Interactivity" [ref=e27] [cursor=pointer]:
- img [ref=e28]
- img "Mini Map" [ref=e31]
- region "Notifications alt+T"
- button "Open Next.js Dev Tools" [ref=e38] [cursor=pointer]:
- img [ref=e39]
- alert [ref=e44]

View File

@@ -0,0 +1,17 @@
- generic [active] [ref=e1]:
- main [ref=e3]:
- application [ref=e6]:
- img
- generic "Control Panel" [ref=e9]:
- button "Zoom In" [ref=e10] [cursor=pointer]:
- img [ref=e11]
- button "Zoom Out" [ref=e13] [cursor=pointer]:
- img [ref=e14]
- button "Fit View" [ref=e16] [cursor=pointer]:
- img [ref=e17]
- button "Toggle Interactivity" [ref=e19] [cursor=pointer]:
- img [ref=e20]
- img "Mini Map" [ref=e23]
- region "Notifications alt+T"
- button "Open Next.js Dev Tools" [ref=e30] [cursor=pointer]:
- img [ref=e31]

View File

@@ -0,0 +1,360 @@
- generic [active] [ref=e1]:
- generic [ref=e29] [cursor=pointer]:
- button "Open Next.js Dev Tools" [ref=e30]:
- img [ref=e31]
- generic [ref=e36]:
- button "Open issues overlay" [ref=e37]:
- generic [ref=e38]:
- generic [ref=e39]: "0"
- generic [ref=e40]: "1"
- generic [ref=e41]: Issue
- button "Collapse issues badge" [ref=e42]:
- img [ref=e43]
- main [ref=e46]:
- button "切到明亮主题" [ref=e48]:
- img [ref=e49]
- generic [ref=e55]:
- generic [ref=e58]:
- generic [ref=e59]:
- img [ref=e60]
- generic [ref=e65]: 分镜头编排
- generic [ref=e66]: 1 分镜 · 3 元素
- generic [ref=e67]: · 组织分镜画面 → 为生成视频做准备
- button "展开编排" [ref=e69]:
- img [ref=e70]
- text: 展开编排
- application [ref=e73]:
- generic [ref=e75]:
- generic:
- generic:
- img:
- group "Edge from input to keyframe" [ref=e76] [cursor=pointer]
- img:
- group "Edge from input to asr" [ref=e79] [cursor=pointer]
- img:
- group "Edge from asr to translate"
- img:
- group "Edge from translate to rewrite" [ref=e82] [cursor=pointer]
- img:
- group "Edge from keyframe to storyboard" [ref=e85] [cursor=pointer]
- img:
- group "Edge from rewrite to storyboard" [ref=e88] [cursor=pointer]
- img:
- group "Edge from storyboard to videogen" [ref=e91] [cursor=pointer]
- img:
- group "Edge from videogen to compose" [ref=e94] [cursor=pointer]
- img:
- group "Edge from rewrite to compose" [ref=e97] [cursor=pointer]
- generic:
- group [ref=e100]:
- generic [ref=e101]:
- generic [ref=e102]:
- button "再上传一个视频" [ref=e103]:
- img [ref=e104]
- button "72.4s" [ref=e105]:
- generic [ref=e107]: 72.4s
- button "64.5s" [ref=e108]:
- generic [ref=e110]: 64.5s
- button "71.4s" [ref=e111]:
- generic [ref=e113]: 71.4s
- button "72.4s" [ref=e114]:
- generic [ref=e116]: 72.4s
- button "64.5s" [ref=e117]:
- generic [ref=e119]: 64.5s
- button "71.4s" [ref=e120]:
- generic [ref=e122]: 71.4s
- button "71.4s" [ref=e123]:
- generic [ref=e125]: 71.4s
- button "71.4s" [ref=e126]:
- generic [ref=e128]: 71.4s
- button "71.4s" [ref=e129]:
- generic [ref=e131]: 71.4s
- button "71.4s" [ref=e132]:
- generic [ref=e134]: 71.4s
- button "8.0s" [ref=e135]:
- generic [ref=e137]: 8.0s
- button "8.0s" [ref=e138]:
- generic [ref=e140]: 8.0s
- button "8.0s" [ref=e141]:
- generic [ref=e143]: 8.0s
- button "8.0s" [ref=e144]:
- generic [ref=e146]: 8.0s
- button "…" [ref=e147]:
- img [ref=e149]
- generic [ref=e151]:
- button "…" [ref=e152]:
- img [ref=e154]
- generic [ref=e156]:
- button "…" [ref=e157]:
- img [ref=e159]
- generic [ref=e161]:
- generic [ref=e162]:
- generic [ref=e163]:
- img [ref=e165]
- generic [ref=e168]: 输入 · Input
- img [ref=e170]
- generic [ref=e174]:
- generic [ref=e175]: STEP 1 · 视频就绪 · 完成
- textbox "再加一个 TK 链接" [ref=e176]
- generic [ref=e177]:
- button "+ 加链接" [disabled] [ref=e178]
- button "再传一个" [ref=e179]:
- img [ref=e180]
- text: 再传一个
- generic [ref=e183]:
- generic [ref=e184]: 576×1024 · 72.4s
- generic [ref=e185]: 📎 上传
- button "重新解析" [ref=e186]
- generic "拖动调整宽度" [ref=e188]
- generic "拖动调整大小(宽 × 高)" [ref=e189]
- group [ref=e190]:
- generic [ref=e191]:
- generic [ref=e192]:
- generic [ref=e193]:
- button "frame 6 1.3s" [ref=e194]:
- img "frame 6" [ref=e195]
- generic [ref=e196]: 1.3s
- button "📋" [ref=e197]
- button "删除该关键帧" [ref=e198]:
- img [ref=e199]
- generic:
- generic:
- generic:
- generic: 分镜 7
- generic: 1.33s
- generic [ref=e202]:
- button "frame 5 3 7.4s" [ref=e203]:
- img "frame 5" [ref=e204]
- generic "3 个元素已抠图" [ref=e206]: "3"
- generic [ref=e207]: 7.4s
- button "📋" [ref=e208]
- button "删除该关键帧" [ref=e209]:
- img [ref=e210]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e213]:
- button "frame 0 11.7s" [ref=e214]:
- img "frame 0" [ref=e215]
- generic [ref=e216]: 11.7s
- button "📋" [ref=e217]
- button "删除该关键帧" [ref=e218]:
- img [ref=e219]
- generic:
- generic:
- generic:
- generic: 分镜 1
- generic: 11.68s
- generic [ref=e222]:
- button "frame 7 18.0s" [ref=e223]:
- img "frame 7" [ref=e224]
- generic [ref=e225]: 18.0s
- button "📋" [ref=e226]
- button "删除该关键帧" [ref=e227]:
- img [ref=e228]
- generic:
- generic:
- generic:
- generic: 分镜 8
- generic: 18.00s
- generic [ref=e231]:
- button "frame 1 23.4s" [ref=e232]:
- img "frame 1" [ref=e233]
- generic [ref=e234]: 23.4s
- button "📋" [ref=e235]
- button "删除该关键帧" [ref=e236]:
- img [ref=e237]
- generic:
- generic:
- generic:
- generic: 分镜 2
- generic: 23.37s
- generic [ref=e240]:
- button "frame 2 32.7s" [ref=e241]:
- img "frame 2" [ref=e242]
- generic [ref=e243]: 32.7s
- button "📋" [ref=e244]
- button "删除该关键帧" [ref=e245]:
- img [ref=e246]
- generic:
- generic:
- generic:
- generic: 分镜 3
- generic: 32.72s
- generic [ref=e249]:
- button "frame 3 49.1s" [ref=e250]:
- img "frame 3" [ref=e251]
- generic [ref=e252]: 49.1s
- button "📋" [ref=e253]
- button "删除该关键帧" [ref=e254]:
- img [ref=e255]
- generic:
- generic:
- generic:
- generic: 分镜 4
- generic: 49.08s
- generic [ref=e258]:
- button "frame 8 52.8s" [ref=e259]:
- img "frame 8" [ref=e260]
- generic [ref=e261]: 52.8s
- button "📋" [ref=e262]
- button "删除该关键帧" [ref=e263]:
- img [ref=e264]
- generic:
- generic:
- generic:
- generic: 分镜 9
- generic: 52.80s
- generic [ref=e267]:
- button "frame 9 55.5s" [ref=e268]:
- img "frame 9" [ref=e269]
- generic [ref=e270]: 55.5s
- button "📋" [ref=e271]
- button "删除该关键帧" [ref=e272]:
- img [ref=e273]
- generic:
- generic:
- generic:
- generic: 分镜 10
- generic: 55.50s
- generic [ref=e276]:
- button "frame 4 65.4s" [ref=e277]:
- img "frame 4" [ref=e278]
- generic [ref=e279]: 65.4s
- button "📋" [ref=e280]
- button "删除该关键帧" [ref=e281]:
- img [ref=e282]
- generic:
- generic:
- generic:
- generic: 分镜 5
- generic: 65.43s
- generic [ref=e285]:
- generic [ref=e287]:
- img [ref=e289]
- generic [ref=e293]: 镜头拆解 · 元素提取
- img [ref=e295]
- generic [ref=e299]:
- generic [ref=e300]: STEP 2 · 1/10 入编排 · 完成
- generic [ref=e301]:
- text: 自动 10 张 ·
- generic [ref=e302]: 0 已清洗
- text: ·
- generic [ref=e303]: 3/3 已抠图
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
- generic "拖动调整宽度" [ref=e305]
- generic "拖动调整大小(宽 × 高)" [ref=e306]
- group [ref=e307]:
- generic [ref=e308]:
- generic [ref=e310]:
- img [ref=e312]
- generic [ref=e315]: 声音文案 · ASR
- generic [ref=e318]:
- generic [ref=e319]: STEP 3 · 可选文案轨 · 待运行
- generic [ref=e320]: Gemini 2.5 · 英文带时间戳分段
- generic "拖动调整宽度" [ref=e322]
- generic "拖动调整大小(宽 × 高)" [ref=e323]
- group [ref=e324]:
- generic [ref=e325]:
- generic [ref=e327]:
- img [ref=e329]
- generic [ref=e333]: 翻译理解 · Translate
- generic [ref=e336]:
- generic [ref=e337]: STEP 4 · EN → ZH · 待运行
- generic [ref=e338]: 中文翻译 · 段落级 · 实时输出
- generic "拖动调整宽度" [ref=e340]
- generic "拖动调整大小(宽 × 高)" [ref=e341]
- group [ref=e342]:
- generic [ref=e343]:
- generic [ref=e344]:
- generic [ref=e345]:
- button "病人骨骼" [ref=e346]:
- img "病人骨骼" [ref=e347]
- button "📋" [ref=e348]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e349]:
- button "医生骨骼" [ref=e350]:
- img "医生骨骼" [ref=e351]
- button "📋" [ref=e352]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e353]:
- button "检查台" [ref=e354]:
- img "检查台" [ref=e355]
- button "📋" [ref=e356]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e357]:
- generic [ref=e359]:
- img [ref=e361]
- generic [ref=e366]: 元素改造 · Storyboard
- img [ref=e368]
- generic [ref=e372]:
- generic [ref=e373]: STEP 6 · 参考元素 → SKG 画面 · 1 分镜 · 完成
- generic [ref=e374]:
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
- generic [ref=e375]: 已有 3 个提取元素 · 1 个分镜进入编排
- button "进入分镜编排" [ref=e376]
- generic "拖动调整宽度" [ref=e378]
- generic "拖动调整大小(宽 × 高)" [ref=e379]
- group [ref=e380]:
- generic [ref=e381]:
- generic [ref=e383]:
- img [ref=e385]
- generic [ref=e389]: 产品文案 · Rewrite
- generic [ref=e392]:
- generic [ref=e393]: STEP 5 · 接 SKG 卖点 · 待运行
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e394]
- generic [ref=e395]: 下一冲刺接入
- generic "拖动调整宽度" [ref=e397]
- generic "拖动调整大小(宽 × 高)" [ref=e398]
- group [ref=e399]:
- generic [ref=e401]:
- generic [ref=e403]:
- img [ref=e405]
- generic [ref=e407]: 生成视频 · Video Gen
- generic [ref=e410]:
- generic [ref=e411]: STEP 7 · 首帧 + 动作 prompt · 待运行
- generic [ref=e412]:
- generic [ref=e413]: Seedance
- generic [ref=e414]: Kling
- generic [ref=e415]: Veo 3
- generic "拖动调整宽度" [ref=e417]
- generic "拖动调整大小(宽 × 高)" [ref=e418]
- group [ref=e419]:
- generic [ref=e420]:
- generic [ref=e422]:
- img [ref=e424]
- generic [ref=e428]: 合成成品 · Compose
- generic [ref=e431]:
- generic [ref=e432]: STEP 8 · ffmpeg + 字幕 · 待运行
- generic [ref=e433]:
- text: 视频片段 + 字幕 / TTS
- text: → 最终 mp4 输出
- generic "拖动调整宽度" [ref=e434]
- generic "拖动调整大小(宽 × 高)" [ref=e435]
- img
- generic "Control Panel" [ref=e436]:
- button "Zoom In" [ref=e437] [cursor=pointer]:
- img [ref=e438]
- button "Zoom Out" [ref=e440] [cursor=pointer]:
- img [ref=e441]
- button "Fit View" [ref=e443] [cursor=pointer]:
- img [ref=e444]
- button "Toggle Interactivity" [ref=e446] [cursor=pointer]:
- img [ref=e447]
- img "Mini Map" [ref=e450]
- region "Notifications alt+T"
- alert [ref=e460]

View File

@@ -0,0 +1,32 @@
- generic [active] [ref=e1]:
- button "Open Next.js Dev Tools" [ref=e7] [cursor=pointer]:
- img [ref=e8]
- main [ref=e14]:
- application [ref=e17]:
- group [ref=e20]:
- generic [ref=e22]:
- generic [ref=e23]:
- img [ref=e25]
- generic [ref=e28]: 输入 · Input
- generic [ref=e31]:
- generic [ref=e32]: STEP 1 · 待运行
- textbox "粘贴 TikTok 链接" [ref=e33]
- generic [ref=e34]:
- button "提交链接" [disabled] [ref=e35]
- button "上传" [ref=e36]:
- img [ref=e37]
- text: 上传
- generic "拖动调整宽度" [ref=e41]
- generic "拖动调整大小(宽 × 高)" [ref=e42]
- img
- generic "Control Panel" [ref=e43]:
- button "Zoom In" [ref=e44] [cursor=pointer]:
- img [ref=e45]
- button "Zoom Out" [ref=e47] [cursor=pointer]:
- img [ref=e48]
- button "Fit View" [ref=e50] [cursor=pointer]:
- img [ref=e51]
- button "Toggle Interactivity" [ref=e53] [cursor=pointer]:
- img [ref=e54]
- img "Mini Map" [ref=e57]
- region "Notifications alt+T"

View File

@@ -0,0 +1,360 @@
- generic [active] [ref=e1]:
- generic [ref=e6] [cursor=pointer]:
- button "Open Next.js Dev Tools" [ref=e7]:
- img [ref=e8]
- generic [ref=e60]:
- button "Open issues overlay" [ref=e61]:
- generic [ref=e62]:
- generic [ref=e63]: "0"
- generic [ref=e64]: "1"
- generic [ref=e65]: Issue
- button "Collapse issues badge" [ref=e66]:
- img [ref=e67]
- main [ref=e14]:
- button "切到明亮主题" [ref=e70]:
- img [ref=e71]
- generic [ref=e15]:
- generic [ref=e79]:
- generic [ref=e80]:
- img [ref=e81]
- generic [ref=e86]: 分镜头编排
- generic [ref=e87]: 1 分镜 · 3 元素
- generic [ref=e88]: · 组织分镜画面 → 为生成视频做准备
- button "展开编排" [ref=e90]:
- img [ref=e91]
- text: 展开编排
- application [ref=e17]:
- generic [ref=e19]:
- generic:
- generic:
- img:
- group "Edge from input to keyframe" [ref=e93] [cursor=pointer]
- img:
- group "Edge from input to asr" [ref=e96] [cursor=pointer]
- img:
- group "Edge from asr to translate"
- 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 rewrite to storyboard" [ref=e105] [cursor=pointer]
- img:
- group "Edge from storyboard to videogen" [ref=e108] [cursor=pointer]
- img:
- group "Edge from videogen to compose" [ref=e111] [cursor=pointer]
- img:
- group "Edge from rewrite to compose" [ref=e114] [cursor=pointer]
- generic:
- group [ref=e20]:
- generic [ref=e21]:
- generic [ref=e117]:
- button "再上传一个视频" [ref=e118]:
- img [ref=e119]
- button "72.4s" [ref=e120]:
- generic [ref=e122]: 72.4s
- button "64.5s" [ref=e123]:
- generic [ref=e125]: 64.5s
- button "71.4s" [ref=e126]:
- generic [ref=e128]: 71.4s
- button "72.4s" [ref=e129]:
- generic [ref=e131]: 72.4s
- button "64.5s" [ref=e132]:
- generic [ref=e134]: 64.5s
- button "71.4s" [ref=e135]:
- generic [ref=e137]: 71.4s
- button "71.4s" [ref=e138]:
- generic [ref=e140]: 71.4s
- button "71.4s" [ref=e141]:
- generic [ref=e143]: 71.4s
- button "71.4s" [ref=e144]:
- generic [ref=e146]: 71.4s
- button "71.4s" [ref=e147]:
- generic [ref=e149]: 71.4s
- button "8.0s" [ref=e150]:
- generic [ref=e152]: 8.0s
- button "8.0s" [ref=e153]:
- generic [ref=e155]: 8.0s
- button "8.0s" [ref=e156]:
- generic [ref=e158]: 8.0s
- button "8.0s" [ref=e159]:
- generic [ref=e161]: 8.0s
- button "…" [ref=e162]:
- img [ref=e164]
- generic [ref=e166]:
- button "…" [ref=e167]:
- img [ref=e169]
- generic [ref=e171]:
- button "…" [ref=e172]:
- img [ref=e174]
- generic [ref=e176]:
- generic [ref=e22]:
- generic [ref=e23]:
- img [ref=e25]
- generic [ref=e28]: 输入 · Input
- img [ref=e177]
- generic [ref=e31]:
- generic [ref=e32]: STEP 1 · 视频就绪 · 完成
- textbox "再加一个 TK 链接" [ref=e180]
- generic [ref=e34]:
- button "+ 加链接" [disabled] [ref=e181]
- button "再传一个" [ref=e182]:
- img [ref=e37]
- text: 再传一个
- generic [ref=e183]:
- generic [ref=e184]: 576×1024 · 72.4s
- generic [ref=e185]: 📎 上传
- button "重新解析" [ref=e186]
- generic "拖动调整宽度" [ref=e41]
- generic "拖动调整大小(宽 × 高)" [ref=e42]
- group [ref=e187]:
- generic [ref=e188]:
- generic [ref=e189]:
- generic [ref=e190]:
- button "frame 6 1.3s" [ref=e191]:
- img "frame 6" [ref=e192]
- generic [ref=e193]: 1.3s
- button "📋" [ref=e194]
- button "删除该关键帧" [ref=e195]:
- img [ref=e196]
- generic:
- generic:
- generic:
- generic: 分镜 7
- generic: 1.33s
- generic [ref=e199]:
- button "frame 5 3 7.4s" [ref=e200]:
- img "frame 5" [ref=e201]
- generic "3 个元素已抠图" [ref=e203]: "3"
- generic [ref=e204]: 7.4s
- button "📋" [ref=e205]
- button "删除该关键帧" [ref=e206]:
- img [ref=e207]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e210]:
- button "frame 0 11.7s" [ref=e211]:
- img "frame 0" [ref=e212]
- generic [ref=e213]: 11.7s
- button "📋" [ref=e214]
- button "删除该关键帧" [ref=e215]:
- img [ref=e216]
- generic:
- generic:
- generic:
- generic: 分镜 1
- generic: 11.68s
- generic [ref=e219]:
- button "frame 7 18.0s" [ref=e220]:
- img "frame 7" [ref=e221]
- generic [ref=e222]: 18.0s
- button "📋" [ref=e223]
- button "删除该关键帧" [ref=e224]:
- img [ref=e225]
- generic:
- generic:
- generic:
- generic: 分镜 8
- generic: 18.00s
- generic [ref=e228]:
- button "frame 1 23.4s" [ref=e229]:
- img "frame 1" [ref=e230]
- generic [ref=e231]: 23.4s
- button "📋" [ref=e232]
- button "删除该关键帧" [ref=e233]:
- img [ref=e234]
- generic:
- generic:
- generic:
- generic: 分镜 2
- generic: 23.37s
- generic [ref=e237]:
- button "frame 2 32.7s" [ref=e238]:
- img "frame 2" [ref=e239]
- generic [ref=e240]: 32.7s
- button "📋" [ref=e241]
- button "删除该关键帧" [ref=e242]:
- img [ref=e243]
- generic:
- generic:
- generic:
- generic: 分镜 3
- generic: 32.72s
- generic [ref=e246]:
- button "frame 3 49.1s" [ref=e247]:
- img "frame 3" [ref=e248]
- generic [ref=e249]: 49.1s
- button "📋" [ref=e250]
- button "删除该关键帧" [ref=e251]:
- img [ref=e252]
- generic:
- generic:
- generic:
- generic: 分镜 4
- generic: 49.08s
- generic [ref=e255]:
- button "frame 8 52.8s" [ref=e256]:
- img "frame 8" [ref=e257]
- generic [ref=e258]: 52.8s
- button "📋" [ref=e259]
- button "删除该关键帧" [ref=e260]:
- img [ref=e261]
- generic:
- generic:
- generic:
- generic: 分镜 9
- generic: 52.80s
- generic [ref=e264]:
- button "frame 9 55.5s" [ref=e265]:
- img "frame 9" [ref=e266]
- generic [ref=e267]: 55.5s
- button "📋" [ref=e268]
- button "删除该关键帧" [ref=e269]:
- img [ref=e270]
- generic:
- generic:
- generic:
- generic: 分镜 10
- generic: 55.50s
- generic [ref=e273]:
- button "frame 4 65.4s" [ref=e274]:
- img "frame 4" [ref=e275]
- generic [ref=e276]: 65.4s
- button "📋" [ref=e277]
- button "删除该关键帧" [ref=e278]:
- img [ref=e279]
- generic:
- generic:
- generic:
- generic: 分镜 5
- generic: 65.43s
- generic [ref=e282]:
- generic [ref=e284]:
- img [ref=e286]
- generic [ref=e290]: 镜头拆解 · 元素提取
- img [ref=e292]
- generic [ref=e296]:
- generic [ref=e297]: STEP 2 · 1/10 入编排 · 完成
- generic [ref=e298]:
- text: 自动 10 张 ·
- generic [ref=e299]: 0 已清洗
- text: ·
- generic [ref=e300]: 3/3 已抠图
- text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材
- generic "拖动调整宽度" [ref=e302]
- generic "拖动调整大小(宽 × 高)" [ref=e303]
- group [ref=e304]:
- generic [ref=e305]:
- generic [ref=e307]:
- img [ref=e309]
- generic [ref=e312]: 声音文案 · ASR
- generic [ref=e315]:
- generic [ref=e316]: STEP 3 · 可选文案轨 · 待运行
- generic [ref=e317]: Gemini 2.5 · 英文带时间戳分段
- generic "拖动调整宽度" [ref=e319]
- generic "拖动调整大小(宽 × 高)" [ref=e320]
- group [ref=e321]:
- generic [ref=e322]:
- generic [ref=e324]:
- img [ref=e326]
- generic [ref=e330]: 翻译理解 · Translate
- generic [ref=e333]:
- generic [ref=e334]: STEP 4 · EN → ZH · 待运行
- generic [ref=e335]: 中文翻译 · 段落级 · 实时输出
- generic "拖动调整宽度" [ref=e337]
- generic "拖动调整大小(宽 × 高)" [ref=e338]
- group [ref=e339]:
- generic [ref=e340]:
- generic [ref=e341]:
- generic [ref=e342]:
- button "病人骨骼" [ref=e343]:
- img "病人骨骼" [ref=e344]
- button "📋" [ref=e345]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e346]:
- button "医生骨骼" [ref=e347]:
- img "医生骨骼" [ref=e348]
- button "📋" [ref=e349]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e350]:
- button "检查台" [ref=e351]:
- img "检查台" [ref=e352]
- button "📋" [ref=e353]
- generic:
- generic:
- generic:
- generic: 分镜 6
- generic: 7.39s
- generic [ref=e354]:
- generic [ref=e356]:
- img [ref=e358]
- generic [ref=e363]: 元素改造 · Storyboard
- img [ref=e365]
- generic [ref=e369]:
- generic [ref=e370]: STEP 6 · 参考元素 → SKG 画面 · 1 分镜 · 完成
- generic [ref=e371]:
- text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。
- generic [ref=e372]: 已有 3 个提取元素 · 1 个分镜进入编排
- button "进入分镜编排" [ref=e373]
- generic "拖动调整宽度" [ref=e375]
- generic "拖动调整大小(宽 × 高)" [ref=e376]
- group [ref=e377]:
- generic [ref=e378]:
- generic [ref=e380]:
- img [ref=e382]
- generic [ref=e386]: 产品文案 · Rewrite
- generic [ref=e389]:
- generic [ref=e390]: STEP 5 · 接 SKG 卖点 · 待运行
- textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e391]
- generic [ref=e392]: 下一冲刺接入
- generic "拖动调整宽度" [ref=e394]
- generic "拖动调整大小(宽 × 高)" [ref=e395]
- group [ref=e396]:
- generic [ref=e398]:
- generic [ref=e400]:
- img [ref=e402]
- generic [ref=e404]: 生成视频 · Video Gen
- generic [ref=e407]:
- generic [ref=e408]: STEP 7 · 首帧 + 动作 prompt · 待运行
- generic [ref=e409]:
- generic [ref=e410]: Seedance
- generic [ref=e411]: Kling
- generic [ref=e412]: Veo 3
- generic "拖动调整宽度" [ref=e414]
- generic "拖动调整大小(宽 × 高)" [ref=e415]
- group [ref=e416]:
- generic [ref=e417]:
- generic [ref=e419]:
- img [ref=e421]
- generic [ref=e425]: 合成成品 · Compose
- generic [ref=e428]:
- generic [ref=e429]: STEP 8 · ffmpeg + 字幕 · 待运行
- generic [ref=e430]:
- text: 视频片段 + 字幕 / TTS
- text: → 最终 mp4 输出
- generic "拖动调整宽度" [ref=e431]
- generic "拖动调整大小(宽 × 高)" [ref=e432]
- img
- generic "Control Panel" [ref=e43]:
- button "Zoom In" [ref=e44] [cursor=pointer]:
- img [ref=e45]
- button "Zoom Out" [ref=e47] [cursor=pointer]:
- img [ref=e48]
- button "Fit View" [ref=e50] [cursor=pointer]:
- img [ref=e51]
- button "Toggle Interactivity" [ref=e53] [cursor=pointer]:
- img [ref=e54]
- img "Mini Map" [ref=e57]
- region "Notifications alt+T"
- alert [ref=e440]

View File

@@ -50,12 +50,14 @@ const LAYOUT: Array<{ id: string; type: keyof typeof NODE_TYPES; x: number; y: n
{ id: "compose", type: "compose", x: 1640, y: 240, w: 320 },
]
const NODE_WIDTHS_KEY = "skg-tk:node-widths:v1"
const NODE_SIZES_KEY = "skg-tk:node-sizes:v2"
function loadNodeWidths(): Record<string, number> {
type NodeSize = { w?: number; h?: number }
function loadNodeSizes(): Record<string, NodeSize> {
if (typeof window === "undefined") return {}
try {
const raw = window.localStorage.getItem(NODE_WIDTHS_KEY)
const raw = window.localStorage.getItem(NODE_SIZES_KEY)
return raw ? JSON.parse(raw) : {}
} catch {
return {}
@@ -445,27 +447,36 @@ 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 savedSizes = useMemo(() => loadNodeSizes(), [])
const [nodes, setNodes, onNodesChange] = useNodesState<Node>(
LAYOUT.map((n) => ({
LAYOUT.map((n) => {
const s = savedSizes[n.id] ?? {}
const w = s.w ?? n.w
const h = s.h
return {
id: n.id,
type: n.type,
position: { x: n.x, y: n.y },
data: nodeData,
draggable: true,
width: savedWidths[n.id] ?? n.w,
})),
width: w,
...(typeof h === "number" ? { height: h } : {}),
style: { width: w, ...(typeof h === "number" ? { height: h } : {}) },
}
}),
)
// 持久化每个节点宽到 localStorageKeyframePanelNode 自己管尺寸,不写回)
// 持久化每个节点宽 / 高到 localStorageKeyframePanelNode 自己管尺寸,不写回)
useEffect(() => {
const widths: Record<string, number> = {}
const sizes: Record<string, NodeSize> = {}
for (const n of nodes) {
if (n.id === KEYFRAME_PANEL_ID) continue
const w = n.width
if (typeof w === "number") widths[n.id] = Math.round(w)
const w = typeof n.width === "number" ? Math.round(n.width) : undefined
const h = typeof n.height === "number" ? Math.round(n.height) : undefined
if (w === undefined && h === undefined) continue
sizes[n.id] = { ...(w !== undefined ? { w } : {}), ...(h !== undefined ? { h } : {}) }
}
try { window.localStorage.setItem(NODE_WIDTHS_KEY, JSON.stringify(widths)) } catch {}
try { window.localStorage.setItem(NODE_SIZES_KEY, JSON.stringify(sizes)) } catch {}
}, [nodes])
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(
EDGES_RAW.map(([from, to], i) => ({

View File

@@ -103,7 +103,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
const inputLocked = isDownloading || d.submitting
return (
<div className="relative" style={{ width: "100%" }}>
<div className="relative" style={{ width: "100%", height: "100%" }}>
{/* 多视频缩略图浮条 — 「+」在最左job 按时间倒序(最新靠左高亮),统一高度 64宽度按视频原比例一行横滚。
浮条宽度 = 节点宽度(节点拖宽后浮条同步变宽,可见更多缩略图,少滚动)。 */}
{!videoExpanded && d.jobs.length > 0 && (
@@ -222,7 +222,6 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an
icon={<Link2 className="h-4 w-4" />}
title="输入 · Input"
subtitle={isDownloading ? "STEP 1 · 下载中" : hasVideo ? "STEP 1 · 视频就绪" : "STEP 1"}
width={320}
selected={selected}
hasTarget={false}
>
@@ -367,7 +366,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: "100%" }}>
<div className="relative" style={{ width: "100%", height: "100%" }}>
{/* 缩略图浮条(节点上方,最多 5 个一行,多行向上扩展) */}
{frames.length > 0 && jobId && (
<div
@@ -488,7 +487,6 @@ export function KeyframeNode({ data, selected }: any) {
icon={<ImageIcon className="h-4 w-4" />}
title="镜头拆解 · 元素提取"
subtitle={`STEP 2 · ${frames.length ? `${d.selectedFrames.size}/${frames.length} 入编排` : "等待抽取"}`}
width={KEYFRAME_WIDTH}
selected={selected}
>
{frames.length > 0 ? (() => {
@@ -836,7 +834,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: "100%" }}>
<div className="relative" style={{ width: "100%", height: "100%" }}>
{/* 节点上方:所有元素 crop 图(编排输入素材)· 跟 keyframe 节点样式一致 */}
{elementCrops.length > 0 && job && (
<div
@@ -919,7 +917,6 @@ export function StoryboardNode({ data, selected }: any) {
icon={<LayoutGrid className="h-4 w-4" />}
title="元素改造 · Storyboard"
subtitle={`STEP 6 · 参考元素 → SKG 画面${storyboardCount > 0 ? ` · ${storyboardCount} 分镜` : ""}`}
width={IMAGEGEN_WIDTH}
selected={selected}
>
<div className="text-[11.5px] leading-relaxed text-[var(--text-soft)]">
@@ -972,7 +969,7 @@ export function VideoGenNode({ data, selected }: any) {
return e
}
return (
<div className="relative" style={{ width: "100%" }}>
<div className="relative" style={{ width: "100%", height: "100%" }}>
{videos.length > 0 && (
<div
className="absolute left-0 right-0 grid grid-cols-3 gap-1.5"

View File

@@ -2,7 +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"
import { ResizeRight, ResizeBR } from "./resize-handle"
export type NodeKind = "input" | "process" | "ai" | "output"
export type NodeStatus = "pending" | "running" | "done" | "failed"
@@ -50,7 +50,7 @@ export function NodeShell({
<div
className={`glass-node ${selected ? "glass-node--selected" : ""} ${status === "running" ? "glass-node--running" : ""}`}
data-type={type}
style={{ width }}
style={{ width, height: "100%" }}
>
{hasTarget && <Handle type="target" position={Position.Left} />}
@@ -76,6 +76,7 @@ export function NodeShell({
{hasSource && <Handle type="source" position={Position.Right} />}
<ResizeRight />
<ResizeBR />
</div>
)
}

View File

@@ -2,11 +2,22 @@
import { type PointerEvent } from "react"
import { useReactFlow } from "@xyflow/react"
/** 节点右边缘 resize 把手:手写 pointer 事件改 node.width自适应 viewport zoom不走 xyflow NodeResizeControl在 glass-node overflow:hidden 下不工作)。 */
export function ResizeRight({ minWidth = 240, maxWidth = 1200 }: { minWidth?: number; maxWidth?: number }) {
const { setNodes, getZoom } = useReactFlow()
interface Bounds {
minWidth?: number
maxWidth?: number
minHeight?: number
maxHeight?: number
}
const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {
/** 拖动期间持续更新 ReactFlow node 的 width/height同时写到 style.width/height 让 wrapper 真实渲染。
* 自适应 viewport zoom。不走 xyflow NodeResizeControlglass-node overflow:hidden + reserved-type CSS 干扰下不响应)。 */
function startResize(
e: PointerEvent<HTMLDivElement>,
setNodes: ReturnType<typeof useReactFlow>["setNodes"],
getZoom: ReturnType<typeof useReactFlow>["getZoom"],
axis: "x" | "y" | "xy",
bounds: Required<Bounds>,
) {
e.preventDefault()
e.stopPropagation()
const target = e.currentTarget
@@ -16,13 +27,26 @@ export function ResizeRight({ minWidth = 240, maxWidth = 1200 }: { minWidth?: nu
target.setPointerCapture(e.pointerId)
const startX = e.clientX
const startY = e.clientY
const zoom = getZoom() || 1
const startWidth = parseFloat(getComputedStyle(nodeEl).width) / zoom
const startHeight = parseFloat(getComputedStyle(nodeEl).height) / zoom
const onMove = (ev: globalThis.PointerEvent) => {
const dx = (ev.clientX - startX) / zoom
const next = Math.max(minWidth, Math.min(maxWidth, startWidth + dx))
setNodes((nodes) => nodes.map((n) => (n.id === nodeId ? { ...n, width: next, style: { ...n.style, width: next } } : n)))
const dy = (ev.clientY - startY) / zoom
const wantW = axis === "y" ? null : Math.max(bounds.minWidth, Math.min(bounds.maxWidth, startWidth + dx))
const wantH = axis === "x" ? null : Math.max(bounds.minHeight, Math.min(bounds.maxHeight, startHeight + dy))
setNodes((nodes) =>
nodes.map((n) => {
if (n.id !== nodeId) return n
const nextStyle = { ...n.style }
const patch: Record<string, unknown> = {}
if (wantW !== null) { patch.width = wantW; nextStyle.width = wantW }
if (wantH !== null) { patch.height = wantH; nextStyle.height = wantH }
return { ...n, ...patch, style: nextStyle }
}),
)
}
const onUp = () => {
window.removeEventListener("pointermove", onMove)
@@ -33,18 +57,37 @@ export function ResizeRight({ minWidth = 240, maxWidth = 1200 }: { minWidth?: nu
window.addEventListener("pointerup", onUp)
}
/** 右边缘把手 — 只改宽度 */
export function ResizeRight({ minWidth = 240, maxWidth = 1400 }: Bounds) {
const { setNodes, getZoom } = useReactFlow()
return (
<div
onPointerDown={onPointerDown}
onPointerDown={(e) => startResize(e, setNodes, getZoom, "x", { minWidth, maxWidth, minHeight: 0, maxHeight: 0 })}
title="拖动调整宽度"
className="nodrag absolute z-20 hover:bg-violet-400/60 active:bg-violet-400/80 transition rounded-r"
style={{ right: 0, top: 12, bottom: 12, width: 6, cursor: "ew-resize", touchAction: "none" }}
/>
)
}
/** 右下角把手 — 同时改宽和高 */
export function ResizeBR({
minWidth = 240, maxWidth = 1400, minHeight = 120, maxHeight = 1400,
}: Bounds) {
const { setNodes, getZoom } = useReactFlow()
return (
<div
onPointerDown={(e) => startResize(e, setNodes, getZoom, "xy", { minWidth, maxWidth, minHeight, maxHeight })}
title="拖动调整大小(宽 × 高)"
className="nodrag absolute z-30 hover:bg-violet-400/80 active:bg-violet-400 transition"
style={{
right: 0,
top: 12,
bottom: 12,
width: 6,
cursor: "ew-resize",
bottom: 0,
width: 14,
height: 14,
cursor: "nwse-resize",
touchAction: "none",
background: "linear-gradient(135deg, transparent 50%, rgba(167,139,250,0.55) 50%)",
}}
/>
)