Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/api/import/extract-text/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NextResponse } from 'next/server';
import { FORMAT_HANDLERS, getExtension } from '@/lib/extract-text';

const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB
export const runtime = 'nodejs';
export const maxDuration = 30;

export async function POST(req: Request) {
try {
Expand Down
3 changes: 3 additions & 0 deletions src/app/api/import/pdf/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { NextResponse } from 'next/server';
import { PDFParse } from 'pdf-parse';

export const runtime = 'nodejs';
export const maxDuration = 30;

export async function POST(req: Request) {
try {
const formData = await req.formData();
Expand Down
35 changes: 34 additions & 1 deletion src/components/import/document-import.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';

import { ClipboardPaste, FileUp, Globe } from 'lucide-react';
import { useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { FileUploadImport } from '@/components/import/file-upload-import';
import { TextImport } from '@/components/import/text-import';
import { UrlImport } from '@/components/import/url-import';
Expand All @@ -12,10 +13,42 @@ import { nativeHaptic } from '@/lib/tauri';
type SubTab = 'paste' | 'upload' | 'url';

export function DocumentImport() {
const searchParams = useSearchParams();
const [activeTab, setActiveTab] = useState<SubTab>('paste');
const { messages } = useI18n('library');
const m = messages.documentImport;

useEffect(() => {
const subtab = searchParams.get('subtab');
const sourceUrl = searchParams.get('sourceUrl');
if (subtab === 'url' || sourceUrl) {
setActiveTab('url');
return;
}

if (subtab === 'upload') {
setActiveTab('upload');
return;
}

if (subtab === 'paste') {
setActiveTab('paste');
}
}, [searchParams]);

useEffect(() => {
const nextParams = new URLSearchParams(window.location.search);
nextParams.set('subtab', activeTab);
if (activeTab !== 'url') {
nextParams.delete('sourceUrl');
}
const nextSearch = nextParams.toString();
const nextUrl = nextSearch ? `${window.location.pathname}?${nextSearch}` : window.location.pathname;
if (nextUrl !== `${window.location.pathname}${window.location.search}`) {
window.history.replaceState(null, '', nextUrl);
}
}, [activeTab]);

const selectTab = (tab: SubTab) => {
setActiveTab(tab);
nativeHaptic('light');
Expand Down
30 changes: 28 additions & 2 deletions src/components/import/url-import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import { AlertCircle, Check, ChevronDown, ExternalLink, Globe, Loader2, Sparkles } from 'lucide-react';
import { nanoid } from 'nanoid';
import { useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { ImportPracticeActions } from '@/components/import/import-practice-actions';
import { TagSelector } from '@/components/shared/tag-selector';
import { Badge } from '@/components/ui/badge';
Expand All @@ -24,6 +25,7 @@ interface FetchResult {
}

export function UrlImport() {
const searchParams = useSearchParams();
const { addContent } = useContentStore();
const { messages } = useI18n('library');
const m = messages.urlImport;
Expand All @@ -40,6 +42,28 @@ export function UrlImport() {
const [showFullText, setShowFullText] = useState(false);
const [savedItem, setSavedItem] = useState<ContentItem | null>(null);

useEffect(() => {
const sourceUrl = searchParams.get('sourceUrl') || '';
if (sourceUrl !== url) {
setUrl(sourceUrl);
}
}, [searchParams]);

useEffect(() => {
const nextParams = new URLSearchParams(window.location.search);
nextParams.set('subtab', 'url');
if (url.trim()) {
nextParams.set('sourceUrl', url.trim());
} else {
nextParams.delete('sourceUrl');
}
const nextSearch = nextParams.toString();
const nextUrl = nextSearch ? `${window.location.pathname}?${nextSearch}` : window.location.pathname;
if (nextUrl !== `${window.location.pathname}${window.location.search}`) {
window.history.replaceState(null, '', nextUrl);
}
}, [url]);

const handleFetch = async () => {
if (!url.trim()) return;
setFetching(true);
Expand Down Expand Up @@ -98,7 +122,9 @@ export function UrlImport() {
<Input
id="url-import-input"
value={url}
onChange={(e) => setUrl(e.target.value)}
onChange={(e) => {
setUrl(e.target.value);
}}
onKeyDown={(e) => e.key === 'Enter' && handleFetch()}
placeholder={m.placeholderUrl}
className="pl-10 bg-white border-indigo-200 focus:border-indigo-500 focus:ring-indigo-500"
Expand Down
9 changes: 6 additions & 3 deletions src/lib/web-page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as extractText from './extract-text';

const URL_REGEX = /https?:\/\/[^\s]+/i;
const HTTP_FALLBACK_HOSTS = new Set(['downloads.bbc.co.uk']);

Expand Down Expand Up @@ -186,6 +184,11 @@ async function fetchRemoteResponse(url: string): Promise<Response> {
}
}

async function extractPdfFromBuffer(buffer: Buffer) {
const extractText = await import('./extract-text');
return extractText.extractPdf(buffer);
}

export function extractFirstUrl(input: string): string | null {
const match = input.match(URL_REGEX);
return match?.[0] ?? null;
Expand Down Expand Up @@ -230,7 +233,7 @@ export async function fetchWebPageContent(url: string): Promise<{ title: string;
throw new Error('Fetched PDF is empty');
}

const extracted = await extractText.extractPdf(raw);
const extracted = await extractPdfFromBuffer(raw);
const text = normalizeWhitespace(extracted.text);
if (!text) {
throw new Error('Could not extract readable text from the PDF');
Expand Down
Loading