'use client' import { useState, useEffect } from 'react' import { useParams } from 'next/navigation' import { AppShell } from '@/components/layout/AppShell' import { NotebookHeader } from '../components/NotebookHeader' import { SourcesColumn } from '../components/SourcesColumn' import { NotesColumn } from '../components/NotesColumn' import { ChatColumn } from '../components/ChatColumn' import { useNotebook } from '@/lib/hooks/use-notebooks' import { useSources } from '@/lib/hooks/use-sources' import { useNotes } from '@/lib/hooks/use-notes' import { LoadingSpinner } from '@/components/common/LoadingSpinner' import { useNotebookColumnsStore } from '@/lib/stores/notebook-columns-store' import { useIsDesktop } from '@/lib/hooks/use-media-query' import { cn } from '@/lib/utils' import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs' import { FileText, StickyNote, MessageSquare } from 'lucide-react' export type ContextMode = 'off' | 'insights' | 'full' export interface ContextSelections { sources: Record notes: Record } export default function NotebookPage() { const params = useParams() // Ensure the notebook ID is properly decoded from URL const notebookId = decodeURIComponent(params.id as string) const { data: notebook, isLoading: notebookLoading } = useNotebook(notebookId) const { data: sources, isLoading: sourcesLoading, refetch: refetchSources } = useSources(notebookId) const { data: notes, isLoading: notesLoading } = useNotes(notebookId) // Get collapse states for dynamic layout const { sourcesCollapsed, notesCollapsed } = useNotebookColumnsStore() // Detect desktop to avoid double-mounting ChatColumn const isDesktop = useIsDesktop() // Mobile tab state (Sources, Notes, or Chat) const [mobileActiveTab, setMobileActiveTab] = useState<'sources' | 'notes' | 'chat'>('chat') // Context selection state const [contextSelections, setContextSelections] = useState({ sources: {}, notes: {} }) // Initialize default selections when sources/notes load useEffect(() => { if (sources && sources.length > 0) { setContextSelections(prev => { const newSourceSelections = { ...prev.sources } sources.forEach(source => { // Only set default if not already set if (!(source.id in newSourceSelections)) { // Default to 'insights' if has insights, otherwise 'full' newSourceSelections[source.id] = source.insights_count > 0 ? 'insights' : 'full' } }) return { ...prev, sources: newSourceSelections } }) } }, [sources]) useEffect(() => { if (notes && notes.length > 0) { setContextSelections(prev => { const newNoteSelections = { ...prev.notes } notes.forEach(note => { // Only set default if not already set if (!(note.id in newNoteSelections)) { // Notes default to 'full' newNoteSelections[note.id] = 'full' } }) return { ...prev, notes: newNoteSelections } }) } }, [notes]) // Handler to update context selection const handleContextModeChange = (itemId: string, mode: ContextMode, type: 'source' | 'note') => { setContextSelections(prev => ({ ...prev, [type === 'source' ? 'sources' : 'notes']: { ...(type === 'source' ? prev.sources : prev.notes), [itemId]: mode } })) } if (notebookLoading) { return (
) } if (!notebook) { return (

Notebook Not Found

The requested notebook could not be found.

) } return (
{/* Mobile: Tabbed interface - only render on mobile to avoid double-mounting */} {!isDesktop && ( <>
setMobileActiveTab(value as 'sources' | 'notes' | 'chat')}> Sources Notes Chat
{/* Mobile: Show only active tab */}
{mobileActiveTab === 'sources' && ( handleContextModeChange(sourceId, mode, 'source')} /> )} {mobileActiveTab === 'notes' && ( handleContextModeChange(noteId, mode, 'note')} /> )} {mobileActiveTab === 'chat' && ( )}
)} {/* Desktop: Collapsible columns layout */}
{/* Sources Column */}
handleContextModeChange(sourceId, mode, 'source')} />
{/* Notes Column */}
handleContextModeChange(noteId, mode, 'note')} />
{/* Chat Column - always expanded, takes remaining space */}
) }