// src/components/SorlangPage.tsx "use client"; // For Next.js App Router, if applicable import React, { useState, useRef, useTransition, useEffect, useCallback, startTransition, } from "react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Card, CardContent, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { useRouter } from "waku"; import { Spinner } from "./ui/spinner"; import { Loader2 } from "lucide-react"; const SorlangPage: React.FC = () => { const [textValue, setTextValue] = useState(""); const [pastedImageUrl, setPastedImageUrl] = useState(null); const [pastedImageFile, setPastedImageFile] = useState(null); // Store the file for extraction const [isAnalyzing, setIsAnalyzing] = useState(false); const [isExtracting, setIsExtracting] = useState(false); const textareaRef = useRef(null); // Cleanup object URL when component unmounts or image changes useEffect(() => { return () => { if (pastedImageUrl) { URL.revokeObjectURL(pastedImageUrl); } }; }, [pastedImageUrl]); const handlePaste = useCallback( (event: ClipboardEvent) => { const items = event.clipboardData?.items; console.log({ items }); if (!items) return; let imageFound = false; for (const item of items) { if (item.kind === "file" && item.type.startsWith("image/")) { event.preventDefault(); // Prevent pasting image data as text const file = item.getAsFile(); if (file) { if (pastedImageUrl) { URL.revokeObjectURL(pastedImageUrl); // Revoke previous if any } const newImageUrl = URL.createObjectURL(file); setPastedImageUrl(newImageUrl); setPastedImageFile(file); imageFound = true; } break; // Handle first image found } } // // If no image was found, let the default text paste happen // // Or, if you want to explicitly handle text paste: // if (!imageFound) { // // Let the default textarea paste handle it, or: // // event.preventDefault(); // // const text = event.clipboardData.getData('text/plain'); // // setTextValue(prev => prev + text); // Or replace, depending on desired behavior // // setPastedImageUrl(null); // Clear image if text is pasted // // setPastedImageFile(null); // } }, [pastedImageUrl], ); useEffect(() => { window.addEventListener("paste", handlePaste); return () => { window.removeEventListener("paste", handlePaste); }; }, [handlePaste]); const router = useRouter(); async function fetchNLP(text: string, app: "spacy" | "stanza") { const opts = { method: "POST", headers: { "Content-type": "application/json" }, body: JSON.stringify({ text, app }), }; const res = await fetch("/api/nlp", opts); const j = await res.json(); console.log("j", j); if ("ok" in j) { sessionStorage.setItem(`${app}res`, JSON.stringify(j.ok)); } } const handleProcessText = async () => { setIsAnalyzing(true); const text = textValue.trim(); if (!text) { alert("Text area is empty!"); return; } await Promise.all([fetchNLP(text, "spacy"), fetchNLP(text, "stanza")]); router.push("/zoom"); setIsAnalyzing(false); }; // const [isPending, startTransition] = useTransition(); const handleExtractTextFromImage = async () => { if (!pastedImageFile) { alert("No image to extract text from!"); return; } setIsExtracting(true); const formData = new FormData(); formData.append("file", pastedImageFile, pastedImageFile.name); console.log("Extracting text from image:", pastedImageFile.name); const res = await fetch("/api/formdata/ocr", { method: "POST", body: formData, }); const j = await res.json(); console.log("ocr res", j); if ("ok" in j) { setTextValue((t) => t + j.ok.join("\n")); } setIsExtracting(false); // handleClearImage(); }; // setPastedImageUrl(null); // setPastedImageFile(null); const handleClearImage = () => { if (pastedImageUrl) { URL.revokeObjectURL(pastedImageUrl); } setPastedImageUrl(null); setPastedImageFile(null); }; return (
Sorlang