diff --git a/src/app/api/nlp/route.ts b/src/app/api/nlp/route.ts index 41c53cb..b524450 100644 --- a/src/app/api/nlp/route.ts +++ b/src/app/api/nlp/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import nlp from 'compromise'; -const MAX_BLOCK_LENGTH = 450; +const MAX_BLOCK_LENGTH = 350; const preprocessSentenceForAudio = (text: string): string => { return text diff --git a/src/components/EPUBViewer.tsx b/src/components/EPUBViewer.tsx index 656af58..93d8019 100644 --- a/src/components/EPUBViewer.tsx +++ b/src/components/EPUBViewer.tsx @@ -5,84 +5,18 @@ import { useParams } from 'next/navigation'; import dynamic from 'next/dynamic'; import { useEPUB } from '@/contexts/EPUBContext'; import { useTTS } from '@/contexts/TTSContext'; +import { useConfig } from '@/contexts/ConfigContext'; import { DocumentSkeleton } from '@/components/DocumentSkeleton'; import TTSPlayer from '@/components/player/TTSPlayer'; import { setLastDocumentLocation } from '@/utils/indexedDB'; import type { Rendition, Book, NavItem } from 'epubjs'; -import { ReactReaderStyle, type IReactReaderStyle } from 'react-reader'; -import { useConfig } from '@/contexts/ConfigContext'; +import { useEPUBTheme, getThemeStyles } from '@/hooks/useEPUBTheme'; const ReactReader = dynamic(() => import('react-reader').then(mod => mod.ReactReader), { ssr: false, loading: () => }); -const getThemeStyles = (): IReactReaderStyle => { - const baseStyle = { - ...ReactReaderStyle, - readerArea: { - ...ReactReaderStyle.readerArea, - transition: undefined, - } - }; - - const colors = { - background: getComputedStyle(document.documentElement).getPropertyValue('--background'), - foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'), - base: getComputedStyle(document.documentElement).getPropertyValue('--base'), - offbase: getComputedStyle(document.documentElement).getPropertyValue('--offbase'), - muted: getComputedStyle(document.documentElement).getPropertyValue('--muted'), - }; - - return { - ...baseStyle, - arrow: { - ...baseStyle.arrow, - color: colors.foreground, - }, - arrowHover: { - ...baseStyle.arrowHover, - color: colors.muted, - }, - readerArea: { - ...baseStyle.readerArea, - backgroundColor: colors.base, - }, - titleArea: { - ...baseStyle.titleArea, - color: colors.foreground, - display: 'none', - }, - tocArea: { - ...baseStyle.tocArea, - background: colors.base, - }, - tocButtonExpanded: { - ...baseStyle.tocButtonExpanded, - background: colors.offbase, - }, - tocButtonBar: { - ...baseStyle.tocButtonBar, - background: colors.muted, - }, - tocButton: { - ...baseStyle.tocButton, - color: colors.muted, - }, - tocAreaButton: { - ...baseStyle.tocAreaButton, - color: colors.muted, - backgroundColor: colors.offbase, - padding: '0.25rem', - paddingLeft: '0.5rem', - paddingRight: '0.5rem', - marginBottom: '0.25rem', - borderRadius: '0.25rem', - borderColor: 'transparent', - }, - }; -}; - interface EPUBViewerProps { className?: string; } @@ -96,11 +30,11 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) { const rendition = useRef(undefined); const toc = useRef([]); const locationRef = useRef(currDocPage); - const [reloadKey, setReloadKey] = useState(0); const [initialPrevLocLoad, setInitialPrevLocLoad] = useState(false); + const { updateTheme } = useEPUBTheme(epubTheme, rendition.current); const handleLocationChanged = useCallback((location: string | number, initial = false) => { - if (!bookRef.current?.isOpen) return; + if (!bookRef.current?.isOpen || !rendition.current) return; // Handle special 'next' and 'prev' cases, which if (location === 'next' && rendition.current) { rendition.current.next(); @@ -111,32 +45,32 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) { return; } - if (bookRef.current && rendition.current) { - const { displayed, href } = rendition.current.location.start; - const chapter = toc.current.find((item) => item.href === href); - - console.log('Displayed:', displayed, 'Chapter:', chapter); - if (locationRef.current !== 1) { - // Save the location to IndexedDB - if (id) { - console.log('Saving location:', location); - setLastDocumentLocation(id as string, location.toString()); - } - } - - setEPUBPageInChapter(displayed.page, displayed.total, chapter?.label || ''); - - // Add a small delay for initial load to ensure rendition is ready - if (initial) { - rendition.current.display(location.toString()).then(() => { - setInitialPrevLocLoad(true); - }); - } else { - locationRef.current = location; - extractPageText(bookRef.current, rendition.current); + const { displayed, href } = rendition.current.location.start; + const chapter = toc.current.find((item) => item.href === href); + + console.log('Displayed:', displayed, 'Chapter:', chapter); + + if (locationRef.current !== 1) { + // Save the location to IndexedDB + if (id) { + console.log('Saving location:', location); + setLastDocumentLocation(id as string, location.toString()); } } + + setEPUBPageInChapter(displayed.page, displayed.total, chapter?.label || ''); + + // Add a small delay for initial load to ensure rendition is ready + if (initial) { + rendition.current.display(location.toString()).then(() => { + setInitialPrevLocLoad(true); + }); + } else { + locationRef.current = location; + extractPageText(bookRef.current, rendition.current); + } + }, [id, setEPUBPageInChapter, extractPageText]); // Replace the debounced text extraction with a proper implementation using useMemo @@ -174,46 +108,6 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) { } }, [extractPageText, debouncedExtractText, initialPrevLocLoad]); - const updateTheme = useCallback((rendition: Rendition) => { - if (!epubTheme) return; // Only apply theme if enabled - - const colors = { - foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'), - base: getComputedStyle(document.documentElement).getPropertyValue('--base'), - }; - - rendition.themes.override('color', colors.foreground); - rendition.themes.override('background', colors.base); - }, [epubTheme]); - - // Watch for theme changes - useEffect(() => { - if (!epubTheme || !bookRef.current?.isOpen || !rendition.current) return; - - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName === 'class') { - if (epubTheme) { - setReloadKey(prev => prev + 1); - } - } - }); - }); - - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ['class'] - }); - - return () => observer.disconnect(); - }, [epubTheme]); - - // Watch for epubTheme changes - useEffect(() => { - if (!epubTheme || !bookRef.current?.isOpen || !rendition.current) return; - setReloadKey(prev => prev + 1); - }, [epubTheme]); - // Register the location change handler useEffect(() => { registerLocationChangeHandler(handleLocationChanged); @@ -230,7 +124,7 @@ export function EPUBViewer({ className = '' }: EPUBViewerProps) {
{ - updateTheme(_rendition); - bookRef.current = _rendition.book; rendition.current = _rendition; + updateTheme(); }} />
diff --git a/src/contexts/EPUBContext.tsx b/src/contexts/EPUBContext.tsx index e874d30..5703183 100644 --- a/src/contexts/EPUBContext.tsx +++ b/src/contexts/EPUBContext.tsx @@ -21,7 +21,6 @@ interface EPUBContextType { currDocText: string | undefined; setCurrentDocument: (id: string) => Promise; clearCurrDoc: () => void; - onDocumentLoadSuccess: ({ numPages }: { numPages: number }) => void; extractPageText: (book: Book, rendition: Rendition) => Promise; } @@ -35,14 +34,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) { const [currDocName, setCurrDocName] = useState(); const [currDocText, setCurrDocText] = useState(); - /** - * Handles successful document load - */ - const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => { - console.log('EPUB loaded:', numPages); - setCurrDocPages(numPages); - }, [setCurrDocPages]); - /** * Clears the current document state */ @@ -106,7 +97,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) { // Context value memoization const contextValue = useMemo( () => ({ - onDocumentLoadSuccess, setCurrentDocument, currDocData, currDocName, @@ -117,7 +107,6 @@ export function EPUBProvider({ children }: { children: ReactNode }) { extractPageText, }), [ - onDocumentLoadSuccess, setCurrentDocument, currDocData, currDocName, diff --git a/src/hooks/useEPUBTheme.ts b/src/hooks/useEPUBTheme.ts new file mode 100644 index 0000000..712e58c --- /dev/null +++ b/src/hooks/useEPUBTheme.ts @@ -0,0 +1,119 @@ +import { useCallback, useEffect } from 'react'; +import { Rendition } from 'epubjs'; +import { ReactReaderStyle, IReactReaderStyle } from 'react-reader'; + +export const getThemeStyles = (): IReactReaderStyle => { + const baseStyle = { + ...ReactReaderStyle, + readerArea: { + ...ReactReaderStyle.readerArea, + transition: undefined, + } + }; + + const colors = { + background: getComputedStyle(document.documentElement).getPropertyValue('--background'), + foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'), + base: getComputedStyle(document.documentElement).getPropertyValue('--base'), + offbase: getComputedStyle(document.documentElement).getPropertyValue('--offbase'), + muted: getComputedStyle(document.documentElement).getPropertyValue('--muted'), + }; + + return { + ...baseStyle, + arrow: { + ...baseStyle.arrow, + color: colors.foreground, + }, + arrowHover: { + ...baseStyle.arrowHover, + color: colors.muted, + }, + readerArea: { + ...baseStyle.readerArea, + backgroundColor: colors.base, + }, + titleArea: { + ...baseStyle.titleArea, + color: colors.foreground, + display: 'none', + }, + tocArea: { + ...baseStyle.tocArea, + background: colors.base, + }, + tocButtonExpanded: { + ...baseStyle.tocButtonExpanded, + background: colors.offbase, + }, + tocButtonBar: { + ...baseStyle.tocButtonBar, + background: colors.muted, + }, + tocButton: { + ...baseStyle.tocButton, + color: colors.muted, + }, + tocAreaButton: { + ...baseStyle.tocAreaButton, + color: colors.muted, + backgroundColor: colors.offbase, + padding: '0.25rem', + paddingLeft: '0.5rem', + paddingRight: '0.5rem', + marginBottom: '0.25rem', + borderRadius: '0.25rem', + borderColor: 'transparent', + }, + }; +}; + +export const useEPUBTheme = (epubTheme: boolean, rendition: Rendition | undefined) => { + const updateTheme = useCallback(() => { + if (!epubTheme || !rendition) return; + + const colors = { + foreground: getComputedStyle(document.documentElement).getPropertyValue('--foreground'), + base: getComputedStyle(document.documentElement).getPropertyValue('--base'), + }; + + // Register theme rules instead of using override + rendition.themes.registerRules('theme-light', { + 'body': { + 'color': colors.foreground, + 'background-color': colors.base + } + }); + + // Select the theme to apply it + rendition.themes.select('theme-light'); + }, [epubTheme, rendition]); + + // Watch for theme changes + useEffect(() => { + if (!epubTheme || !rendition) return; + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName === 'class') { + updateTheme(); + } + }); + }); + + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }); + + return () => observer.disconnect(); + }, [epubTheme, rendition, updateTheme]); + + // Watch for epubTheme changes + useEffect(() => { + if (!epubTheme || !rendition) return; + updateTheme(); + }, [epubTheme, rendition, updateTheme]); + + return { updateTheme }; +}; \ No newline at end of file