Spaces:
Sleeping
Sleeping
| import 'katex/dist/katex.min.css'; | |
| import { useState, useEffect } from 'react'; | |
| // Import custom hooks | |
| import { useDocumentProcessor } from '../hooks/useDocumentProcessor'; | |
| import { useChunkNavigation } from '../hooks/useChunkNavigation'; | |
| import { usePanelResize } from '../hooks/usePanelResize'; | |
| // Import components | |
| import LoadingAnimation from './LoadingAnimation'; | |
| import DocumentViewer from './DocumentViewer'; | |
| import ChunkPanel from './ChunkPanel'; | |
| import ProgressBar from './ProgressBar'; | |
| import WelcomeScreen from './WelcomeScreen'; | |
| import OnboardingWizard from './OnboardingWizard'; | |
| import Highlights from '../highlights.json'; | |
| function DocumentProcessor({ initialFile, fileName, academicBackground }) { | |
| const onboardingData = { | |
| hasFile: !!initialFile, | |
| fileName, | |
| academicBackground | |
| }; | |
| // State for PDF navigation | |
| const [pdfNavigation, setPdfNavigation] = useState(null); | |
| // State for first LLM response loading | |
| const [waitingForFirstResponse, setWaitingForFirstResponse] = useState(false); | |
| // State for welcome screen visibility - if coming from onboarding, skip welcome | |
| const [showWelcomeScreen, setShowWelcomeScreen] = useState(!onboardingData?.hasFile); | |
| // State for document controls (like scrollToPage) | |
| const [documentControls, setDocumentControls] = useState(null); | |
| // State for onboarding wizard | |
| const [showOnboarding, setShowOnboarding] = useState(!!onboardingData?.hasFile); | |
| const [onboardingCompleted, setOnboardingCompleted] = useState(false); | |
| // Custom hooks | |
| const { | |
| fileInputRef, | |
| selectedFile, | |
| processing, | |
| uploadProgress, | |
| documentData, | |
| handleFileChange, | |
| processDocument, | |
| setSelectedFile | |
| } = useDocumentProcessor(); | |
| // Auto-load document when coming from onboarding | |
| useEffect(() => { | |
| if (onboardingData?.hasFile && initialFile && !selectedFile && !documentData) { | |
| // Use the actual uploaded file | |
| setSelectedFile(initialFile); | |
| setTimeout(() => { | |
| processDocument(); | |
| }, 500); | |
| } | |
| }, [onboardingData, selectedFile, documentData, setSelectedFile, processDocument, initialFile]); | |
| // Function to get the page number of the first chunk | |
| const getFirstChunkPage = () => { | |
| if (testPreloadedHighlights && testPreloadedHighlights[0] && testPreloadedHighlights[0].length > 0) { | |
| return testPreloadedHighlights[0][0].position.boundingRect.pageNumber; | |
| } | |
| return 1; // Default to page 1 if no highlights found | |
| }; | |
| // Function to handle "Let's start" click | |
| const handleGetStarted = () => { | |
| setShowWelcomeScreen(false); | |
| // Scroll to the first chunk after a short delay to ensure the PDF viewer is ready | |
| if (documentControls && documentControls.scrollToFirstChunk) { | |
| setTimeout(() => { | |
| documentControls.scrollToFirstChunk(); | |
| }, 500); | |
| } else { | |
| // Retry after a longer delay | |
| setTimeout(() => { | |
| if (documentControls && documentControls.scrollToFirstChunk) { | |
| documentControls.scrollToFirstChunk(); | |
| } | |
| }, 2000); | |
| } | |
| }; | |
| // Function to handle onboarding completion | |
| const handleOnboardingComplete = (onboardingPreferences) => { | |
| console.log('Onboarding preferences:', onboardingPreferences); | |
| setShowOnboarding(false); | |
| setOnboardingCompleted(true); | |
| // TODO: Store onboarding preferences for use in tutoring | |
| // Scroll to the first chunk after a short delay | |
| if (documentControls && documentControls.scrollToFirstChunk) { | |
| setTimeout(() => { | |
| documentControls.scrollToFirstChunk(); | |
| }, 500); | |
| } | |
| }; | |
| const { | |
| chunkStates, | |
| currentChunkIndex, | |
| chunkExpanded, | |
| showChat, | |
| goToNextChunk, | |
| goToPrevChunk, | |
| skipChunk, | |
| markChunkUnderstood, | |
| startInteractiveLesson, | |
| setChunkExpanded, | |
| setShowChat, | |
| setChunkAsInteractive, | |
| updateGlobalChatHistory, | |
| getGlobalChatHistory, | |
| addMessageToChunk, | |
| getCurrentChunkMessages, | |
| hasChunkMessages, | |
| isChunkLoading, | |
| streamResponse | |
| } = useChunkNavigation(documentData); | |
| const { | |
| leftPanelWidth, | |
| isDragging, | |
| containerRef, | |
| handleMouseDown | |
| } = usePanelResize(50); | |
| // Add test preloaded highlights data - keyed by chunk index | |
| // Lennart version | |
| const testPreloadedHighlights = Highlights; | |
| // Temporarily inject test highlights into documentData for testing | |
| const documentDataWithHighlights = documentData ? { | |
| ...documentData, | |
| preloadedHighlights: testPreloadedHighlights | |
| } : null; | |
| // Sync PDF page navigation with chunk switching | |
| useEffect(() => { | |
| if (pdfNavigation && documentData && documentData.chunks[currentChunkIndex]) { | |
| const currentChunk = documentData.chunks[currentChunkIndex]; | |
| if (currentChunk.page && pdfNavigation.goToPage) { | |
| pdfNavigation.goToPage(currentChunk.page); | |
| } | |
| } | |
| }, [currentChunkIndex, pdfNavigation, documentData]); | |
| // Simplified startInteractiveLesson | |
| const handleStartInteractiveLesson = () => { | |
| startInteractiveLesson(); | |
| }; | |
| // Main render | |
| return ( | |
| <div | |
| ref={containerRef} | |
| className="h-screen bg-gray-100 flex gap-2 p-6 overflow-hidden" | |
| style={{ cursor: isDragging ? 'col-resize' : 'default' }} | |
| > | |
| {/* Left Panel - Document */} | |
| <div style={{ width: `${leftPanelWidth}%`, height: '100%' }} className="flex flex-col"> | |
| {/* Document Viewer */} | |
| <div className="flex-1 min-h-0"> | |
| <DocumentViewer | |
| selectedFile={selectedFile} | |
| documentData={documentDataWithHighlights} | |
| onPageChange={setPdfNavigation} | |
| preloadedHighlights={documentDataWithHighlights?.preloadedHighlights || null} | |
| currentChunkIndex={currentChunkIndex} | |
| onDocumentReady={setDocumentControls} | |
| isChunkLoading={isChunkLoading} | |
| /> | |
| </div> | |
| </div> | |
| {/* Resizable Divider */} | |
| <div | |
| className="flex items-center justify-center cursor-col-resize group transition-all duration-200" | |
| style={{ width: '8px' }} | |
| onMouseDown={handleMouseDown} | |
| > | |
| <div | |
| className="w-px h-full rounded-full transition-all duration-200 group-hover:shadow-lg" | |
| style={{ | |
| backgroundColor: isDragging ? 'rgba(59, 130, 246, 0.8)' : 'transparent', | |
| boxShadow: isDragging ? '0 0 8px rgba(59, 130, 246, 0.8)' : 'none' | |
| }} | |
| ></div> | |
| </div> | |
| {/* Right Panel Container */} | |
| <div | |
| className="flex flex-col" | |
| style={{ width: `${100 - leftPanelWidth}%` }} | |
| > | |
| {/* Progress Bar */} | |
| <div className="mb-4 flex-shrink-0"> | |
| <ProgressBar | |
| currentChunkIndex={currentChunkIndex} | |
| totalChunks={documentData?.chunks?.length || 0} | |
| onChunkClick={null} // Start with linear progression only | |
| /> | |
| </div> | |
| {/* Right Panel Content - Welcome Screen, Onboarding, or Chunk Panel */} | |
| <div className="flex-1 flex flex-col min-h-0 bg-white rounded-lg shadow-sm"> | |
| {showOnboarding ? ( | |
| <OnboardingWizard | |
| fileName={onboardingData?.fileName || 'Unknown Paper'} | |
| academicBackground={onboardingData?.academicBackground || ''} | |
| onComplete={handleOnboardingComplete} | |
| /> | |
| ) : showWelcomeScreen ? ( | |
| <WelcomeScreen onGetStarted={handleGetStarted} /> | |
| ) : ( | |
| <ChunkPanel | |
| documentData={documentDataWithHighlights} | |
| currentChunkIndex={currentChunkIndex} | |
| showChat={showChat} | |
| updateGlobalChatHistory={updateGlobalChatHistory} | |
| getGlobalChatHistory={getGlobalChatHistory} | |
| addMessageToChunk={addMessageToChunk} | |
| getCurrentChunkMessages={getCurrentChunkMessages} | |
| hasChunkMessages={hasChunkMessages} | |
| setWaitingForFirstResponse={setWaitingForFirstResponse} | |
| isChunkLoading={isChunkLoading} | |
| markChunkUnderstood={markChunkUnderstood} | |
| skipChunk={skipChunk} | |
| goToPrevChunk={goToPrevChunk} | |
| streamResponse={streamResponse} | |
| /> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default DocumentProcessor; |