ai-chatbot/components/canvas/PromptTranslateButton.tsx

79 lines
2.5 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
export function PromptTranslateButton({
prompt,
model,
onApply,
}: {
prompt: string;
model?: string;
onApply: (text: string) => void;
}) {
const [busy, setBusy] = useState(false),
[result, setResult] = useState("");
const controller = useRef<AbortController | null>(null);
useEffect(() => () => controller.current?.abort(), []);
return (
<>
<button
type="button"
disabled={busy || !prompt.trim()}
className="rounded px-2 py-1 text-sm disabled:opacity-40"
onClick={async () => {
const request = new AbortController();
controller.current = request;
setBusy(true);
try {
const response = await fetch("/api/canvas/prompt-tools", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mode: "translate", prompt, model }),
signal: AbortSignal.any([request.signal, AbortSignal.timeout(100000)]),
});
const body = await response.json();
if (!response.ok || !body.success || !body.data?.text)
throw Error(body.error || "翻译没有返回结果");
if (!request.signal.aborted) setResult(body.data.text);
} catch (error) {
if (!request.signal.aborted)
toast.error(error instanceof Error ? error.message : "翻译失败");
} finally {
setBusy(false);
}
}}
>
{busy ? "翻译中…" : "翻译"}
</button>
{result ? (
<div
role="dialog"
aria-label="翻译结果"
className="fixed inset-4 z-[700] flex flex-col gap-3 rounded-xl border border-white/20 bg-[#222] p-5 text-white"
>
<textarea
aria-label="翻译内容"
className="min-h-0 flex-1 resize-none bg-transparent"
value={result}
onChange={(event) => setResult(event.target.value)}
/>
<div className="flex justify-end gap-3">
<button type="button" onClick={() => setResult("")}>
放弃
</button>
<button
type="button"
onClick={() => {
onApply(result);
setResult("");
}}
>
应用翻译
</button>
</div>
</div>
) : null}
</>
);
}