"use client";
import { useState, useEffect, useRef } from "react";
import { useTheme } from "next-themes";
import {
Code,
Eye,
Send,
Download,
Copy,
Maximize2,
Minimize2,
FileCode2,
X,
Github,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Message, PreviewState } from "@/lib/types";
// @ts-expect-error
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { customOneDark, customOneLight, generateUUID } from "@/lib/utils";
import DynamicFileRenderer from "@/webzero/preview";
import ThemeToggle from "@/components/ThemeToggle";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { useSessionContext } from "@/context/SessionContext";
function CodeMessageCard({
version,
onClick,
}: {
version: number;
onClick: () => void;
}) {
return (
);
}
const sendMessage = async (content: string) => {
const response = await fetch("/api/new", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ content }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Failed to send message");
}
const data = await response.json();
return data;
};
const sendIteration = async (
previousDescription: string,
newUpdate: string,
currentCode: string
) => {
const response = await fetch("/api/iterate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ previousDescription, newUpdate, currentCode }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Failed to process iteration");
}
const data = await response.json();
return data;
};
export function ChatPage() {
const [message, setMessage] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [currentIteration, setCurrentIteration] = useState<{
previousDescription: string;
currentCode: string;
} | null>(null);
const [previewState, setPreviewState] = useState({
isFullscreen: false,
isOpen: false,
});
const [selectedAIMessage, setSelectedAIMessage] = useState(
null
);
const { theme } = useTheme();
const isDark = theme === "dark";
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
const { sessions, currentSessionId, updateSessionMessages } =
useSessionContext();
const currentSession = sessions.find(
(session) => session.id === currentSessionId
);
useEffect(() => {
setSelectedAIMessage(null);
setPreviewState((prev) => ({
...prev,
isOpen: false,
isFullscreen: false,
}));
if (currentSession && currentSession.messages.length > 0) {
const lastAIMessage = currentSession.messages
.slice()
.reverse()
.find((msg) => msg.from === "ai");
if (lastAIMessage) {
const precedingUserMessage =
currentSession.messages[
currentSession.messages.indexOf(lastAIMessage) - 1
];
if (precedingUserMessage && precedingUserMessage.from === "user") {
setCurrentIteration({
previousDescription: precedingUserMessage.content,
currentCode: lastAIMessage.content,
});
} else {
setCurrentIteration(null);
}
} else {
setCurrentIteration(null);
}
} else {
setCurrentIteration(null);
}
}, [currentSessionId]);
useEffect(() => {
scrollToBottom();
}, [currentSession?.messages]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!message.trim() || !currentSessionId) return;
setIsLoading(true);
const newUserMessage: Message = {
id: generateUUID().toString(),
from: "user",
content: message,
};
const updatedMessages = [
...(currentSession?.messages || []),
newUserMessage,
];
updateSessionMessages(currentSessionId, updatedMessages);
setMessage("");
try {
let response;
if (currentIteration) {
response = await sendIteration(
currentIteration.previousDescription,
message,
currentIteration.currentCode
);
} else {
response = await sendMessage(message);
}
updateSessionMessages(currentSessionId, [...updatedMessages, response]);
setCurrentIteration({
previousDescription: message,
currentCode: response.content,
});
setSelectedAIMessage(response);
setPreviewState((prev) => ({ ...prev, isOpen: true }));
} catch (error) {
console.error("Error:", error);
} finally {
setIsLoading(false);
}
};
const handleCopyCode = async (code: string) => {
try {
await navigator.clipboard.writeText(code);
} catch (error) {
console.error("Failed to copy code:", error);
}
};
const handleDownloadCode = (code: string, filename: string) => {
const blob = new Blob([code], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
};
const toggleFullscreen = () => {
setPreviewState((prev) => ({
...prev,
isFullscreen: !prev.isFullscreen,
}));
};
return (
{!currentSession || currentSession.messages.length === 0 ? (
) : (
{currentSession.messages.map((msg, index) => (
{msg.from === "ai" && (
"AI"
)}
{msg.from === "user" ? (
{msg.content}
) : (
{
setSelectedAIMessage(msg);
setPreviewState((prev) => ({
...prev,
isOpen: true,
}));
}}
/>
)}
))}
{previewState.isOpen && (
<>
Preview
Code
{previewState.isFullscreen && }
{!previewState.isFullscreen && (
)}
{selectedAIMessage?.content || ""}
>
)}
)}
);
}