Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ interface SidebarThreadPartitionInput {
readonly clockKey: string;
}

function scrollSidebarToTop(content: HTMLElement | null) {
content?.closest("[data-slot=scroll-area-viewport]")?.scrollTo({ top: 0 });
}

function threadShellKey(thread: Pick<SidebarThreadSummary, "environmentId" | "id">): string {
return scopedThreadKey(scopeThreadRef(thread.environmentId, thread.id));
}
Expand Down Expand Up @@ -1019,6 +1023,7 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
const [renamingTitle, setRenamingTitle] = useState("");
const settlingThreadKeysRef = useRef(new Set<string>());
const threadListRef = useRef<HTMLUListElement | null>(null);
const sidebarContentRef = useRef<HTMLDivElement | null>(null);
const dragLabelOffsetRef = useRef(0);
const listMotionRef = useRef<ReturnType<typeof createSidebarListMotion> | null>(null);
const [dragState, setDragState] = useState<{
Expand All @@ -1044,9 +1049,11 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
const routeThreadKey = routeThreadRef ? scopedThreadKey(routeThreadRef) : null;

// A selection made under one scope can hold threads the next scope hides.
// The viewport outlives the scope and does not anchor, so a new list starts at the top.
const setProjectScopeKeys = (keys: readonly string[]) => {
clearSelection();
setPersistedProjectScopeKeys(keys);
scrollSidebarToTop(sidebarContentRef.current);
};
// A scope whose project is gone reads as all projects once every snapshot has
// loaded. The stored keys are left alone, so the scope returns with its project.
Expand Down Expand Up @@ -2894,6 +2901,7 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
<ThreadContextDragGhost />
<SidebarChromeHeader isElectron={isElectron} />
<SidebarContent
ref={sidebarContentRef}
className="min-h-full"
fixedHeader={
<SidebarGroup className="z-[1]">
Expand Down
Loading