39181f21ad
ResultAI had h-0 collapsing output; add X-Accel-Buffering no, clearer fetch errors, and NGINX.md for gate proxy setup. Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import React, { useEffect, useRef, useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { RotateCw } from "lucide-react";
|
|
import Markdown from "react-markdown";
|
|
|
|
function ResultAI({
|
|
completion,
|
|
isLoading,
|
|
onCompletion,
|
|
error,
|
|
}: {
|
|
completion: string;
|
|
isLoading: boolean;
|
|
onCompletion: () => void;
|
|
error: string;
|
|
}) {
|
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
const [autoScroll, setAutoScroll] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setAutoScroll(isLoading);
|
|
}, [isLoading]);
|
|
|
|
useEffect(() => {
|
|
if (!autoScroll) {
|
|
return;
|
|
}
|
|
scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight);
|
|
}, [completion, autoScroll]);
|
|
|
|
function onScroll(e: HTMLElement) {
|
|
if (!isLoading) {
|
|
return;
|
|
}
|
|
const hitBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 15;
|
|
if (hitBottom !== autoScroll) {
|
|
setAutoScroll(hitBottom);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex w-full flex-col gap-2">
|
|
{isLoading && (
|
|
<div className="flex items-center text-sm text-muted-foreground">
|
|
<RotateCw size={16} className="animate-spin" />
|
|
<span className="ml-1">AI 分析中...</span>
|
|
</div>
|
|
)}
|
|
<div
|
|
ref={scrollRef}
|
|
onScroll={(e) => onScroll(e.currentTarget)}
|
|
className="min-h-[240px] max-h-[420px] overflow-y-auto rounded-md border bg-background p-3 shadow sm:p-5 dark:border-0 dark:bg-secondary/90 dark:shadow-none"
|
|
>
|
|
{error ? (
|
|
<div className="text-sm text-destructive">
|
|
<p className="font-medium">请求出错了</p>
|
|
<p className="mt-1 whitespace-pre-wrap">{error}</p>
|
|
</div>
|
|
) : completion ? (
|
|
<Markdown className="prose max-w-none dark:prose-invert">
|
|
{completion}
|
|
</Markdown>
|
|
) : isLoading ? (
|
|
<p className="text-sm text-muted-foreground">正在等待 AI 响应...</p>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">暂无解读内容</p>
|
|
)}
|
|
{!isLoading && (
|
|
<Button onClick={onCompletion} size="sm" className="mt-3">
|
|
<RotateCw size={18} className="mr-1" />
|
|
重新生成
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ResultAI;
|