Working
This commit is contained in:
parent
5ecd54a0be
commit
8263a84e40
9 changed files with 78 additions and 44 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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'} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ export default {
|
|||
foreground: "var(--foreground)",
|
||||
base: "var(--base)",
|
||||
offbase: "var(--offbase)",
|
||||
accent: "var(--accent)",
|
||||
muted: "var(--muted)",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue