diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx
index 4da6d20d..2b25d70e 100644
--- a/src/pages/Settings.tsx
+++ b/src/pages/Settings.tsx
@@ -54,6 +54,7 @@ function SettingsLayout(props: {
onSearchUnFocus: (newSearch?: string) => void;
selectedCategory: string | null;
setSelectedCategory: (category: string | null) => void;
+ onCategoryChange?: (category: string | null) => void;
}) {
const { className } = props;
const { t } = useTranslation();
@@ -105,6 +106,7 @@ function SettingsLayout(props: {
{props.children}
@@ -168,7 +170,6 @@ export function AccountSettings(props: {
export function SettingsPage() {
const [searchQuery, setSearchQuery] = useState("");
const [selectedCategory, setSelectedCategory] = useState(null);
- const prevCategoryRef = useRef(null);
const backendChangeModal = useModal("settings-backend-change-confirmation");
const [pendingBackendChange, setPendingBackendChange] = useState<
string | null
@@ -271,22 +272,6 @@ export function SettingsPage() {
};
}, []);
- // Scroll to top when category changes (but not on initial load or when searching)
- useEffect(() => {
- if (
- prevCategoryRef.current !== null &&
- prevCategoryRef.current !== selectedCategory &&
- !searchQuery.trim()
- ) {
- // Only scroll to top if we're actually switching categories (not initial load)
- // Use requestAnimationFrame to ensure DOM has updated
- requestAnimationFrame(() => {
- window.scrollTo({ top: 0, behavior: "smooth" });
- });
- }
- prevCategoryRef.current = selectedCategory;
- }, [selectedCategory, searchQuery]);
-
const { t } = useTranslation();
const activeTheme = useThemeStore((s) => s.theme);
const setTheme = useThemeStore((s) => s.setTheme);
@@ -381,6 +366,21 @@ export function SettingsPage() {
}
}, []);
+ const handleCategoryChange = useCallback(
+ (category: string | null) => {
+ if (searchQuery.trim()) return;
+ const sectionId = category ?? "settings-account";
+ setTimeout(() => {
+ scrollToElement(`#${sectionId}`, {
+ behavior: "smooth",
+ block: "start",
+ offset: 120, // Account for fixed search bar
+ });
+ }, 100); // Wait for section to render after tab switch
+ },
+ [searchQuery],
+ );
+
const appLanguage = useLanguageStore((s) => s.language);
const setAppLanguage = useLanguageStore((s) => s.setLanguage);
@@ -983,6 +983,7 @@ export function SettingsPage() {
onSearchUnFocus={handleSearchUnFocus}
selectedCategory={selectedCategory}
setSelectedCategory={setSelectedCategory}
+ onCategoryChange={handleCategoryChange}
className="space-y-28"
>
{(searchQuery.trim() ||
diff --git a/src/pages/parts/settings/SidebarPart.tsx b/src/pages/parts/settings/SidebarPart.tsx
index 0bada997..57f7d6f2 100644
--- a/src/pages/parts/settings/SidebarPart.tsx
+++ b/src/pages/parts/settings/SidebarPart.tsx
@@ -11,6 +11,7 @@ import { AppInfoPart } from "./AppInfoPart";
export function SidebarPart(props: {
selectedCategory: string | null;
setSelectedCategory: (category: string | null) => void;
+ onCategoryChange?: (category: string | null) => void;
searchQuery: string;
}) {
const { t } = useTranslation();
@@ -102,6 +103,7 @@ export function SidebarPart(props: {
// Set the selected category when clicking a sidebar link
// null means "All Settings" - show all sections
props.setSelectedCategory(id);
+ props.onCategoryChange?.(id);
},
[props],
);