+ {/* Theme Toggle */}
+
+
+
+
+ {/* Width constraint and centering for content */}
+
+ {/* Children (page content) */}
{children}
diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx
index 15bb0f0e8..0fa33f560 100644
--- a/app/(main)/page.tsx
+++ b/app/(main)/page.tsx
@@ -4,9 +4,9 @@ import CodeViewer from "@/components/code-viewer";
import { useScrollTo } from "@/hooks/use-scroll-to";
import { CheckIcon } from "@heroicons/react/16/solid";
import { ArrowLongRightIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
-import { ArrowUpOnSquareIcon } from "@heroicons/react/24/outline";
+import { ArrowUpOnSquareIcon, ArrowDownTrayIcon } from "@heroicons/react/24/outline";
import * as Select from "@radix-ui/react-select";
-import * as Switch from "@radix-ui/react-switch";
+import * as Switch from "@radix-ui/react-switch"; // Keep Switch import if needed elsewhere, otherwise remove
import { AnimatePresence, motion } from "framer-motion";
import { FormEvent, useEffect, useState } from "react";
import LoadingDots from "../../components/loading-dots";
@@ -20,32 +20,47 @@ export default function Home() {
"initial" | "creating" | "created" | "updating" | "updated"
>("initial");
let [prompt, setPrompt] = useState("");
- let models = [
+ // Define model type including optional disabled flag
+ type ModelOption = {
+ label: string;
+ value: string;
+ disabled?: boolean;
+ };
+ // Updated models array with speed labels
+ let models: ModelOption[] = [
{
- label: "gemini-2.0-flash-exp",
+ label: "gemini-2.0-flash-exp (Fast)", // Added speed label
value: "gemini-2.0-flash-exp",
},
{
- label: "gemini-1.5-pro",
- value: "gemini-1.5-pro",
+ label: "gemini-2.5-pro-exp-03-25 (Slow)", // Added speed label
+ value: "gemini-2.5-pro-exp-03-25",
},
{
- label: "gemini-1.5-flash",
- value: "gemini-1.5-flash",
+ label: "Deepseek R1 (Coming Soon)",
+ value: "deepseek-r1",
+ disabled: true, // Mark as disabled
}
];
- let [model, setModel] = useState(models[0].value);
+ let [model, setModel] = useState(models[0].value); // Default to the first available model
let [modification, setModification] = useState("");
let [generatedCode, setGeneratedCode] = useState("");
let [initialAppConfig, setInitialAppConfig] = useState({
model: "",
});
let [ref, scrollTo] = useScrollTo();
+ // Store conversation history including user prompts and assistant (code) responses
let [messages, setMessages] = useState<{ role: string; content: string }[]>(
[],
);
+ // Add state for forcing CodeViewer re-render
+ let [viewerKey, setViewerKey] = useState(0);
+ // Add state to control code editor visibility
+ let [showCodeEditorView, setShowCodeEditorView] = useState(false);
let loading = status === "creating" || status === "updating";
+ // Condition to show the viewer/modification section
+ let shouldShowViewerSection = status === "created" || status === "updated" || status === "updating";
async function createApp(e: FormEvent
) {
e.preventDefault();
@@ -78,6 +93,7 @@ export default function Home() {
const reader = res.body.getReader();
let receivedData = "";
+ let cleanedData = ""; // Declare cleanedData outside the loop
while (true) {
const { done, value } = await reader.read();
@@ -85,15 +101,91 @@ export default function Home() {
break;
}
receivedData += new TextDecoder().decode(value);
- const cleanedData = removeCodeFormatting(receivedData);
+ // Assign to the outer cleanedData variable (fix duplicated line)
+ cleanedData = removeCodeFormatting(receivedData);
setGeneratedCode(cleanedData);
}
- setMessages([{ role: "user", content: prompt }]);
+ // Use the outer cleanedData variable here
+ // Set messages only once with both user prompt and assistant response
+ setMessages([
+ { role: "user", content: prompt },
+ { role: "assistant", content: cleanedData },
+ ]);
setInitialAppConfig({ model });
setStatus("created");
+ setShowCodeEditorView(false); // Reset to preview-only view on new creation
+ // Removed setViewerKey from here
}
+ async function updateApp(e: FormEvent) {
+ e.preventDefault();
+
+ // Check status directly instead of using the removed variable
+ if (!(status === 'created' || status === 'updated') || !modification) return;
+
+ scrollTo({ delay: 0.5 });
+ setStatus("updating");
+
+ // Prepare messages for the API, including history and the new modification
+ const apiMessages = [
+ ...messages,
+ { role: "user", content: modification },
+ ];
+
+ let res = await fetch("/api/generateCode", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ model: initialAppConfig.model || model, // Use initial model for consistency
+ messages: apiMessages,
+ }),
+ });
+
+ if (!res.ok) {
+ // TODO: Handle error display better
+ console.error("Update failed:", res.statusText);
+ setStatus("created"); // Revert status on failure
+ return;
+ }
+
+ if (!res.body) {
+ throw new Error("No response body");
+ }
+
+ const reader = res.body.getReader();
+ let receivedData = "";
+ let finalCleanedCode = ""; // Variable to hold the final code
+ // Don't clear generatedCode here; let the key change handle the refresh
+
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) {
+ break;
+ }
+ receivedData += new TextDecoder().decode(value);
+ // Only update the temporary variable during the stream
+ finalCleanedCode = removeCodeFormatting(receivedData);
+ }
+
+ // Set generatedCode state ONLY ONCE after the loop finishes
+ setGeneratedCode(finalCleanedCode);
+
+ // Add the modification prompt and the new assistant response to messages
+ setMessages([
+ ...apiMessages,
+ // Use finalCleanedCode here too for consistency
+ { role: "assistant", content: finalCleanedCode },
+ ]);
+ setModification(""); // Clear modification input
+ setStatus("updated");
+ setShowCodeEditorView(true); // Ensure editor stays visible after update
+ // Removed setViewerKey from here
+ }
+
+ // Effect to scroll the code viewer
useEffect(() => {
let el = document.querySelector(".cm-scroller");
if (el && loading) {
@@ -102,27 +194,42 @@ export default function Home() {
}
}, [loading, generatedCode]);
+ // Effect to update viewerKey AFTER code generation/update is complete
+ useEffect(() => {
+ // Removed console logs
+ if (status === 'created' || status === 'updated') {
+ setViewerKey(prev => prev + 1);
+ }
+ }, [generatedCode, status]); // Depend on generatedCode and status
+
+ // Removed console log
+
return (
-
+ // Added flex-grow to main element
+
- Powered by Gemini API
+ Powered by Codinera
-
- Turn your idea
-
into an app
+ {/* Adjusted text colors */}
+
+ Turn your idea
+
into an app