()
| 2 | import { smartnotesStart, connectSmartnotesStream, type SmartNotesEvent } from "../../lib/api" |
| 3 | |
| 4 | export default function SmartNotes() { |
| 5 | const [topic, setTopic] = useState("") |
| 6 | const [busy, setBusy] = useState(false) |
| 7 | const [status, setStatus] = useState("") |
| 8 | const [filePath, setFilePath] = useState<string | null>(null) |
| 9 | |
| 10 | const onGenerate = async () => { |
| 11 | if (!topic.trim() || busy) return |
| 12 | setBusy(true) |
| 13 | setStatus("Starting…") |
| 14 | setFilePath(null) |
| 15 | |
| 16 | try { |
| 17 | const { noteId } = await smartnotesStart({ topic }) |
| 18 | const { close } = connectSmartnotesStream(noteId, (ev: SmartNotesEvent) => { |
| 19 | if (ev.type === "phase") setStatus(`Status: ${ev.value}`) |
| 20 | if (ev.type === "file") { setFilePath(ev.file); setStatus("Ready") } |
| 21 | if (ev.type === "done") { setStatus("Done"); close(); setBusy(false) } |
| 22 | if (ev.type === "error") { setStatus(`Error: ${ev.error}`); close(); setBusy(false) } |
| 23 | }) |
| 24 | } catch (e: any) { |
| 25 | setStatus(e.message || "Failed") |
| 26 | setBusy(false) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | return ( |
| 31 | <div className="group rounded-2xl bg-stone-950 border border-zinc-800 p-4 hover:border-sky-500/50 transition-all duration-300 hover:shadow-lg hover:shadow-violet-500/10"> |
| 32 | <div className="flex items-start justify-between gap-3"> |
| 33 | <div className="flex-1"> |
| 34 | <div className="flex items-center gap-2 mb-1"> |
| 35 | <div className="text-xs uppercase tracking-wide text-blue-400 font-semibold">notes generator</div> |
| 36 | <div className="w-2 h-2 rounded-full bg-gradient-to-r from-blue-400 to-sky-400 animate-pulse"></div> |
| 37 | </div> |
| 38 | <div className="text-white font-semibold text-xl mb-2">SmartNotes</div> |
| 39 | <div className="text-stone-300 text-sm leading-relaxed"> |
| 40 | Transform any topic into comprehensive, structured study notes. Perfect for exam prep and research. |
| 41 | </div> |
| 42 | </div> |
| 43 | </div> |
| 44 | |
| 45 | <div className="mt-6 space-y-3"> |
| 46 | <div className="flex gap-2"> |
| 47 | <div className="relative flex-1"> |
| 48 | <input |
| 49 | value={topic} |
| 50 | onChange={(e) => setTopic(e.target.value)} |
| 51 | placeholder="Enter your topic..." |
| 52 | className="w-full px-4 py-3 pr-16 rounded-xl bg-stone-900/70 border border-zinc-700 text-white placeholder-zinc-400 focus:border-sky-500 focus:ring-2 focus:ring-sky-500/20 outline-none transition-all duration-300" |
| 53 | onKeyDown={(e) => e.key === "Enter" && onGenerate()} |
| 54 | /> |
| 55 | </div> |
| 56 | <button |
| 57 | onClick={onGenerate} |
| 58 | disabled={busy || !topic.trim()} |
| 59 | className="px-6 py-3 rounded-xl bg-gradient-to-r from-sky-500 to-blue-500 hover:from-sky-600 hover:to-blue-600 disabled:opacity-50 disabled:cursor-not-allowed text-white font-medium transition-all duration-300" |
| 60 | > |
| 61 | {busy ? "Generating…" : "Generate"} |
nothing calls this directly
no test coverage detected