diff --git a/packages/ui/src/components/layout/RightSidebarTabs.tsx b/packages/ui/src/components/layout/RightSidebarTabs.tsx index 733b3ad9..cc25c247 100644 --- a/packages/ui/src/components/layout/RightSidebarTabs.tsx +++ b/packages/ui/src/components/layout/RightSidebarTabs.tsx @@ -4,13 +4,44 @@ import { RiFolder3Line, RiGitBranchLine } from '@remixicon/react'; import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip'; import { GitView } from '@/components/views'; import { useUIStore } from '@/stores/useUIStore'; +import { useGitStore } from '@/stores/useGitStore'; +import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; +import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { SidebarFilesTree } from './SidebarFilesTree'; type RightTab = 'git' | 'files'; +/** + * Keeps git status fresh while the right sidebar is open. + * Replaces the GitPollingProvider removed in commit b2d5ccb4. + * The previous polling ran globally; now we only refresh when the sidebar is open. + */ +function useRightSidebarGitSync(directory: string | undefined, isSidebarOpen: boolean) { + const { git } = useRuntimeAPIs(); + const ensureStatus = useGitStore((state) => state.ensureStatus); + + React.useEffect(() => { + if (!directory || !git || !isSidebarOpen) return; + + void ensureStatus(directory, git); + + const POLL_INTERVAL = 10_000; + const id = setInterval(() => { + if (typeof document !== 'undefined' && document.hidden) return; + void ensureStatus(directory, git); + }, POLL_INTERVAL); + + return () => clearInterval(id); + }, [directory, git, isSidebarOpen, ensureStatus]); +} + export const RightSidebarTabs: React.FC = () => { const rightSidebarTab = useUIStore((state) => state.rightSidebarTab); const setRightSidebarTab = useUIStore((state) => state.setRightSidebarTab); + const isRightSidebarOpen = useUIStore((state) => state.isRightSidebarOpen); + const directory = useEffectiveDirectory(); + + useRightSidebarGitSync(directory, isRightSidebarOpen); const tabItems = React.useMemo(() => [ { @@ -44,4 +75,4 @@ export const RightSidebarTabs: React.FC = () => { ); -}; +}; \ No newline at end of file diff --git a/packages/ui/src/components/views/git/ChangesSection.tsx b/packages/ui/src/components/views/git/ChangesSection.tsx index 824ac6e8..b068e0fb 100644 --- a/packages/ui/src/components/views/git/ChangesSection.tsx +++ b/packages/ui/src/components/views/git/ChangesSection.tsx @@ -67,9 +67,36 @@ export const ChangesSection: React.FC = ({ enabled: shouldVirtualize, }); + // Force virtualizer to remeasure when the scroll container transitions + // from display:none (hidden tab via keep-alive) back to visible layout. + // Without this, the virtualizer uses stale zero-height measurements and + // renders no rows until the user scrolls. + React.useEffect(() => { + if (!shouldVirtualize) return; + const el = scrollRef.current; + if (!el) return; + + const observer = new ResizeObserver(() => { + rowVirtualizer.measure(); + }); + observer.observe(el); + return () => observer.disconnect(); + }, [shouldVirtualize, rowVirtualizer]); + + // Compute virtual rows with useMemo. We include totalSize as a dependency so + // that when the ResizeObserver calls measure() — which clears the itemSizeCache + // and recalculates — the size change invalidates the memo and getVirtualItems() + // returns fresh rows. Using useMemo avoids calling getVirtualItems() directly in + // the render body, which can trigger maybeNotify() → onChange() → useReducer + // dispatch during render (React minified error #185). + const totalSize = rowVirtualizer.getTotalSize(); const virtualRows = React.useMemo( - () => (shouldVirtualize ? rowVirtualizer.getVirtualItems() : []), - [rowVirtualizer, shouldVirtualize], + // totalSize invalidates the memo when the virtualizer recalculates after + // measure/scroll, ensuring getVirtualItems() returns up-to-date rows. + // Without it, the stable rowVirtualizer ref would never invalidate the memo + // and rows would stay empty after measure(). + () => (shouldVirtualize && totalSize >= 0 ? rowVirtualizer.getVirtualItems() : []), + [shouldVirtualize, rowVirtualizer, totalSize], ); React.useEffect(() => {