diff --git a/.memory/worklog.json b/.memory/worklog.json index 8fda864..5de379c 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -2691,6 +2691,13 @@ "type": "session-heartbeat", "message": "Claude 会话活跃 · 最近命令:claude · 3 项未提交变更 · 最近提交:auto-save 2026-05-14 00:37 (+1, ~2)", "files_changed": 3 + }, + { + "ts": "2026-05-14T00:43:18+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 00:42 (+4, ~3)", + "hash": "042efdc", + "files_changed": 7 } ] } diff --git a/.playwright-mcp/page-2026-05-13T16-45-57-495Z.yml b/.playwright-mcp/page-2026-05-13T16-45-57-495Z.yml new file mode 100644 index 0000000..04c7677 --- /dev/null +++ b/.playwright-mcp/page-2026-05-13T16-45-57-495Z.yml @@ -0,0 +1,13 @@ +- generic [active] [ref=e1]: + - generic [ref=e6] [cursor=pointer]: + - button "Open Next.js Dev Tools" [ref=e7]: + - img [ref=e8] + - generic [ref=e13]: + - button "Open issues overlay" [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: "0" + - generic [ref=e17]: "1" + - generic [ref=e18]: Issue + - button "Collapse issues badge" [ref=e19]: + - img [ref=e20] + - 'heading "Application error: a client-side exception has occurred while loading localhost (see the browser console for more information)." [level=2] [ref=e24]' \ No newline at end of file diff --git a/.playwright-mcp/page-2026-05-13T16-45-59-775Z.yml b/.playwright-mcp/page-2026-05-13T16-45-59-775Z.yml new file mode 100644 index 0000000..34556b4 --- /dev/null +++ b/.playwright-mcp/page-2026-05-13T16-45-59-775Z.yml @@ -0,0 +1,131 @@ +- generic [ref=e1]: + - generic [active]: + - generic [ref=e27]: + - generic [ref=e28]: + - generic [ref=e29]: + - navigation [ref=e30]: + - button "previous" [disabled] [ref=e31]: + - img "previous" [ref=e32] + - generic [ref=e34]: + - generic [ref=e35]: 1/ + - text: "1" + - button "next" [disabled] [ref=e36]: + - img "next" [ref=e37] + - img + - generic [ref=e39]: + - link "Next.js 16.0.7 (stale) Turbopack" [ref=e40] [cursor=pointer]: + - /url: https://nextjs.org/docs/messages/version-staleness + - img [ref=e41] + - generic "There is a newer version (16.2.6) available, upgrade recommended!" [ref=e43]: Next.js 16.0.7 (stale) + - generic [ref=e44]: Turbopack + - img + - dialog "Runtime ReferenceError" [ref=e46]: + - generic [ref=e49]: + - generic [ref=e50]: + - generic [ref=e51]: + - generic [ref=e53]: Runtime ReferenceError + - generic [ref=e54]: + - button "Copy Error Info" [ref=e55] [cursor=pointer]: + - img [ref=e56] + - button "No related documentation found" [disabled] [ref=e58]: + - img [ref=e59] + - link "Learn more about enabling Node.js inspector for server code with Chrome DevTools" [ref=e61] [cursor=pointer]: + - /url: https://nextjs.org/docs/app/building-your-application/configuring/debugging#server-side-code + - img [ref=e62] + - generic [ref=e74]: Cannot access 'pinnedNodes' before initialization + - generic [ref=e76]: + - paragraph [ref=e78]: + - text: Call Stack + - generic [ref=e79]: "13" + - generic [ref=e80]: + - generic [ref=e81]: + - text: Home + - button "Sourcemapping failed. Click to log cause of error." [ref=e82] [cursor=pointer]: + - img [ref=e83] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/_377ebb54._.js (6845:9) + - generic [ref=e85]: + - generic [ref=e86]: + - text: Object.react_stack_bottom_frame + - button "Sourcemapping failed. Click to log cause of error." [ref=e87] [cursor=pointer]: + - img [ref=e88] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (14816:24) + - generic [ref=e90]: + - generic [ref=e91]: + - text: renderWithHooks + - button "Sourcemapping failed. Click to log cause of error." [ref=e92] [cursor=pointer]: + - img [ref=e93] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (4645:24) + - generic [ref=e95]: + - generic [ref=e96]: + - text: updateFunctionComponent + - button "Sourcemapping failed. Click to log cause of error." [ref=e97] [cursor=pointer]: + - img [ref=e98] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (6106:21) + - generic [ref=e100]: + - generic [ref=e101]: + - text: beginWork + - button "Sourcemapping failed. Click to log cause of error." [ref=e102] [cursor=pointer]: + - img [ref=e103] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (6702:24) + - generic [ref=e105]: + - generic [ref=e106]: + - text: runWithFiberInDEV + - button "Sourcemapping failed. Click to log cause of error." [ref=e107] [cursor=pointer]: + - img [ref=e108] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (959:74) + - generic [ref=e110]: + - generic [ref=e111]: + - text: performUnitOfWork + - button "Sourcemapping failed. Click to log cause of error." [ref=e112] [cursor=pointer]: + - img [ref=e113] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (9556:97) + - generic [ref=e115]: + - generic [ref=e116]: + - text: workLoopSync + - button "Sourcemapping failed. Click to log cause of error." [ref=e117] [cursor=pointer]: + - img [ref=e118] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (9450:40) + - generic [ref=e120]: + - generic [ref=e121]: + - text: renderRootSync + - button "Sourcemapping failed. Click to log cause of error." [ref=e122] [cursor=pointer]: + - img [ref=e123] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (9434:13) + - generic [ref=e125]: + - generic [ref=e126]: + - text: performWorkOnRoot + - button "Sourcemapping failed. Click to log cause of error." [ref=e127] [cursor=pointer]: + - img [ref=e128] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (9099:47) + - generic [ref=e130]: + - generic [ref=e131]: + - text: performWorkOnRootViaSchedulerTask + - button "Sourcemapping failed. Click to log cause of error." [ref=e132] [cursor=pointer]: + - img [ref=e133] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_react-dom_e6182150._.js (10224:9) + - generic [ref=e135]: + - generic [ref=e136]: + - text: MessagePort.performWorkUntilDeadline + - button "Sourcemapping failed. Click to log cause of error." [ref=e137] [cursor=pointer]: + - img [ref=e138] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_compiled_4535dad2._.js (2647:64) + - generic [ref=e140]: + - generic [ref=e141]: + - text: ClientPageRoot + - button "Sourcemapping failed. Click to log cause of error." [ref=e142] [cursor=pointer]: + - img [ref=e143] + - text: file:///Users/kangwan/Projects/business/20260512-20260512-skg-tk-%E4%BA%8C%E5%88%9B%E9%AA%8C%E8%AF%81/web/.next/dev/static/chunks/e559b_next_dist_03f6cd6c._.js (2202:50) + - generic [ref=e145]: "1" + - generic [ref=e146]: "2" + - generic [ref=e6] [cursor=pointer]: + - button "Open Next.js Dev Tools" [ref=e7]: + - img [ref=e8] + - generic [ref=e13]: + - button "Open issues overlay" [ref=e14]: + - generic [ref=e15]: + - generic [ref=e16]: "0" + - generic [ref=e17]: "1" + - generic [ref=e18]: Issue + - button "Collapse issues badge" [ref=e19]: + - img [ref=e20] + - 'heading "Application error: a client-side exception has occurred while loading localhost (see the browser console for more information)." [level=2] [ref=e24]' \ No newline at end of file diff --git a/.playwright-mcp/page-2026-05-13T16-46-28-588Z.yml b/.playwright-mcp/page-2026-05-13T16-46-28-588Z.yml new file mode 100644 index 0000000..62aaa00 --- /dev/null +++ b/.playwright-mcp/page-2026-05-13T16-46-28-588Z.yml @@ -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] \ No newline at end of file diff --git a/.playwright-mcp/page-2026-05-13T16-46-30-804Z.yml b/.playwright-mcp/page-2026-05-13T16-46-30-804Z.yml new file mode 100644 index 0000000..42e670d --- /dev/null +++ b/.playwright-mcp/page-2026-05-13T16-46-30-804Z.yml @@ -0,0 +1,449 @@ +- 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]: 0 分镜 · 0 元素 + - generic [ref=e67]: · 组织分镜画面 → 为生成视频做准备 + - button "展开编排" [disabled] [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] + - generic [ref=e105]: + - button "64.5s" [ref=e106]: + - generic [ref=e108]: 64.5s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 64.5s + - generic [ref=e109]: + - button "72.4s" [ref=e110]: + - generic [ref=e112]: 72.4s + - generic: + - generic: + - generic: + - generic: 576×1024 + - generic: 72.4s + - generic [ref=e113]: + - button "64.5s" [ref=e114]: + - generic [ref=e116]: 64.5s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 64.5s + - generic [ref=e117]: + - button "71.4s" [ref=e118]: + - generic [ref=e120]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e121]: + - button "72.4s" [ref=e122]: + - generic [ref=e124]: 72.4s + - generic: + - generic: + - generic: + - generic: 576×1024 + - generic: 72.4s + - generic [ref=e125]: + - button "71.4s" [ref=e126]: + - generic [ref=e128]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e129]: + - button "71.4s" [ref=e130]: + - generic [ref=e132]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e133]: + - button "71.4s" [ref=e134]: + - generic [ref=e136]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e137]: + - button "71.4s" [ref=e138]: + - generic [ref=e140]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e141]: + - button "71.4s" [ref=e142]: + - generic [ref=e144]: 71.4s + - generic: + - generic: + - generic: + - generic: 1080×1920 + - generic: 71.4s + - generic [ref=e145]: + - button "8.0s" [ref=e146]: + - generic [ref=e148]: 8.0s + - generic: + - generic: + - generic: + - generic: 640×360 + - generic: 8.0s + - generic [ref=e149]: + - button "8.0s" [ref=e150]: + - generic [ref=e152]: 8.0s + - generic: + - generic: + - generic: + - generic: 640×360 + - generic: 8.0s + - generic [ref=e153]: + - button "8.0s" [ref=e154]: + - generic [ref=e156]: 8.0s + - generic: + - generic: + - generic: + - generic: 640×360 + - generic: 8.0s + - generic [ref=e157]: + - button "8.0s" [ref=e158]: + - generic [ref=e160]: 8.0s + - generic: + - generic: + - generic: + - generic: 640×360 + - generic: 8.0s + - button "…" [ref=e162]: + - img [ref=e164] + - generic [ref=e166]: … + - button "…" [ref=e168]: + - img [ref=e170] + - generic [ref=e172]: … + - button "…" [ref=e174]: + - img [ref=e176] + - generic [ref=e178]: … + - generic [ref=e179]: + - generic [ref=e180]: + - img [ref=e182] + - generic [ref=e185]: 输入 · Input + - generic [ref=e186]: + - img [ref=e187] + - button "钉住 · 锁定位置与尺寸" [ref=e191]: + - img [ref=e192] + - generic [ref=e195]: + - generic [ref=e196]: STEP 1 · 视频就绪 · 完成 + - textbox "再加一个 TK 链接" [ref=e197] + - generic [ref=e198]: + - button "+ 加链接" [disabled] [ref=e199] + - button "再传一个" [ref=e200]: + - img [ref=e201] + - text: 再传一个 + - generic [ref=e204]: + - generic [ref=e205]: 1080×1920 · 64.5s + - generic [ref=e206]: 🔗 链接 + - button "重新解析" [ref=e207] + - generic "拖动调整宽度" [ref=e209] + - generic "拖动调整大小(宽 × 高)" [ref=e210] + - group [ref=e211]: + - generic [ref=e212]: + - generic [ref=e213]: + - generic [ref=e214]: + - button "frame 9 1.7s" [ref=e215]: + - img "frame 9" [ref=e216] + - generic [ref=e217]: 1.7s + - button "📋" [ref=e218] + - button "删除该关键帧" [ref=e219]: + - img [ref=e220] + - generic: + - generic: + - generic: + - generic: 分镜 10 + - generic: 1.66s + - generic [ref=e223]: + - button "frame 0 ✨ 24.7s" [ref=e224]: + - img "frame 0" [ref=e225] + - generic "已清洗" [ref=e227]: ✨ + - generic [ref=e228]: 24.7s + - button "📋" [ref=e229] + - button "删除该关键帧" [ref=e230]: + - img [ref=e231] + - generic: + - generic: + - generic: + - generic: 分镜 1 + - generic: 24.73s + - generic [ref=e234]: + - button "frame 1 33.6s" [ref=e235]: + - img "frame 1" [ref=e236] + - generic [ref=e237]: 33.6s + - button "📋" [ref=e238] + - button "删除该关键帧" [ref=e239]: + - img [ref=e240] + - generic: + - generic: + - generic: + - generic: 分镜 2 + - generic: 33.61s + - generic [ref=e243]: + - button "frame 2 37.7s" [ref=e244]: + - img "frame 2" [ref=e245] + - generic [ref=e246]: 37.7s + - button "📋" [ref=e247] + - button "删除该关键帧" [ref=e248]: + - img [ref=e249] + - generic: + - generic: + - generic: + - generic: 分镜 3 + - generic: 37.70s + - generic [ref=e252]: + - button "frame 3 39.4s" [ref=e253]: + - img "frame 3" [ref=e254] + - generic [ref=e255]: 39.4s + - button "📋" [ref=e256] + - button "删除该关键帧" [ref=e257]: + - img [ref=e258] + - generic: + - generic: + - generic: + - generic: 分镜 4 + - generic: 39.42s + - generic [ref=e261]: + - button "frame 4 1 43.1s" [ref=e262]: + - img "frame 4" [ref=e263] + - generic "1 个元素已抠图" [ref=e265]: "1" + - generic [ref=e266]: 43.1s + - button "📋" [ref=e267] + - button "删除该关键帧" [ref=e268]: + - img [ref=e269] + - generic: + - generic: + - generic: + - generic: 分镜 5 + - generic: 43.13s + - generic [ref=e272]: + - button "frame 5 45.0s" [ref=e273]: + - img "frame 5" [ref=e274] + - generic [ref=e275]: 45.0s + - button "📋" [ref=e276] + - button "删除该关键帧" [ref=e277]: + - img [ref=e278] + - generic: + - generic: + - generic: + - generic: 分镜 6 + - generic: 45.05s + - generic [ref=e281]: + - button "frame 6 53.6s" [ref=e282]: + - img "frame 6" [ref=e283] + - generic [ref=e284]: 53.6s + - button "📋" [ref=e285] + - button "删除该关键帧" [ref=e286]: + - img [ref=e287] + - generic: + - generic: + - generic: + - generic: 分镜 7 + - generic: 53.60s + - generic [ref=e290]: + - button "frame 7 56.0s" [ref=e291]: + - img "frame 7" [ref=e292] + - generic [ref=e293]: 56.0s + - button "📋" [ref=e294] + - button "删除该关键帧" [ref=e295]: + - img [ref=e296] + - generic: + - generic: + - generic: + - generic: 分镜 8 + - generic: 55.96s + - generic [ref=e299]: + - button "frame 8 58.4s" [ref=e300]: + - img "frame 8" [ref=e301] + - generic [ref=e302]: 58.4s + - button "📋" [ref=e303] + - button "删除该关键帧" [ref=e304]: + - img [ref=e305] + - generic: + - generic: + - generic: + - generic: 分镜 9 + - generic: 58.39s + - generic [ref=e308]: + - generic [ref=e310]: + - img [ref=e312] + - generic [ref=e316]: 镜头拆解 · 元素提取 + - generic [ref=e317]: + - img [ref=e318] + - button "钉住 · 锁定位置与尺寸" [ref=e322]: + - img [ref=e323] + - generic [ref=e326]: + - generic [ref=e327]: STEP 2 · 0/10 入编排 · 完成 + - generic [ref=e328]: + - text: 自动 10 张 · + - generic [ref=e329]: 1 已清洗 + - text: · + - generic [ref=e330]: 1/2 已抠图 + - text: 点缩略图 → 清洗水印 / 提取可借鉴元素 → 改造成 SKG 画面素材 + - generic "拖动调整宽度" [ref=e332] + - generic "拖动调整大小(宽 × 高)" [ref=e333] + - group [ref=e334]: + - generic [ref=e335]: + - generic [ref=e337]: + - img [ref=e339] + - generic [ref=e342]: 声音文案 · ASR + - button "钉住 · 锁定位置与尺寸" [ref=e345]: + - img [ref=e346] + - generic [ref=e349]: + - generic [ref=e350]: STEP 3 · 可选文案轨 · 待运行 + - generic [ref=e351]: Gemini 2.5 · 英文带时间戳分段 + - generic "拖动调整宽度" [ref=e353] + - generic "拖动调整大小(宽 × 高)" [ref=e354] + - group [ref=e355]: + - generic [ref=e356]: + - generic [ref=e358]: + - img [ref=e360] + - generic [ref=e364]: 翻译理解 · Translate + - button "钉住 · 锁定位置与尺寸" [ref=e367]: + - img [ref=e368] + - generic [ref=e371]: + - generic [ref=e372]: STEP 4 · EN → ZH · 待运行 + - generic [ref=e373]: 中文翻译 · 段落级 · 实时输出 + - generic "拖动调整宽度" [ref=e375] + - generic "拖动调整大小(宽 × 高)" [ref=e376] + - group [ref=e377]: + - generic [ref=e378]: + - generic [ref=e380]: + - button "透明骷髅" [ref=e381]: + - img "透明骷髅" [ref=e382] + - button "📋" [ref=e383] + - generic: + - generic: + - generic: + - generic: 分镜 5 + - generic: 43.13s + - generic [ref=e384]: + - generic [ref=e386]: + - img [ref=e388] + - generic [ref=e393]: 元素改造 · Storyboard + - generic [ref=e394]: + - img [ref=e395] + - button "钉住 · 锁定位置与尺寸" [ref=e399]: + - img [ref=e400] + - generic [ref=e403]: + - generic [ref=e404]: STEP 6 · 参考元素 → SKG 画面 · 完成 + - generic [ref=e405]: + - text: 不是复刻原视频:先把参考图里的主体 / 场景 / 动作 / 道具拆出来,再替换成 SKG 产品画面。 + - generic [ref=e406]: 已有 1 个提取元素 · 0 个分镜进入编排 + - button "进入分镜编排" [disabled] [ref=e407] + - generic "拖动调整宽度" [ref=e409] + - generic "拖动调整大小(宽 × 高)" [ref=e410] + - group [ref=e411]: + - generic [ref=e412]: + - generic [ref=e414]: + - img [ref=e416] + - generic [ref=e420]: 产品文案 · Rewrite + - button "钉住 · 锁定位置与尺寸" [ref=e423]: + - img [ref=e424] + - generic [ref=e427]: + - generic [ref=e428]: STEP 5 · 接 SKG 卖点 · 待运行 + - textbox "粘贴 SKG 产品信息 / 关键卖点(可作为视频脚本和镜头动作参考)" [disabled] [ref=e429] + - generic [ref=e430]: 下一冲刺接入 + - generic "拖动调整宽度" [ref=e432] + - generic "拖动调整大小(宽 × 高)" [ref=e433] + - group [ref=e434]: + - generic [ref=e436]: + - generic [ref=e438]: + - img [ref=e440] + - generic [ref=e442]: 生成视频 · Video Gen + - button "钉住 · 锁定位置与尺寸" [ref=e445]: + - img [ref=e446] + - generic [ref=e449]: + - generic [ref=e450]: STEP 7 · 首帧 + 动作 prompt · 待运行 + - generic [ref=e451]: + - generic [ref=e452]: Seedance + - generic [ref=e453]: Kling + - generic [ref=e454]: Veo 3 + - generic "拖动调整宽度" [ref=e456] + - generic "拖动调整大小(宽 × 高)" [ref=e457] + - group [ref=e458]: + - generic [ref=e459]: + - generic [ref=e461]: + - img [ref=e463] + - generic [ref=e467]: 合成成品 · Compose + - button "钉住 · 锁定位置与尺寸" [ref=e470]: + - img [ref=e471] + - generic [ref=e474]: + - generic [ref=e475]: STEP 8 · ffmpeg + 字幕 · 待运行 + - generic [ref=e476]: + - text: 视频片段 + 字幕 / TTS + - text: → 最终 mp4 输出 + - generic "拖动调整宽度" [ref=e477] + - generic "拖动调整大小(宽 × 高)" [ref=e478] + - img + - generic "Control Panel" [ref=e479]: + - button "Zoom In" [ref=e480] [cursor=pointer]: + - img [ref=e481] + - button "Zoom Out" [ref=e483] [cursor=pointer]: + - img [ref=e484] + - button "Fit View" [ref=e486] [cursor=pointer]: + - img [ref=e487] + - button "Toggle Interactivity" [ref=e489] [cursor=pointer]: + - img [ref=e490] + - img "Mini Map" [ref=e493] + - region "Notifications alt+T": + - list: + - listitem [ref=e503]: + - img [ref=e505] + - generic [ref=e508]: 📥 视频已就绪 — 请点 Input 节点里的「点这里开始解析」按钮 + - alert [ref=e509] \ No newline at end of file diff --git a/web/app/page.tsx b/web/app/page.tsx index d990420..b6e9f13 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -64,6 +64,18 @@ function loadNodeSizes(): Record { } } +const NODE_PINS_KEY = "skg-tk:node-pins:v1" + +function loadNodePins(): string[] { + if (typeof window === "undefined") return [] + try { + const raw = window.localStorage.getItem(NODE_PINS_KEY) + return raw ? JSON.parse(raw) : [] + } catch { + return [] + } +} + const EDGES_RAW: Array<[string, string]> = [ ["input", "keyframe"], ["input", "asr"], @@ -412,6 +424,16 @@ export default function Home() { return () => { if (pollRef.current) clearInterval(pollRef.current) } }, [job?.id, job?.status, job?.generated_videos?.map((v) => `${v.id}:${v.status}:${v.progress}`).join("|")]) + const [pinnedNodes, setPinnedNodes] = useState>(() => new Set(loadNodePins())) + const handleToggleNodePin = useCallback((id: string) => { + setPinnedNodes((prev) => { + const next = new Set(prev) + if (next.has(id)) next.delete(id); else next.add(id) + try { window.localStorage.setItem(NODE_PINS_KEY, JSON.stringify([...next])) } catch {} + return next + }) + }, []) + const nodeData: NodeData = useMemo(() => ({ job, jobs, @@ -444,7 +466,9 @@ export default function Home() { setWorkbenchOpen(true) }, onCopyImage: handleCopyImage, - }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleSwitchJob, setJob, handleDeleteFrame, handleDeleteGenerated, handleDeleteVideo, handleCopyImage]) + pinnedNodes, + onToggleNodePin: handleToggleNodePin, + }), [job, jobs, activeJobId, submitting, analyzing, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleSwitchJob, setJob, handleDeleteFrame, handleDeleteGenerated, handleDeleteVideo, handleCopyImage, pinnedNodes, handleToggleNodePin]) // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag) const savedSizes = useMemo(() => loadNodeSizes(), []) @@ -453,12 +477,13 @@ export default function Home() { const s = savedSizes[n.id] ?? {} const w = s.w ?? n.w const h = s.h + const isPinned = pinnedNodes.has(n.id) return { id: n.id, type: n.type, position: { x: n.x, y: n.y }, data: nodeData, - draggable: true, + draggable: !isPinned, width: w, ...(typeof h === "number" ? { height: h } : {}), style: { width: w, ...(typeof h === "number" ? { height: h } : {}) }, @@ -466,6 +491,13 @@ export default function Home() { }), ) + // pinned 变化时同步每个节点 draggable + useEffect(() => { + setNodes((prev) => prev.map((n) => + n.id === KEYFRAME_PANEL_ID ? n : { ...n, draggable: !pinnedNodes.has(n.id) }, + )) + }, [pinnedNodes, setNodes]) + // 持久化每个节点宽 / 高到 localStorage(KeyframePanelNode 自己管尺寸,不写回) useEffect(() => { const sizes: Record = {} diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index f9036ae..b793a05 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -46,6 +46,8 @@ export interface NodeData { onOpenStoryboard?: (frameIdx: number) => void // 打开分镜头编排专属面板 onOpenWorkbench?: (frameIdx?: number) => void // 展开顶部分镜编排内嵌面板 onCopyImage?: (ref: ImageRef) => void // 复制图片到全局剪贴板(粘贴到分镜头编排插槽) + pinnedNodes?: Set // 已钉住的节点 id 集合 — 钉住后位置 + 尺寸锁定 + onToggleNodePin?: (id: string) => void } /* ---- 状态映射工具 ---- */ @@ -234,6 +236,8 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an subtitle={isDownloading ? "STEP 1 · 下载中" : hasVideo ? "STEP 1 · 视频就绪" : "STEP 1"} selected={selected} hasTarget={false} + pinned={d.pinnedNodes?.has("input")} + onTogglePin={() => d.onToggleNodePin?.("input")} > {/* URL + 上传入口 — 一直显示(即使已有视频,也可以继续加新的) */} <> @@ -499,6 +503,8 @@ export function KeyframeNode({ data, selected }: any) { title="镜头拆解 · 元素提取" subtitle={`STEP 2 · ${frames.length ? `${d.selectedFrames.size}/${frames.length} 入编排` : "等待抽取"}`} selected={selected} + pinned={d.pinnedNodes?.has("keyframe")} + onTogglePin={() => d.onToggleNodePin?.("keyframe")} > {frames.length > 0 ? (() => { const cleanedCount = frames.filter((x) => x.cleaned_url).length @@ -726,6 +732,8 @@ export function ASRNode({ data, selected }: any) { title="声音文案 · ASR" subtitle="STEP 3 · 可选文案轨" selected={selected} + pinned={d.pinnedNodes?.has("asr")} + onTogglePin={() => d.onToggleNodePin?.("asr")} >
Gemini 2.5 · 英文带时间戳分段 @@ -767,6 +775,8 @@ export function TranslateNode({ data, selected }: any) { title="翻译理解 · Translate" subtitle="STEP 4 · EN → ZH" selected={selected} + pinned={d.pinnedNodes?.has("translate")} + onTogglePin={() => d.onToggleNodePin?.("translate")} >
中文翻译 · 段落级 · 实时输出 @@ -785,7 +795,8 @@ export function TranslateNode({ data, selected }: any) { /* ============================================================ 7. RewriteNode (placeholder) ============================================================ */ -export function RewriteNode({ selected }: any) { +export function RewriteNode({ data, selected }: any) { + const d: NodeData = data return ( d.onToggleNodePin?.("rewrite")} >