From 1ae274a658d0a705b698a8873c286ec73403b1a6 Mon Sep 17 00:00:00 2001 From: polwex Date: Thu, 15 May 2025 12:17:54 +0700 Subject: m --- components.json | 21 ++++ src/actions/lang.ts | 12 +++ src/actions/login.ts | 19 ++-- src/components/Main.tsx | 227 +++++++++++++++++++++++++++++++++++++++++ src/components/Profile.tsx | 36 +++++++ src/components/ui/textarea.tsx | 18 ++++ src/pages/index.tsx | 18 +--- src/pages/login.tsx | 14 +-- src/styles/globals.css | 124 ++++++++++++++++++++++ 9 files changed, 461 insertions(+), 28 deletions(-) create mode 100644 components.json create mode 100644 src/actions/lang.ts create mode 100644 src/components/Main.tsx create mode 100644 src/components/Profile.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/styles/globals.css diff --git a/components.json b/components.json new file mode 100644 index 0000000..285033d --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/src/actions/lang.ts b/src/actions/lang.ts new file mode 100644 index 0000000..d54a220 --- /dev/null +++ b/src/actions/lang.ts @@ -0,0 +1,12 @@ +"use server"; +import { AsyncRes } from "@/lib/types"; +import db from "../lib/db"; + +export async function spacy(text: string, lang: string): AsyncRes { + const res = await call(formData, true); + console.log("reg res", res); + if ("error" in res) return { error: "Something went wrong" }; + else { + return { success: true }; + } +} diff --git a/src/actions/login.ts b/src/actions/login.ts index 7ca9d77..4ceb13f 100644 --- a/src/actions/login.ts +++ b/src/actions/login.ts @@ -38,13 +38,14 @@ export async function postLogin( prevState: FormState, formData: FormData, ): Promise { - const res = await call(formData, false); - if ("error" in res) return { error: res.error }; - else { - setCookie(res.ok as number); - return prevState; - // return { success: true }; - } + return prevState; + // const res = await call(formData, false); + // if ("error" in res) return { error: res.error }; + // else { + // setCookie(res.ok as number); + // return prevState; + // // return { success: true }; + // } } async function setCookie(userId: number) { const COOKIE_EXPIRY = Date.now() + 1000 * 60 * 60 * 24 * 30; @@ -58,8 +59,8 @@ async function setCookie(userId: number) { // unstable_redirect("/"); } -async function postLogout(prev: any) { +export async function postLogout(prev: number) { const { delCookie } = cookies(); const rest = delCookie("sorlang"); - return prev; + return prev + 9; } diff --git a/src/components/Main.tsx b/src/components/Main.tsx new file mode 100644 index 0000000..ee8dbab --- /dev/null +++ b/src/components/Main.tsx @@ -0,0 +1,227 @@ +// 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 { Loader2 } from "lucide-react"; // Loading spinner + +const SorlangPage: React.FC = () => { + const [textValue, setTextValue] = useState(""); + const [pastedImageUrl, setPastedImageUrl] = useState(null); + const [pastedImageFile, setPastedImageFile] = useState(null); // Store the file for extraction + const [isExtracting, setIsExtracting] = useState(false); + const [extractedTextResult, setExtractedTextResult] = useState( + null, + ); + + 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: React.ClipboardEvent) => { + const items = event.clipboardData?.items; + console.log({ items }); + if (!items) return; + + let imageFound = false; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (!item) return; + 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); + setTextValue(""); // Clear textarea when image is pasted, or decide on desired behavior + setExtractedTextResult(null); // Clear previous extraction results + 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], + ); + + const handleProcessText = () => { + if (!textValue.trim()) { + alert("Text area is empty!"); + return; + } + console.log("Processing text:", textValue); + // Add your text processing logic here + alert( + `Text submitted: "${textValue.substring(0, 50)}${textValue.length > 50 ? "..." : ""}"`, + ); + }; + + const [isPending, startTransition] = useTransition(); + const onClick = () => { + startTransition(async() => { + const + }) + } + const handleExtractTextFromImage = async () => { + if (!pastedImageFile) { + alert("No image to extract text from!"); + return; + } + setIsExtracting(true); + setExtractedTextResult(null); + console.log("Extracting text from image:", pastedImageFile.name); + + // --- SIMULATE OCR API CALL --- + // In a real app, you would send `pastedImageFile` to a backend + // or use a client-side OCR library like Tesseract.js + await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate network delay + + // Example: Simulate successful extraction + const mockExtractedText = `This is simulated extracted text from "${pastedImageFile.name}".\nIt could be multiple lines.`; + + // Example: Simulate an error + // const mockExtractedText = null; + // alert("Failed to extract text (simulated)."); + + if (mockExtractedText) { + setTextValue(mockExtractedText); // Put extracted text into the textarea + setExtractedTextResult( + `Successfully extracted text and placed it in the textarea.`, + ); + } else { + setExtractedTextResult("Failed to extract text (simulated)."); + } + // --- END SIMULATION --- + + setIsExtracting(false); + // Optionally clear the image after attempting extraction + // setPastedImageUrl(null); + // setPastedImageFile(null); + }; + + const handleClearImage = () => { + if (pastedImageUrl) { + URL.revokeObjectURL(pastedImageUrl); + } + setPastedImageUrl(null); + setPastedImageFile(null); + setExtractedTextResult(null); + }; + + return ( +
+ + + + Sorlang + + + +