"use client"; import { useCallback, useState, type ChangeEvent } from "react"; import { hasUsableSamples, type StyleLearnMode } from "@/lib/style/style"; import { useToast } from "@/components/Toast"; import { Button } from "@/components/ui/Button"; import { Field } from "@/components/ui/Field"; import { StatusNote } from "@/components/ui/StatusNote"; import { TextArea } from "@/components/ui/TextArea"; import { buttonClass } from "@/lib/ui/variants"; interface StyleUploadProps { busy: boolean; pollStatus: "idle" | "polling" | "done" | "error"; progress: number; // 是否已有指纹(决定默认 mode = update)。 hasFingerprint: boolean; onLearn: (samples: string[], mode: StyleLearnMode) => void; } // 学文风输入(UX §6.9):多段文本粘贴 + 文件选择(在浏览器读成文本)。 // 样本以正文文本入 body(无对象存储,确认决策)。 export function StyleUpload({ busy, pollStatus, progress, hasFingerprint, onLearn, }: StyleUploadProps) { const [text, setText] = useState(""); const toast = useToast(); // 读取选中的文本文件,追加到文本框(多文件用空行分隔)。 const onFiles = useCallback( async (e: ChangeEvent): Promise => { const files = Array.from(e.target.files ?? []); if (files.length === 0) return; try { const contents = await Promise.all(files.map((f) => f.text())); setText((prev) => { const joined = contents.join("\n\n"); return prev.trim().length > 0 ? `${prev}\n\n${joined}` : joined; }); } catch { toast("读取文件失败,请改用粘贴。", "error"); } finally { e.target.value = ""; // 允许重复选同一文件。 } }, [toast], ); const submit = (): void => { // 以空行切分成多段样本(对齐后端 samples:list[str])。 const samples = text .split(/\n{2,}/) .map((s) => s.trim()) .filter((s) => s.length > 0); if (!hasUsableSamples(samples)) { toast("请粘贴或选择至少一段样本正文。", "error"); return; } onLearn(samples, hasFingerprint ? "update" : "create"); }; return (