diff --git a/src/components/DocumentList.tsx b/src/components/DocumentList.tsx index f53680b..d20f322 100644 --- a/src/components/DocumentList.tsx +++ b/src/components/DocumentList.tsx @@ -1,7 +1,7 @@ import { usePDF } from '@/contexts/PDFContext'; import Link from 'next/link'; import { Dialog } from '@headlessui/react'; -import { Transition } from '@headlessui/react'; +import { Transition, TransitionChild, DialogPanel, DialogTitle } from '@headlessui/react'; import { Fragment, useState } from 'react'; export function DocumentList() { @@ -102,7 +102,7 @@ export function DocumentList() { className="relative z-50" onClose={() => setIsDeleteDialogOpen(false)} > -
- +
- - - + Delete Document - +

Are you sure you want to delete "{documentToDelete?.name}"? This action cannot be undone. @@ -154,8 +154,8 @@ export function DocumentList() { Delete

-
-
+ +
diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 73e9dde..03af752 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -1,7 +1,7 @@ 'use client'; import { Fragment } from 'react'; -import { Dialog, Transition, Listbox } from '@headlessui/react'; +import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild, Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/react'; import { useTheme } from '@/contexts/ThemeContext'; interface SettingsModalProps { @@ -22,7 +22,7 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) { return ( setIsOpen(false)}> -
- +
- - - + Settings - +
setTheme(newTheme.id)}>
- + {selectedTheme.name} - + - + {themes.map((theme) => ( - `relative cursor-pointer select-none py-2 pl-10 pr-4 ${ @@ -115,9 +115,9 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) { ) : null} )} - + ))} - +
@@ -137,8 +137,8 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) { Close
- - + +
diff --git a/src/components/TTSPlayer.tsx b/src/components/TTSPlayer.tsx index 7ec0831..ab6cf6e 100644 --- a/src/components/TTSPlayer.tsx +++ b/src/components/TTSPlayer.tsx @@ -34,13 +34,13 @@ const speedOptions = [ export default function TTSPlayer() { const [isVisible, setIsVisible] = useState(true); - const { - isPlaying, - togglePlay, - skipForward, - skipBackward, - isProcessing, - speed, + const { + isPlaying, + togglePlay, + skipForward, + skipBackward, + isProcessing, + speed, setSpeedAndRestart, voice, setVoiceAndRestart, @@ -50,12 +50,34 @@ export default function TTSPlayer() { //console.log(availableVoices); return ( -
+
+ + + {speed}x + + + + {speedOptions.map((option) => ( + + `relative cursor-pointer select-none py-2 px-3 ${active ? 'bg-offbase' : '' + } ${selected ? 'font-medium' : ''}` + } + > + {option.label} + + ))} + + +
+ - + -
- - - {speed}x - - - - {speedOptions.map((option) => ( - - `relative cursor-pointer select-none py-2 px-3 ${ - active ? 'bg-offbase' : '' - } ${selected ? 'font-medium' : ''}` - } - > - {option.label} - - ))} - - -
@@ -117,10 +116,8 @@ export default function TTSPlayer() { - `relative cursor-pointer select-none py-2 px-3 ${ - active ? 'bg-offbase' : '' - } ${selected ? 'font-medium' : ''}` + className={({ active, selected }) => + `relative cursor-pointer select-none py-2 px-3 ${active ? 'bg-offbase' : ''} ${selected ? 'font-medium' : ''}` } > {voiceId} diff --git a/src/contexts/PDFContext.tsx b/src/contexts/PDFContext.tsx index 2d6a1c3..06fb524 100644 --- a/src/contexts/PDFContext.tsx +++ b/src/contexts/PDFContext.tsx @@ -15,6 +15,9 @@ import { pdfjs } from 'react-pdf'; import stringSimilarity from 'string-similarity'; import nlp from 'compromise'; +// Add the correct type import +import type { TextContent, TextItem } from 'pdfjs-dist/types/src/display/api'; + // Set worker from public directory pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.mjs'; @@ -135,11 +138,66 @@ export function PDFProvider({ children }: { children: ReactNode }) { for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); - const pageText = textContent.items.map((item: any) => item.str).join(' '); - fullText += pageText + ' '; + + // Filter out non-text items and assert proper type + const textItems = textContent.items.filter((item): item is TextItem => + 'str' in item && 'transform' in item + ); + + // Group text items into lines based on their vertical position + const tolerance = 2; + const lines: TextItem[][] = []; + let currentLine: TextItem[] = []; + let currentY: number | null = null; + + textItems.forEach((item) => { + const y = item.transform[5]; + if (currentY === null) { + currentY = y; + currentLine.push(item); + } else if (Math.abs(y - currentY) < tolerance) { + currentLine.push(item); + } else { + lines.push(currentLine); + currentLine = [item]; + currentY = y; + } + }); + lines.push(currentLine); + + // Process each line to build text + let pageText = ''; + for (const line of lines) { + // Sort items horizontally within the line + line.sort((a, b) => a.transform[4] - b.transform[4]); + + let lineText = ''; + let prevItem: TextItem | null = null; + + for (const item of line) { + if (!prevItem) { + lineText = item.str; + } else { + const prevEndX = prevItem.transform[4] + (prevItem.width ?? 0); + const currentStartX = item.transform[4]; + const space = currentStartX - prevEndX; + + // Add space if gap is significant, otherwise concatenate directly + if (space > ((item.width ?? 0) * 0.3)) { + lineText += ' ' + item.str; + } else { + lineText += item.str; + } + } + prevItem = item; + } + pageText += lineText + ' '; + } + + fullText += pageText + '\n'; } - return fullText; + return fullText.replace(/\s+/g, ' ').trim(); } catch (error) { console.error('Error extracting text from PDF:', error); throw new Error('Failed to extract text from PDF');