From 10994df952cf901fbd4ed8c2137d8ff90fa81cff Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Fri, 24 Apr 2026 20:08:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20v0.19.4=20=E7=94=9F=E6=88=90=E9=A1=B5?= =?UTF-8?q?=E5=BE=80=E4=B8=8A=E7=BF=BB=E5=8A=A0=E8=BD=BD=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E6=97=B6=E4=B8=8D=E5=86=8D=E8=B7=B3=E5=88=B0=E5=BA=95=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象: 用户往上翻看历史, loadMore 往 tasks 数组头部 prepend 老任务后, 页面自动跳到最底, 打断浏览。 根因 VideoGenerationPage.tsx useEffect 依赖 tasks.length, 只要数量 增加就 scrollTo(scrollHeight)。这个逻辑本意是"新任务生成 push 到 末尾时滚到底", 但区分不出"头部 prepend" vs "尾部 push", 于是 loadMore 也触发了滚底。handleScroll 里的 anchor(scrollTop += diff) 本来会做视觉保位, 但 useEffect 的 scrollTo smooth 抢先/盖过它。 修复 改成比末尾 task 的 id 是否变化 (prevLastIdRef) 而非 length - 新任务 push 到末尾 → 末尾 id 变 → 滚到底 ✅ - 头部加载历史 → 末尾 id 不变 → 保位置 ✅ - 轮询更新任务属性(如生成完成) → 数组内容变但末尾 id 不变 → 不打扰 ✅ - 删除某条任务(非末尾) → 末尾 id 不变 → 不滚 ✅ - 只有"末尾多了新东西"这一种情况才滚底, 符合用户直觉 验证 vitest 71 failed 全部为 v0.18.x 以来的 preexisting 失败 (phase2/phase3 路径解析问题), stash 前后对比完全一致, 本次改动 零新增回归。 Co-Authored-By: Claude Opus 4.7 (1M context) --- web/src/components/VideoGenerationPage.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/web/src/components/VideoGenerationPage.tsx b/web/src/components/VideoGenerationPage.tsx index b99080f..26f6b24 100644 --- a/web/src/components/VideoGenerationPage.tsx +++ b/web/src/components/VideoGenerationPage.tsx @@ -20,7 +20,7 @@ export function VideoGenerationPage() { const regenerate = useGenerationStore((s) => s.regenerate); const removeTask = useGenerationStore((s) => s.removeTask); const scrollRef = useRef(null); - const prevCountRef = useRef(tasks.length); + const prevLastIdRef = useRef(null); const initialLoadRef = useRef(true); const savedScrollTop = useGenerationStore((s) => s.savedScrollTop); const saveScrollPosition = useGenerationStore((s) => s.saveScrollPosition); @@ -36,9 +36,14 @@ export function VideoGenerationPage() { loadTasks(); }, [loadTasks]); - // Restore scroll position after initial load, or scroll to bottom for new tasks + // Restore scroll position after initial load, or scroll to bottom ONLY when a new task + // is appended at the tail. 通过比较末尾 task 的 id 来判断 —— 头部加载历史(prepend)、 + // 任务状态更新(如轮询完成)、删除某条都不会改变末尾 id,因此不会触发滚动, + // 避免用户往上翻时被突然拽回底部。 useEffect(() => { if (tasks.length === 0) return; + const currentLastId = tasks[tasks.length - 1]?.id ?? null; + if (initialLoadRef.current) { initialLoadRef.current = false; // Use requestAnimationFrame to ensure DOM has rendered @@ -50,15 +55,16 @@ export function VideoGenerationPage() { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }); - prevCountRef.current = tasks.length; + prevLastIdRef.current = currentLastId; return; } - if (tasks.length > prevCountRef.current && scrollRef.current) { + + if (currentLastId !== prevLastIdRef.current && scrollRef.current) { scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' }); } - prevCountRef.current = tasks.length; + prevLastIdRef.current = currentLastId; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [tasks.length]); + }, [tasks]); // Save scroll position + auto-load older tasks when scrolled near top const handleScroll = useCallback(() => {