From 616c041626008ce89d72b141bf1ce538c224af0f Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Mon, 2 Feb 2026 19:21:47 -0600 Subject: [PATCH] fix(tabs): scroll file preview tabs into view when selected The TabBar scroll-into-view effect only triggered for AI tabs. Added activeFileTabId to the useEffect dependencies so file preview tabs are also scrolled into view when opened. --- .../renderer/components/TabBar.test.tsx | 64 +++++++++++++++++++ src/renderer/components/TabBar.tsx | 8 ++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/__tests__/renderer/components/TabBar.test.tsx b/src/__tests__/renderer/components/TabBar.test.tsx index bbc8ab35..94115a13 100644 --- a/src/__tests__/renderer/components/TabBar.test.tsx +++ b/src/__tests__/renderer/components/TabBar.test.tsx @@ -1527,6 +1527,70 @@ describe('TabBar', () => { rafSpy.mockRestore(); }); + + it('scrolls to center file tab when activeFileTabId changes', async () => { + // Mock requestAnimationFrame + const rafSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { + cb(0); + return 0; + }); + const scrollToSpy = vi.fn(); + + const tabs = [createTab({ id: 'tab-1', name: 'Tab 1' })]; + const fileTab: FilePreviewTab = { + id: 'file-1', + path: '/path/to/file.ts', + name: 'file', + extension: '.ts', + }; + const unifiedTabs = [ + { id: 'tab-1', type: 'ai' as const, data: tabs[0] }, + { id: 'file-1', type: 'file' as const, data: fileTab }, + ]; + + const { rerender, container } = render( + + ); + + // Mock scrollTo on the container + const tabBarContainer = container.firstChild as HTMLElement; + tabBarContainer.scrollTo = scrollToSpy; + + // Clear initial calls + scrollToSpy.mockClear(); + + // Select the file tab - this should trigger scroll to file tab + rerender( + + ); + + // scrollTo should have been called when file tab was selected + expect(scrollToSpy).toHaveBeenCalled(); + + rafSpy.mockRestore(); + }); }); describe('styling', () => { diff --git a/src/renderer/components/TabBar.tsx b/src/renderer/components/TabBar.tsx index 82cb2106..dc0d63c8 100644 --- a/src/renderer/components/TabBar.tsx +++ b/src/renderer/components/TabBar.tsx @@ -1598,12 +1598,14 @@ function TabBarInner({ const tabRefs = useRef>(new Map()); const [isOverflowing, setIsOverflowing] = useState(false); - // Center the active tab in the scrollable area when activeTabId changes or filter is toggled + // Center the active tab in the scrollable area when activeTabId or activeFileTabId changes, or filter is toggled useEffect(() => { requestAnimationFrame(() => { const container = tabBarRef.current; + // When a file tab is active, scroll to it; otherwise scroll to the active AI tab + const targetTabId = activeFileTabId || activeTabId; const tabElement = container?.querySelector( - `[data-tab-id="${activeTabId}"]` + `[data-tab-id="${targetTabId}"]` ) as HTMLElement | null; if (container && tabElement) { // Calculate scroll position to center the tab @@ -1612,7 +1614,7 @@ function TabBarInner({ container.scrollTo({ left: scrollLeft, behavior: 'smooth' }); } }); - }, [activeTabId, showUnreadOnly]); + }, [activeTabId, activeFileTabId, showUnreadOnly]); // Can always close tabs - closing the last one creates a fresh new tab const canClose = true;