"use client"; import { motion, useReducedMotion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { CameraIcon, FolderIcon, PlayIcon, PlusIcon, SparklesIcon } from "@/components/icons"; import { Button } from "@/components/ui"; import { DOCUMENT_ACCEPT_ATTR, documentsToText, type ExtractedDocument, } from "@/lib/documents"; import type { HomeworkMode, HomeworkRecord } from "@/lib/schemas"; import { ACCEPTED_TYPES, formatBytes, imageFilesFromDataTransfer } from "@/lib/image"; import { useI18n } from "@/lib/i18n"; import type { useNotesIntake } from "@/lib/use-notes-intake"; import { useIsPhone } from "@/lib/use-phone"; import { openSettings } from "@/lib/ui-events"; import { cn, truncate } from "@/lib/utils"; import { HomeworkHistory } from "./HomeworkHistory"; /** Same wording the create screen uses for an extracted document. */ function describeExtraction( document: ExtractedDocument, tr: (key: string, vars?: Record) => string, pl: (n: number, base: string, vars?: Record) => string, ): string { const parts = [ pl(document.words, "upload.words", { n: document.words.toLocaleString() }), document.pages ? pl(document.pages, "upload.pages") : "", document.images.length ? pl(document.images.length, "upload.pageImages") : "", document.truncated ? tr("upload.truncated") : "", ]; return parts.filter(Boolean).join(" · "); } const MODES: Array<{ id: HomeworkMode; icon: React.ReactNode; titleKey: string; subtitleKey: string }> = [ { id: "make", icon: , titleKey: "homework.mode.make.title", subtitleKey: "homework.mode.make.subtitle" }, { id: "guide", icon: , titleKey: "homework.mode.guide.title", subtitleKey: "homework.mode.guide.subtitle" }, ]; export function HomeworkInput({ mode, onModeChange, text, onTextChange, intake, onSubmit, error, history, onOpen, onDelete, }: { mode: HomeworkMode; onModeChange: (mode: HomeworkMode) => void; text: string; onTextChange: (text: string) => void; intake: ReturnType; /** `modeOverride` lets the phone mode cards start the run they tapped. */ onSubmit: (modeOverride?: HomeworkMode) => void; error: { message: string; kind?: string } | null; history: HomeworkRecord[]; onOpen: (record: HomeworkRecord) => void; onDelete: (id: string) => void; }) { const { t, plural } = useI18n(); const fileInputRef = useRef(null); const cameraInputRef = useRef(null); const [step, setStep] = useState(1); const [pasteOpen, setPasteOpen] = useState(false); const isPhone = useIsPhone(); const reduceMotion = useReducedMotion(); // Clipboard: screenshots become photos, long copied text becomes the assignment. useEffect(() => { const onPaste = (event: ClipboardEvent) => { const target = event.target as HTMLElement | null; const isTextField = target && /^(INPUT|TEXTAREA)$/.test(target.tagName); const files = imageFilesFromDataTransfer(event.clipboardData); if (files.length > 0) { event.preventDefault(); void intake.addFiles(files); return; } if (!isTextField) { const pasted = event.clipboardData?.getData("text/plain")?.trim(); if (pasted && pasted.length > 40) { event.preventDefault(); onTextChange(text ? `${text}\n${pasted}` : pasted); } } }; window.addEventListener("paste", onPaste); return () => window.removeEventListener("paste", onPaste); }, [intake, onTextChange, text]); const readyDocuments = intake.documents.filter((entry) => entry.status === "ready" && entry.extracted); const documentsBusy = intake.documents.some((entry) => entry.status === "extracting"); const documentImages = readyDocuments.flatMap((entry) => entry.extracted!.images); const documentText = documentsToText(readyDocuments.map((entry) => entry.extracted!)); const totalBytes = intake.images.reduce((sum, image) => sum + image.bytes, 0); const hasContent = intake.images.length > 0 || documentImages.length > 0 || text.trim().length > 0 || documentText.trim().length > 0; const canSubmit = hasContent && !intake.busy && !documentsBusy; const keyError = error?.kind === "needs_key" || error?.kind === "auth"; const errorCard = error ? (

{keyError ? t("upload.errAuth") : t("homework.error.title")}

{error.message || t("homework.error.body")}

{keyError ? ( ) : null}
) : null; /* Shared mode card: desktop toggles the selection, the phone flow starts it. */ const modeCard = (option: (typeof MODES)[number], onPick: () => void) => { const selected = mode === option.id; return ( ); }; const fileInput = ( { void intake.addFiles(Array.from(event.target.files ?? [])); event.target.value = ""; }} /> ); const cameraInput = ( { void intake.addFiles(Array.from(event.target.files ?? [])); event.target.value = ""; }} /> ); /* Phones: the 2-step flow. Same intake and mode state as the desktop page. */ if (isPhone) { const stepLabel = step === 1 ? t("homework.phone.assignment") : t("homework.phone.mode"); const fileSummary = [ intake.images.length > 0 ? plural(intake.images.length, "upload.photoCount") : "", intake.documents.length > 0 ? plural(intake.documents.length, "upload.docSummary") : "", ] .filter(Boolean) .join(" · "); const preview = text.trim() ? truncate(text.replace(/\s+/g, " ").trim(), 140) : fileSummary || t("homework.source.need"); const showPaste = pasteOpen || text.trim().length > 0; return (

{t("homeworkHeroTitle")}

{t("homework.subtitle")}

{errorCard}

{stepLabel}

{t("common.stepOf", { n: step, total: 2 })}
{step === 1 ? (
{showPaste ? (