Skip to content

Commit dded950

Browse files
Merge branch 'main' into cursor/cve/opentelemetry-core
2 parents cb81431 + efcabdf commit dded950

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828
- Passed Zoekt index parameters via argv to preserve revision names with punctuation. [#1376](https://github.com/sourcebot-dev/sourcebot/pull/1376)
2929
- [EE] Validated OAuth bearer token scopes before allowing access to the Sourcebot MCP resource server. [#1396](https://github.com/sourcebot-dev/sourcebot/pull/1396)
3030
- Added HTTP security headers to all web app responses. [#1407](https://github.com/sourcebot-dev/sourcebot/pull/1407)
31+
- Maintained the sidebar scroll position when navigating between chats instead of resetting to the top. [#1411](https://github.com/sourcebot-dev/sourcebot/pull/1411)
3132
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
3233
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
3334

packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,18 @@ import { signOut } from "next-auth/react";
4040
import { useTheme } from "next-themes";
4141
import Link from "next/link";
4242
import posthog from "posthog-js";
43-
import { ReactNode, useEffect, useMemo, useRef, useState } from "react";
43+
import { ReactNode, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
4444
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
4545
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
4646
import { Separator } from "@/components/ui/separator";
4747
import { WhatsNewSidebarButton } from "./whatsNewSidebarButton";
4848
import { UpgradeButton } from "./upgradeButton";
4949
import { useIsMobile } from "@/hooks/use-mobile";
5050

51+
// Retains the sidebar's scroll position across route-driven remounts of the
52+
// `@sidebar` slot. Module-level so it survives remounts but resets on reload.
53+
let lastScrollTop = 0;
54+
5155
interface SidebarBaseProps {
5256
session: Session | null;
5357
collapsible?: "icon" | "offcanvas" | "none";
@@ -61,12 +65,27 @@ export function SidebarBase({ session, collapsible = "icon", headerContent, chil
6165
const contentRef = useRef<HTMLDivElement>(null);
6266
const isMobile = useIsMobile();
6367

68+
// The sidebar lives in the `@sidebar` parallel route slot, whose catch-all
69+
// segment remounts on navigation between chats. Persist the scroll position
70+
// across those remounts so it isn't reset to the top each time.
71+
useLayoutEffect(() => {
72+
const el = contentRef.current;
73+
if (!el) {
74+
return;
75+
}
76+
el.scrollTop = lastScrollTop;
77+
setIsScrolled(el.scrollTop > 0);
78+
}, []);
79+
6480
useEffect(() => {
6581
const el = contentRef.current;
6682
if (!el) {
6783
return;
6884
}
69-
const handleScroll = () => setIsScrolled(el.scrollTop > 0);
85+
const handleScroll = () => {
86+
lastScrollTop = el.scrollTop;
87+
setIsScrolled(el.scrollTop > 0);
88+
};
7089
el.addEventListener("scroll", handleScroll);
7190
return () => el.removeEventListener("scroll", handleScroll);
7291
}, []);

0 commit comments

Comments
 (0)