diff --git a/frontend/src/core/pages/HomePage.tsx b/frontend/src/core/pages/HomePage.tsx index f99cbfc8b..9bbe90391 100644 --- a/frontend/src/core/pages/HomePage.tsx +++ b/frontend/src/core/pages/HomePage.tsx @@ -22,6 +22,7 @@ import FileManager from "@app/components/FileManager"; import LocalIcon from "@app/components/shared/LocalIcon"; import { useFilesModalContext } from "@app/contexts/FilesModalContext"; import AppConfigModal from "@app/components/shared/AppConfigModal"; +import { getStartupNavigationAction } from "@app/utils/homePageNavigation"; import "@app/pages/HomePage.css"; @@ -60,22 +61,37 @@ export default function HomePage() { const { setActiveFileIndex } = useViewer(); const prevFileCountRef = useRef(activeFiles.length); - // Auto-switch to viewer when going from 0 to 1 file - // Skip this if PDF Text Editor is active - it handles its own empty state + // Startup/open transition behavior: + // - opening exactly 1 file from empty -> viewer (unless already in fileEditor) + // - opening 2+ files from empty -> fileEditor useEffect(() => { const prevCount = prevFileCountRef.current; const currentCount = activeFiles.length; - if ( - navigationState.workbench !== 'fileEditor' && - prevCount === 0 && - currentCount === 1 - ) { - // PDF Text Editor handles its own empty state with a dropzone - if (selectedToolKey !== 'pdfTextEditor') { - actions.setWorkbench('viewer'); - setActiveFileIndex(0); + console.log('[HomePage] Navigation effect triggered:', { + prevCount, + currentCount, + currentWorkbench: navigationState.workbench, + selectedToolKey, + }); + + const action = getStartupNavigationAction( + prevCount, + currentCount, + selectedToolKey, + navigationState.workbench + ); + + console.log('[HomePage] Navigation action returned:', action); + + if (action) { + console.log('[HomePage] Applying navigation:', action); + actions.setWorkbench(action.workbench); + if (typeof action.activeFileIndex === 'number') { + setActiveFileIndex(action.activeFileIndex); } + } else { + console.log('[HomePage] No navigation - staying in current workbench'); } prevFileCountRef.current = currentCount; diff --git a/frontend/src/core/types/workbench.ts b/frontend/src/core/types/workbench.ts index 6fdcca858..d34f942ea 100644 --- a/frontend/src/core/types/workbench.ts +++ b/frontend/src/core/types/workbench.ts @@ -6,7 +6,7 @@ export type BaseWorkbenchType = typeof BASE_WORKBENCH_TYPES[number]; // Workbench types including custom views export type WorkbenchType = BaseWorkbenchType | `custom:${string}`; -export const getDefaultWorkbench = (): WorkbenchType => 'fileEditor'; +export const getDefaultWorkbench = (): WorkbenchType => 'viewer'; // Type guard using the same source of truth export const isValidWorkbench = (value: string): value is WorkbenchType => { diff --git a/frontend/src/core/utils/homePageNavigation.test.ts b/frontend/src/core/utils/homePageNavigation.test.ts new file mode 100644 index 000000000..e6cadf79e --- /dev/null +++ b/frontend/src/core/utils/homePageNavigation.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, it } from 'vitest'; +import { getStartupNavigationAction } from '@app/utils/homePageNavigation'; +import type { WorkbenchType } from '@app/types/workbench'; + +describe('getStartupNavigationAction', () => { + it('returns viewer + active index for 0->1 transition when not in fileEditor', () => { + expect(getStartupNavigationAction(0, 1, null, 'viewer' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + expect(getStartupNavigationAction(0, 1, null, 'pageEditor' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + }); + + it('returns fileEditor for 0->2+ transition when not in fileEditor', () => { + expect(getStartupNavigationAction(0, 2, null, 'viewer' as WorkbenchType)).toEqual({ + workbench: 'fileEditor', + }); + }); + + it('does not force navigation for pdfTextEditor', () => { + expect(getStartupNavigationAction(0, 1, 'pdfTextEditor', 'viewer' as WorkbenchType)).toBeNull(); + expect(getStartupNavigationAction(0, 3, 'pdfTextEditor', 'viewer' as WorkbenchType)).toBeNull(); + }); + + it('does not navigate on non-startup transitions', () => { + expect(getStartupNavigationAction(1, 2, null, 'viewer' as WorkbenchType)).toBeNull(); + expect(getStartupNavigationAction(2, 1, null, 'viewer' as WorkbenchType)).toBeNull(); + }); + + it('does not navigate when user already has files (N→M transitions)', () => { + // User has 1 file, adds another -> no navigation (stay in current workbench) + expect(getStartupNavigationAction(1, 2, null, 'viewer' as WorkbenchType)).toBeNull(); + expect(getStartupNavigationAction(1, 2, null, 'fileEditor' as WorkbenchType)).toBeNull(); + + // User has 3 files, adds more -> no navigation + expect(getStartupNavigationAction(3, 4, null, 'fileEditor' as WorkbenchType)).toBeNull(); + + // User has 2 files, deletes 1 -> no navigation + expect(getStartupNavigationAction(2, 1, null, 'viewer' as WorkbenchType)).toBeNull(); + }); + + it('handles all workbench types consistently for 0→N transitions', () => { + // 0→1 always goes to viewer regardless of current workbench (since default is viewer) + expect(getStartupNavigationAction(0, 1, null, 'viewer' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + expect(getStartupNavigationAction(0, 1, null, 'fileEditor' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + expect(getStartupNavigationAction(0, 1, null, 'pageEditor' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + expect(getStartupNavigationAction(0, 1, null, 'custom:formFill' as WorkbenchType)).toEqual({ + workbench: 'viewer', + activeFileIndex: 0, + }); + + // 0→N (N>1) always goes to fileEditor + expect(getStartupNavigationAction(0, 3, null, 'viewer' as WorkbenchType)).toEqual({ + workbench: 'fileEditor', + }); + expect(getStartupNavigationAction(0, 3, null, 'custom:myTool' as WorkbenchType)).toEqual({ + workbench: 'fileEditor', + }); + }); +}); diff --git a/frontend/src/core/utils/homePageNavigation.ts b/frontend/src/core/utils/homePageNavigation.ts new file mode 100644 index 000000000..02f161643 --- /dev/null +++ b/frontend/src/core/utils/homePageNavigation.ts @@ -0,0 +1,49 @@ +import type { WorkbenchType } from '@app/types/workbench'; + +export type StartupWorkbench = 'viewer' | 'fileEditor'; + +export interface StartupNavigationAction { + workbench: StartupWorkbench; + activeFileIndex?: number; +} + +export function getStartupNavigationAction( + previousFileCount: number, + currentFileCount: number, + selectedToolKey: string | null, + currentWorkbench: WorkbenchType +): StartupNavigationAction | null { + console.log('[homePageNavigation] Called with:', { + previousFileCount, + currentFileCount, + selectedToolKey, + currentWorkbench, + }); + + // pdfTextEditor handles its own empty state + if (selectedToolKey === 'pdfTextEditor') { + console.log('[homePageNavigation] pdfTextEditor detected, returning null'); + return null; + } + + // Only handle transitions from empty (0 files) to some files + if (previousFileCount !== 0) { + console.log('[homePageNavigation] Not a 0→N transition, returning null'); + return null; + } + + // 0→1: Go to viewer to view the single file + if (currentFileCount === 1) { + console.log('[homePageNavigation] 0→1 transition, returning viewer'); + return { workbench: 'viewer', activeFileIndex: 0 }; + } + + // 0→N (N>1): Go to fileEditor to manage multiple files + if (currentFileCount > 1) { + console.log('[homePageNavigation] 0→N transition, returning fileEditor'); + return { workbench: 'fileEditor' }; + } + + console.log('[homePageNavigation] Still at 0 files, returning null'); + return null; +}