// dashboard/src/features/reader/ReaderPanel.tsx import { useState, useEffect } from 'react'; import type { StandardPaper, NoteRecord } from '../types'; import { useReaderState } from '../hooks/useReaderState'; import { ReaderToolbar } from '../components/reader/ReaderToolbar'; import { BilingualViewer } from '../components/reader/BilingualViewer'; import { ReaderNotesSidebar } from '../components/reader/ReaderNotesSidebar'; interface ReaderPanelProps { selectedPaper: StandardPaper; library: StandardPaper[]; recentlySelected: StandardPaper[]; onSwitchPaper: (paper: StandardPaper) => void; parsing: boolean; handleParse: (bibcode: string, force?: boolean) => void; translating: boolean; handleTranslate: (bibcode: string, force?: boolean) => void; vectorizing: boolean; handleVectorize: (bibcode: string) => void; showNotesPanel: boolean; setShowNotesPanel: (show: boolean) => void; notes: NoteRecord[]; englishText: string; chineseText: string; handleTextSelection: (paragraphIdx: number) => void; selectedParagraphIdx: number | null; setSelectedParagraphIdx: (idx: number | null) => void; selectedText: string; setSelectedText: (text: string) => void; newNoteColor: string; setNewNoteColor: (color: string) => void; newNoteText: string; setNewNoteText: (text: string) => void; handleCreateNote: () => void; handleDeleteNote: (id: number) => void; showConfirm: (message: string, onConfirm: () => void, title?: string) => void; onJumpToSource: (bibcode: string, paragraphIndex: number) => void; } export function ReaderPanel(props: ReaderPanelProps) { const { selectedPaper, library, recentlySelected, onSwitchPaper, parsing, handleParse, translating, handleTranslate, vectorizing, handleVectorize, showNotesPanel, setShowNotesPanel, notes, englishText, chineseText, handleTextSelection, selectedParagraphIdx, setSelectedParagraphIdx, selectedText, setSelectedText, newNoteColor, setNewNoteColor, newNoteText, setNewNoteText, handleCreateNote, handleDeleteNote, showConfirm, onJumpToSource, } = props; // 局部管理阅读视角模式(原文/中文/对照) const [viewMode, setViewMode] = useState<'bilingual' | 'english' | 'chinese'>(() => { if (typeof window !== 'undefined' && window.innerWidth < 768) { return chineseText ? 'chinese' : 'english'; } if (!chineseText) { return 'english'; } return 'bilingual'; }); const [userOverrode, setUserOverrode] = useState(false); // 监听窗口尺寸变化,处理移动端与桌面端的自适应视角切换 useEffect(() => { if (typeof window === 'undefined') return; let prevWidth = window.innerWidth; const handleResize = () => { const currentWidth = window.innerWidth; const wasMobile = prevWidth < 768; const isMobile = currentWidth < 768; if (wasMobile !== isMobile) { if (isMobile) { // 从宽屏缩放到手机端:若有翻译则默认显示中文译文,否则原文 setViewMode(chineseText ? 'chinese' : 'english'); } else { // 从手机端拉宽到桌面端/平板:若有翻译则显示双语对照,否则显示原文 setViewMode(chineseText ? 'bilingual' : 'english'); } } prevWidth = currentWidth; }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, [chineseText]); // 无翻译时强制回退到英文模式,有翻译时且在桌面端时自动切换到双语对照 useEffect(() => { if (userOverrode) return; const isMobile = typeof window !== 'undefined' && window.innerWidth < 768; if (isMobile) { if (!chineseText && viewMode !== 'english') { queueMicrotask(() => setViewMode('english')); } return; } if (!chineseText && viewMode === 'bilingual') { queueMicrotask(() => setViewMode('english')); } else if (chineseText && viewMode === 'english') { queueMicrotask(() => setViewMode('bilingual')); } }, [chineseText, viewMode, userOverrode]); const handleViewModeChange = (mode: 'bilingual' | 'english' | 'chinese') => { setUserOverrode(true); setViewMode(mode); }; // 切换文献时重置用户手动覆盖标记 useEffect(() => { queueMicrotask(() => setUserOverrode(false)); }, [selectedPaper?.bibcode]); // 调用封装后的阅读器逻辑状态 Hook const state = useReaderState({ selectedPaper, englishText, viewMode, setViewMode: handleViewModeChange, }); return (
{/* 顶部控制栏 */} {/* 并排对比视窗与侧边栏 */} {/* 学术助手侧边栏 */}
); }