This commit is contained in:
2026-04-12 01:02:14 +08:00
parent 509487f155
commit 9b053e302b
14085 changed files with 2680009 additions and 12 deletions

View File

@@ -0,0 +1,152 @@
"use client";
import { useCallback, useRef, useState } from "react";
import { ChatMessages } from "@/components/chat/chat-messages";
import { ChatInput } from "@/components/chat/chat-input";
import { sendChat, type Message } from "@/lib/api";
export default function Home() {
const [messages, setMessages] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [streamingText, setStreamingText] = useState("");
const [streamingImages, setStreamingImages] = useState<string[]>([]);
const [statusText, setStatusText] = useState("");
const scrollRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
setTimeout(() => {
scrollRef.current?.scrollTo({
top: scrollRef.current.scrollHeight,
behavior: "smooth",
});
}, 50);
};
const handleSend = useCallback(
async (text: string, refImage: File | null) => {
// 添加用户消息
const userMessage: Message = { role: "user", content: text };
const updatedMessages = [...messages, userMessage];
setMessages(updatedMessages);
setIsLoading(true);
setStreamingText("");
setStreamingImages([]);
setStatusText("");
scrollToBottom();
let assistantText = "";
let collectedImages: string[] = [];
try {
for await (const event of sendChat(updatedMessages, refImage)) {
switch (event.type) {
case "text_delta":
assistantText += event.data.text as string;
setStreamingText(assistantText);
scrollToBottom();
break;
case "tool_start":
setStatusText(event.data.message as string);
scrollToBottom();
break;
case "image_result":
collectedImages = [
...collectedImages,
...(event.data.images as string[]),
];
setStreamingImages(collectedImages);
setStatusText("");
scrollToBottom();
break;
case "error":
assistantText += `\n\n[错误: ${event.data.message}]`;
setStreamingText(assistantText);
break;
case "done":
break;
}
}
} catch (e) {
assistantText += `\n\n[请求失败: ${e instanceof Error ? e.message : "未知错误"}]`;
}
// 完成:将流式内容合并为正式消息
const assistantMessage: Message = {
role: "assistant",
content: assistantText || "(生成完成)",
images: collectedImages.length > 0 ? collectedImages : undefined,
};
setMessages([...updatedMessages, assistantMessage]);
setIsLoading(false);
setStreamingText("");
setStreamingImages([]);
setStatusText("");
scrollToBottom();
},
[messages]
);
return (
<div className="h-screen flex flex-col">
{/* 标题栏 */}
<header className="flex-shrink-0 border-b border-[var(--border)] bg-[var(--bg-secondary)] px-6 py-3">
<h1 className="text-lg font-semibold">Art Agent</h1>
<p className="text-xs text-[var(--text-secondary)]">
AI · MVP
</p>
</header>
{/* 消息区域 */}
<div ref={scrollRef} className="flex-1 overflow-y-auto">
{messages.length === 0 && !isLoading ? (
<div className="h-full flex items-center justify-center">
<div className="text-center space-y-3">
<div className="text-4xl">🎨</div>
<h2 className="text-xl font-medium text-[var(--text-primary)]">
使 Art Agent
</h2>
<p className="text-sm text-[var(--text-secondary)] max-w-md">
<br />
</p>
<div className="flex flex-wrap justify-center gap-2 mt-4">
{[
"画一个赛博朋克风格的退出按钮",
"设计一个卡通风格的金币图标",
"画一个奇幻风格的游戏角色立绘",
].map((hint) => (
<button
key={hint}
onClick={() => handleSend(hint, null)}
className="text-xs px-3 py-1.5 rounded-full
border border-[var(--border)] text-[var(--text-secondary)]
hover:border-[var(--accent)] hover:text-[var(--accent)]
transition-colors cursor-pointer"
>
{hint}
</button>
))}
</div>
</div>
</div>
) : (
<ChatMessages
messages={messages}
isLoading={isLoading}
streamingText={streamingText}
streamingImages={streamingImages}
statusText={statusText}
/>
)}
</div>
{/* 输入区域 */}
<ChatInput onSend={handleSend} disabled={isLoading} />
</div>
);
}