"use client"; import { useEffect, useRef, useState, Suspense } from "react"; import { cn } from "@/lib/utils"; import NextLink from "next/link"; import { useSearchParams } from "next/navigation"; import { GraduationCap } from "lucide-react"; import { useAuth } from "@/lib/auth/useAuth"; import { normalizeStudyIntent } from "@/lib/study-intents"; import { MainWorkspace } from "./MainWorkspace"; import { MobileDrawerContent } from "./MobileDrawerContent"; import { LeftWorkspaceRail, MobileBottomNav, MobilePanelDrawer, SourceRail, } from "./WorkspacePanels"; import { RightPanelTabs } from "./RightPanelTabs"; import type { MobilePanel, StudyChatShellProps } from "./types"; import { useStudyConversation } from "./useStudyConversation"; import { useStudySources } from "./useStudySources"; import { useStudyUploads } from "./useStudyUploads"; import { useWorkspaceResources } from "./useWorkspaceResources"; import { firstName } from "./utils"; import { SourceClassificationModal } from "./SourceClassificationModal"; import { MATERIAL_UPLOAD_ACCEPT, PDF_UPLOAD_ACCEPT } from "@/lib/uploads/document-upload-contract"; function StudyChatShellInner({ initialIntent = "teach_topic", title, subtitle, hideLeftRail = false, }: StudyChatShellProps) { const searchParams = useSearchParams(); const { user } = useAuth(); const queryIntent = normalizeStudyIntent(searchParams.get("intent") ?? initialIntent); const queryPrompt = searchParams.get("prompt"); const querySourceId = searchParams.get("sourceId"); const runOnOpen = searchParams.get("run") === "1"; const [workspaceSearch, setWorkspaceSearch] = useState(""); const [plusOpen, setPlusOpen] = useState(false); const [mobilePanel, setMobilePanel] = useState(null); const materialInputRef = useRef(null); const paperInputRef = useRef(null); const { documents, documentsLoading, selectedSourceIds, selectedDocuments, sourceSearch, setSourceSearch, setSelectedSourceIds, refreshSources, toggleSource, clearSelectedSources, upsertAndSelectDocument, } = useStudySources(querySourceId); const { dashboard, videoJobs } = useWorkspaceResources(); const { activeIntent, chatNotice, chatSessions, input, isHistoryLoading, isLoading, messages, newChat, openSession, runAction, startVideoRender, setInput, setMessages, submit, threadEndRef, } = useStudyConversation({ closeMobilePanel: () => setMobilePanel(null), closePlusMenu: () => setPlusOpen(false), documents, queryIntent, queryPrompt, querySourceId, runOnOpen, selectedDocuments, selectedSourceIds, setSelectedSourceIds, user, }); const { sourceDraftMode, setSourceDraftMode, draftTitle, setDraftTitle, draftText, setDraftText, isSavingDraft, isUploading, uploadNotice, canRetryUpload, retryUpload, handleMaterialFile, handlePaperFile, saveDraftSource, classificationModal, openClassificationModal, closeClassificationModal, handleClassificationComplete, } = useStudyUploads({ documents, selectedDocuments, setMessages, refreshSources, upsertAndSelectDocument, }); const studentName = firstName(user?.name); const promptPlaceholder = "Ask anything..."; const heroTitle = title ?? "How can I help you today?"; const heroSubtitle = subtitle ?? "Ask anything, upload a readable PDF, or use the video plugin."; useEffect(() => { function onKeyDown(event: KeyboardEvent) { if (event.key === "Escape") { setPlusOpen(false); setMobilePanel(null); } } window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }, []); const composerProps = { value: input, onChange: setInput, onSubmit: () => void submit(), placeholder: promptPlaceholder, disabled: isLoading, selectedDocuments, onRemoveSource: toggleSource, onMaterialFile: (file: File) => void handleMaterialFile(file), onOpenMaterialPicker: () => materialInputRef.current?.click(), onOpenPaperPicker: () => paperInputRef.current?.click(), sourceDraftMode, onSourceDraftModeChange: setSourceDraftMode, draftTitle, onDraftTitleChange: setDraftTitle, draftText, onDraftTextChange: setDraftText, onSaveDraftSource: () => void saveDraftSource(), isSavingDraft, isUploading, uploadNotice: uploadNotice || chatNotice, onRetryUpload: canRetryUpload ? retryUpload : undefined, plusOpen, onPlusOpenChange: setPlusOpen, }; const sourceRail = ( materialInputRef.current?.click()} onEditClassification={openClassificationModal} /> ); const rightPanel = ( materialInputRef.current?.click()} onMaterialFile={(file) => void handleMaterialFile(file)} /> ); return (
{ const file = event.target.files?.[0]; event.target.value = ""; if (file) void handleMaterialFile(file); }} /> { const file = event.target.files?.[0]; event.target.value = ""; if (file) void handlePaperFile(file); }} />
{hideLeftRail ? null : void openSession(sessionId)} onToggleSource={toggleSource} onUpload={() => materialInputRef.current?.click()} isHistoryLoading={isHistoryLoading} />} setMobilePanel("profile")} onOpenSources={() => setMobilePanel("sources")} onStartRender={startVideoRender} onToggleSource={toggleSource} onUpload={() => materialInputRef.current?.click()} selectedDocuments={selectedDocuments} studentName={studentName} threadEndRef={threadEndRef} /> {rightPanel}
setMobilePanel(null)}> setMobilePanel(panel)} onClosePanels={() => setMobilePanel(null)} />
); } export function StudyChatShell({ mode = "home", initialIntent = "teach_topic", title, subtitle, hideLeftRail = false, }: StudyChatShellProps) { return (
Loading Study Chat...
}>
); }