import type { VocabularyEntry, Session, StoredDocument, OcrPrompts, IpaMode, SyllableMode, } from './types' import { getApiBase } from './constants' /** * Start a new session: create on server, upload document, process first page or PDF. */ export async function startSessionFlow(params: { sessionName: string selectedDocumentId: string | null directFile: File | null selectedMobileFile: { dataUrl: string; type: string; name: string } | null storedDocuments: StoredDocument[] ocrPrompts: OcrPrompts startActivity: (type: string, meta: any) => void setSession: (s: Session | null | ((prev: Session | null) => Session | null)) => void setWorksheetTitle: (t: string) => void setExtractionStatus: (s: string) => void setPdfPageCount: (n: number) => void setSelectedPages: (p: number[]) => void setPagesThumbnails: (t: string[]) => void setIsLoadingThumbnails: (l: boolean) => void setVocabulary: (v: VocabularyEntry[]) => void setActiveTab: (t: 'upload' | 'pages' | 'vocabulary' | 'spreadsheet' | 'worksheet' | 'export' | 'settings') => void setError: (e: string | null) => void }): Promise { const { sessionName, selectedDocumentId, directFile, selectedMobileFile, storedDocuments, ocrPrompts, startActivity, setSession, setWorksheetTitle, setExtractionStatus, setPdfPageCount, setSelectedPages, setPagesThumbnails, setIsLoadingThumbnails, setVocabulary, setActiveTab, setError, } = params setError(null) setExtractionStatus('Session wird erstellt...') const API_BASE = getApiBase() const sessionRes = await fetch(`${API_BASE}/api/v1/vocab/sessions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: sessionName, ocr_prompts: ocrPrompts }), }) if (!sessionRes.ok) throw new Error('Session konnte nicht erstellt werden') const sessionData = await sessionRes.json() setSession(sessionData) setWorksheetTitle(sessionName) startActivity('vocab_extraction', { description: sessionName }) let file: File let isPdf = false if (directFile) { file = directFile isPdf = directFile.type === 'application/pdf' } else if (selectedMobileFile) { isPdf = selectedMobileFile.type === 'application/pdf' const base64Data = selectedMobileFile.dataUrl.split(',')[1] const byteCharacters = atob(base64Data) const byteNumbers = new Array(byteCharacters.length) for (let i = 0; i < byteCharacters.length; i++) byteNumbers[i] = byteCharacters.charCodeAt(i) const blob = new Blob([new Uint8Array(byteNumbers)], { type: selectedMobileFile.type }) file = new File([blob], selectedMobileFile.name, { type: selectedMobileFile.type }) } else { const selectedDoc = storedDocuments.find(d => d.id === selectedDocumentId) if (!selectedDoc || !selectedDoc.url) throw new Error('Das ausgewaehlte Dokument ist nicht verfuegbar.') isPdf = selectedDoc.type === 'application/pdf' const base64Data = selectedDoc.url.split(',')[1] const byteCharacters = atob(base64Data) const byteNumbers = new Array(byteCharacters.length) for (let i = 0; i < byteCharacters.length; i++) byteNumbers[i] = byteCharacters.charCodeAt(i) const blob = new Blob([new Uint8Array(byteNumbers)], { type: selectedDoc.type }) file = new File([blob], selectedDoc.name, { type: selectedDoc.type }) } if (isPdf) { setExtractionStatus('PDF wird hochgeladen...') const formData = new FormData() formData.append('file', file) const pdfInfoRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${sessionData.id}/upload-pdf-info`, { method: 'POST', body: formData, }) if (!pdfInfoRes.ok) throw new Error('PDF konnte nicht verarbeitet werden') const pdfInfo = await pdfInfoRes.json() setPdfPageCount(pdfInfo.page_count) setSelectedPages(Array.from({ length: pdfInfo.page_count }, (_, i) => i)) setActiveTab('pages') setExtractionStatus(`${pdfInfo.page_count} Seiten erkannt. Vorschau wird geladen...`) setIsLoadingThumbnails(true) const thumbnails: string[] = [] for (let i = 0; i < pdfInfo.page_count; i++) { try { const thumbRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${sessionData.id}/pdf-thumbnail/${i}?hires=true`) if (thumbRes.ok) { const blob = await thumbRes.blob(); thumbnails.push(URL.createObjectURL(blob)) } } catch (e) { console.error(`Failed to load thumbnail for page ${i}`) } } setPagesThumbnails(thumbnails) setIsLoadingThumbnails(false) setExtractionStatus(`${pdfInfo.page_count} Seiten bereit. Waehlen Sie die zu verarbeitenden Seiten.`) } else { setExtractionStatus('KI analysiert das Bild... (kann 30-60 Sekunden dauern)') const formData = new FormData() formData.append('file', file) const uploadRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${sessionData.id}/upload`, { method: 'POST', body: formData, }) if (!uploadRes.ok) throw new Error('Bild konnte nicht verarbeitet werden') const uploadData = await uploadRes.json() setSession(prev => prev ? { ...prev, status: 'extracted', vocabulary_count: uploadData.vocabulary_count } : null) const vocabRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${sessionData.id}/vocabulary`) if (vocabRes.ok) { const vocabData = await vocabRes.json() setVocabulary(vocabData.vocabulary || []) setExtractionStatus(`${vocabData.vocabulary?.length || 0} Vokabeln gefunden!`) } await new Promise(r => setTimeout(r, 1000)) setActiveTab('vocabulary') } return sessionData } /** * Resume an existing session from the API. */ export async function resumeSessionFlow( existingSession: Session, setSession: (s: Session) => void, setWorksheetTitle: (t: string) => void, setVocabulary: (v: VocabularyEntry[]) => void, setActiveTab: (t: 'upload' | 'pages' | 'vocabulary' | 'spreadsheet' | 'worksheet' | 'export' | 'settings') => void, setExtractionStatus: (s: string) => void, ): Promise { const API_BASE = getApiBase() const sessionRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${existingSession.id}`) if (!sessionRes.ok) throw new Error('Session nicht gefunden') const sessionData = await sessionRes.json() setSession(sessionData) setWorksheetTitle(sessionData.name) if (sessionData.status === 'extracted' || sessionData.status === 'completed') { const vocabRes = await fetch(`${API_BASE}/api/v1/vocab/sessions/${existingSession.id}/vocabulary`) if (vocabRes.ok) { const vd = await vocabRes.json(); setVocabulary(vd.vocabulary || []) } setActiveTab('vocabulary') setExtractionStatus('') } else if (sessionData.status === 'pending') { setActiveTab('upload') setExtractionStatus('Diese Session hat noch keine Vokabeln. Bitte laden Sie ein Dokument hoch.') } else { setActiveTab('vocabulary') setExtractionStatus('') } }