import React, { useEffect, useRef } from 'react'; import { Scroll } from 'folds'; import classNames from 'classnames'; import { Sidebar, SidebarContent, SidebarStackSeparator, SidebarStack, } from '../../components/sidebar'; import { SidebarGlass } from '../../components/sidebar/Sidebar.css'; import { DirectTab, HomeTab, SpaceTabs, InboxTab, ExploreTab, SettingsTab, UnverifiedTab, SearchTab, BookmarksTab, } from './sidebar'; import { CreateTab } from './sidebar/CreateTab'; import { useSetting } from '../../state/hooks/settings'; import { settingsAtom } from '../../state/settings'; import { useTheme, ThemeKind } from '../../hooks/useTheme'; import { useReducedMotion } from '../../hooks/useReducedMotion'; import { getChatBg } from '../../features/lotus/chatBackground'; export function SidebarNav() { const scrollRef = useRef(null) as React.RefObject; const [glassmorphismSidebar] = useSetting(settingsAtom, 'glassmorphismSidebar'); const [chatBackground] = useSetting(settingsAtom, 'chatBackground'); const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal'); const [pauseAnimations] = useSetting(settingsAtom, 'pauseAnimations'); const theme = useTheme(); const isDark = theme.kind === ThemeKind.Dark; const reduced = useReducedMotion(); // backdrop-filter only blurs content directly behind the element in the z-axis. // The sidebar is a flex sibling of the room view, so nothing sits behind it by default. // Fix: mirror the active chat background onto document.body so the sidebar blurs through it. useEffect(() => { const { style } = document.body; const hasBg = chatBackground !== 'none' || glassmorphismSidebar || lotusTerminal; if (!hasBg) { style.removeProperty('background-image'); style.removeProperty('background-color'); style.removeProperty('background-size'); style.removeProperty('background-position'); style.removeProperty('animation'); style.removeProperty('will-change'); return; } const effectiveBg = chatBackground !== 'none' ? chatBackground : 'tactical'; const bgStyle = getChatBg(effectiveBg, isDark, pauseAnimations || reduced); style.backgroundImage = (bgStyle.backgroundImage as string | undefined) ?? ''; style.backgroundColor = (bgStyle.backgroundColor as string | undefined) ?? ''; style.backgroundSize = (bgStyle.backgroundSize as string | undefined) ?? ''; style.backgroundPosition = (bgStyle.backgroundPosition as string | undefined) ?? ''; // The animated body mirror (animation + will-change) exists solely so the // glassmorphism sidebar can blur through document.body. When glass is OFF nothing // samples this layer, yet SidebarNav is always mounted, so writing an animated bg + // will-change here would leave a permanent invisible animated compositor layer // app-wide. Only mirror the animation when glass is on; the static background above // (needed by lotusTerminal / non-animated cases) is still written regardless. if (glassmorphismSidebar) { style.animation = (bgStyle.animation as string | undefined) ?? ''; if (bgStyle.animation) { style.willChange = 'background-position, background-size'; } else { style.removeProperty('will-change'); } } else { style.removeProperty('animation'); style.removeProperty('will-change'); } return () => { style.removeProperty('background-image'); style.removeProperty('background-color'); style.removeProperty('background-size'); style.removeProperty('background-position'); style.removeProperty('animation'); style.removeProperty('will-change'); }; }, [glassmorphismSidebar, chatBackground, lotusTerminal, isDark, pauseAnimations, reduced]); return ( } sticky={ <> } /> ); }