This commit is contained in:
Richard Roberson 2025-01-18 05:38:13 -07:00
parent 5ecd54a0be
commit 8263a84e40
9 changed files with 78 additions and 44 deletions

View file

@ -7,6 +7,8 @@
--foreground: #171717;
--base: #f5f5f5;
--offbase: #dbdbdb;
--accent: #ef4444;
--muted: #737373;
}
@media (prefers-color-scheme: dark) {
@ -15,6 +17,8 @@
--foreground: #ededed;
--base: #333333;
--offbase: #4a4a4a;
--accent: #f87171;
--muted: #a3a3a3;
}
}

View file

@ -28,7 +28,15 @@ export default function RootLayout({
<body
className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased`}
>
<Providers>{children}</Providers>
<Providers>
<div className="min-h-screen bg-background py-4">
<div className="max-w-6xl mx-auto px-2">
<div className="bg-base rounded-lg shadow-lg">
{children}
</div>
</div>
</div>
</Providers>
</body>
</html>
);

View file

@ -5,15 +5,11 @@ import { DocumentList } from '@/components/DocumentList';
export default function Home() {
return (
<div className="min-h-screen bg-background py-4">
<div className="max-w-5xl mx-auto px-2">
<div className="bg-base rounded-lg shadow-lg p-4">
<h1 className="text-2xl font-bold mb-6 text-center">PDF Reader</h1>
<div className="flex flex-col items-center gap-6">
<PDFUploader className="w-full max-w-md" />
<DocumentList />
</div>
</div>
<div className='p-4'>
<h1 className="text-2xl font-bold mb-6 text-center">OpenReader WebUI</h1>
<div className="flex flex-col items-center gap-6">
<PDFUploader className="w-full max-w-md" />
<DocumentList />
</div>
</div>
);

View file

@ -9,7 +9,7 @@ export default function PDFViewerPage() {
const { id } = useParams();
const { getDocument } = usePDF();
const router = useRouter();
const document = getDocument(id as string);
if (!document) {
@ -17,13 +17,13 @@ export default function PDFViewerPage() {
<div className="min-h-screen bg-background py-6 px-4 sm:py-8">
<div className="max-w-6xl mx-auto">
<div className="bg-base rounded-lg shadow-lg p-6">
<p className="text-center text-lg">Document not found. Please select a document from the home page.</p>
<p className="text-center text-lg text-muted">Document not found. Please select a document from the home page.</p>
<div className="mt-6 text-center">
<Link
<Link
href="/"
className="inline-flex items-center px-4 py-2 bg-base text-foreground rounded-lg hover:bg-offbase transition-colors"
>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-5 h-5 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Back to Documents
@ -36,28 +36,22 @@ export default function PDFViewerPage() {
}
return (
<div className="min-h-screen bg-background py-4 px-4 sm:py-2">
<div className="max-w-6xl mx-auto">
<div className="mb-4">
<Link
<>
<div className="p-2 pb-2 border-b border-offbase">
<div className="flex items-center justify-between">
<Link
href="/"
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-colors"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Documents
</Link>
</div>
<div className="bg-base rounded-lg shadow-lg overflow-hidden">
<div className="p-4 pb-2 border-b">
<h1 className="text-xl font-semibold">{document.name}</h1>
</div>
<div className="p-1">
<PDFViewer pdfFile={document.data} />
</div>
<h1 className="mr-2 text-xl font-semibold text-foreground">{document.name}</h1>
</div>
</div>
</div>
<PDFViewer pdfFile={document.data} highlightText={'Richard Roberson'} />
</>
);
}

View file

@ -6,7 +6,7 @@ export function DocumentList() {
if (documents.length === 0) {
return (
<div className="w-full text-center text-gray-500">
<div className="w-full text-center text-muted">
No documents uploaded yet
</div>
);
@ -14,32 +14,32 @@ export function DocumentList() {
return (
<div className="w-full">
<h2 className="text-xl font-semibold mb-4">Your Documents</h2>
<div className="bg-white rounded-lg shadow p-4 space-y-2">
<h2 className="text-xl font-semibold mb-4 text-foreground">Your Documents</h2>
<div className="bg-background rounded-lg shadow p-2 space-y-2">
{documents.map((doc) => (
<div
key={doc.id}
className="flex items-center justify-between hover:bg-gray-50 p-2 rounded-lg transition-colors"
className="flex items-center justify-between hover:bg-base p-1 rounded-lg transition-colors"
>
<Link
href={`/pdf/${encodeURIComponent(doc.id)}`}
className="flex items-center space-x-4 flex-1"
>
<div className="flex-shrink-0">
<svg className="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-8 h-8 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
</div>
<div>
<h3 className="font-medium">{doc.name}</h3>
<p className="text-sm text-gray-500">
<h3 className="font-medium text-foreground">{doc.name}</h3>
<p className="text-sm text-muted">
{(doc.size / 1024 / 1024).toFixed(2)} MB
</p>
</div>
</Link>
<button
onClick={() => removeDocument(doc.id)}
className="p-2 text-gray-400 hover:text-red-500 transition-colors"
className="p-2 text-muted hover:text-accent transition-colors"
title="Remove document"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">

View file

@ -32,16 +32,16 @@ export function PDFUploader({ className = '' }: PDFUploaderProps) {
{...getRootProps()}
className={`
w-full p-8 border-2 border-dashed rounded-lg
${isDragActive ? 'border-blue-500 bg-blue-50' : 'border-gray-300'}
${isDragActive ? 'border-accent bg-base' : 'border-muted'}
transition-colors duration-200 ease-in-out cursor-pointer
hover:border-blue-400 hover:bg-gray-50
hover:border-accent hover:bg-base
${className}
`}
>
<input {...getInputProps()} />
<div className="flex flex-col items-center justify-center text-center">
<svg
className="w-12 h-12 mb-4 text-gray-400"
className="w-12 h-12 mb-4 text-muted"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@ -55,10 +55,10 @@ export function PDFUploader({ className = '' }: PDFUploaderProps) {
/>
</svg>
<p className="mb-2 text-lg font-semibold text-gray-700">
<p className="mb-2 text-lg font-semibold text-foreground">
Drop your PDF here, or click to select
</p>
<p className="text-sm text-gray-500">
<p className="text-sm text-muted">
Only PDF files are accepted
</p>
</div>

View file

@ -3,22 +3,47 @@
import { Document, Page, pdfjs } from 'react-pdf';
import 'react-pdf/dist/Page/AnnotationLayer.css';
import 'react-pdf/dist/Page/TextLayer.css';
import { useState } from 'react';
import { useState, useEffect } from 'react';
// Set worker from public directory
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.mjs';
interface PDFViewerProps {
pdfFile: string;
highlightText?: string; // Text to highlight in the PDF
}
export function PDFViewer({ pdfFile }: PDFViewerProps) {
export function PDFViewer({ pdfFile, highlightText }: PDFViewerProps) {
const [numPages, setNumPages] = useState<number>();
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
setNumPages(numPages);
}
// Function to highlight text in the PDF
const highlightPattern = (text: string, pattern: string) => {
if (!pattern) return text;
const regex = new RegExp(`(${pattern})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
};
// Add styles for highlighted text
useEffect(() => {
const style = document.createElement('style');
style.textContent = `
.react-pdf__Page__textContent mark {
background-color: yellow;
border-radius: 2px;
padding: 0;
margin: 0;
}
`;
document.head.appendChild(style);
return () => {
document.head.removeChild(style);
};
}, []);
return (
<div className="flex flex-col items-center">
<Document
@ -42,6 +67,11 @@ export function PDFViewer({ pdfFile }: PDFViewerProps) {
renderTextLayer={true}
className="rounded-xl shadow-lg"
scale={1.2}
customTextRenderer={(textItem) =>
highlightText ?
highlightPattern(textItem.str, highlightText) :
textItem.str
}
/>
</div>
</div>

View file

@ -43,7 +43,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
reader.onloadend = () => {
const base64Data = reader.result as string;
const newDoc: PDFDocument = {
id: `${file.name}-${Date.now()}`,
id: `${file.name.replace(/[^a-zA-Z0-9]/g, '-').replace(/-+/g, '-')}-${Date.now()}`,
name: file.name,
size: file.size,
lastModified: file.lastModified,

View file

@ -13,6 +13,8 @@ export default {
foreground: "var(--foreground)",
base: "var(--base)",
offbase: "var(--offbase)",
accent: "var(--accent)",
muted: "var(--muted)",
},
},
},