diff --git a/apps/web/src/components/AppSidebarLayout.tsx b/apps/web/src/components/AppSidebarLayout.tsx index a3ba76679689..0bc21fbe3f69 100644 --- a/apps/web/src/components/AppSidebarLayout.tsx +++ b/apps/web/src/components/AppSidebarLayout.tsx @@ -23,7 +23,7 @@ import { resolveSidebarStageFocusRingOffsetClass, useSidebarStageBackdropVariant, } from "./SidebarStageBackdrop"; -import { useProjects } from "../state/entities"; +import { useProjects, useThreadShells } from "../state/entities"; import { resolveInitialThreadSidebarWidth, resolveThreadSidebarMaximumWidth, @@ -128,11 +128,12 @@ function SidebarControl() { ); } -// Settings swaps the thread sidebar out of the tree. Keep the lightweight -// project projection subscribed so returning to a draft never renders the -// zero-project state while the environment snapshot reconnects. -function ProjectProjectionRetention() { +// Settings swaps the thread sidebar out of the tree. Keep its lightweight +// projections subscribed so returning never briefly renders empty project or +// thread lists while the environment snapshot reconnects. +function SidebarProjectionRetention() { useProjects(); + useThreadShells(); return null; } @@ -210,7 +211,7 @@ export function AppSidebarLayout({ children }: { children: ReactNode }) { return ( - + | null>(null); + const listAutoAnimateAttachFrameRef = useRef(null); const attachListAutoAnimateRef = useCallback((node: HTMLUListElement | null) => { + if (listAutoAnimateAttachFrameRef.current !== null) { + window.cancelAnimationFrame(listAutoAnimateAttachFrameRef.current); + listAutoAnimateAttachFrameRef.current = null; + } + listAutoAnimateControllerRef.current?.destroy?.(); + listAutoAnimateControllerRef.current = null; if (!node) return; - autoAnimate(node, { duration: 150, easing: "ease-out" }); + + // DndContext installs its accessibility nodes just after this list mounts. + // Attach one frame later so those setup mutations are ignored without + // canceling auto-animate's initial coordinate tracking. + listAutoAnimateAttachFrameRef.current = window.requestAnimationFrame(() => { + listAutoAnimateAttachFrameRef.current = null; + if (!node.isConnected) return; + listAutoAnimateControllerRef.current = autoAnimate(node, { + duration: 150, + easing: "ease-out", + }); + }); }, []); // New thread defaults to the project you're in (active thread's project,