import { useEffect, useState, useRef } from 'react' import InlineResult from './components/InlineResult' import { useStreaming } from './hooks/useStreaming' import logoSvg from './assets/logo.svg' import { API_BASE } from './config' import './App.css' function App() { const [activeTab, setActiveTab] = useState('youtube') const [youtubeUrl, setYoutubeUrl] = useState('') const [transcript, setTranscript] = useState('') const [selectedFile, setSelectedFile] = useState(null) const [models, setModels] = useState([]) const [selectedModel, setSelectedModel] = useState('') const fileInputRef = useRef(null) const { loading, response, error, streamingText, submit } = useStreaming() useEffect(() => { let cancelled = false ;(async () => { try { const res = await fetch(`${API_BASE}/models`) if (!res.ok) return const data = await res.json() if (cancelled) return const available = Array.isArray(data.available) ? data.available : [] setModels(available) const serverDefault = typeof data.default === 'string' ? data.default : '' setSelectedModel((prev) => prev || serverDefault || available[0] || '') } catch { // Non-fatal: model list stays empty; backend will still pick default if model omitted. } })() return () => { cancelled = true } }, []) const handleSubmit = () => submit(activeTab, { youtubeUrl, transcript, selectedFile, selectedModel: selectedModel || undefined, }) const handleFileDrop = (e) => { e.preventDefault() e.stopPropagation() const file = e.dataTransfer?.files[0] || e.target.files?.[0] if (file && file.name.endsWith('.txt')) setSelectedFile(file) else if (file) alert('Only .txt files are supported') } const formatFileSize = (bytes) => { if (bytes < 1024) return bytes + ' bytes' if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB' return (bytes / (1024 * 1024)).toFixed(1) + ' MB' } const ctrlEnter = (e) => { if (e.key === 'Enter' && e.ctrlKey && !loading) handleSubmit() } const resultProps = { error, loading, response, streamingText, selectedModel } return ( <>
Précis Précis
API Docs

Summarize Content

Upload a YouTube video, paste a transcript, or drop a text file to generate a summary.

Upload Content
{[['youtube', 'YouTube Video'], ['transcript', 'Article / Transcript'], ['file', 'Text File']].map(([key, label]) => ( ))}
{/* YouTube */}
setYoutubeUrl(e.target.value)} onKeyDown={ctrlEnter} />

Paste a YouTube URL. Ctrl+Enter to generate.

{activeTab === 'youtube' && ( )}
{/* Transcript */}