From 9151df3851ed7e03d3b82a89a1bc7e8d5a579878 Mon Sep 17 00:00:00 2001 From: mertksk Date: Thu, 10 Apr 2025 21:13:39 +0300 Subject: [PATCH 1/3] Add files via upload --- README.md | 25 +- app/(main)/layout.tsx | 19 +- app/(main)/page.tsx | 282 ++++++++++++++++---- app/api/downloadProject/route.ts | 72 ++++++ app/api/generateCode/route.ts | 30 ++- app/layout.tsx | 14 +- components/Footer.tsx | 51 +--- components/Header.tsx | 25 +- components/ThemeProvider.tsx | 34 ++- components/ThemeToggle.tsx | 5 +- components/code-viewer.css | 25 +- next-env.d.ts | 5 + package-lock.json | 428 +++++++++++++++++++++++++++---- package.json | 2 + public/logo.png | Bin 0 -> 36815 bytes tailwind.config.ts | 5 +- 16 files changed, 815 insertions(+), 207 deletions(-) create mode 100644 app/api/downloadProject/route.ts create mode 100644 next-env.d.ts create mode 100644 public/logo.png diff --git a/README.md b/README.md index 8f359581e..fe404c79d 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,7 @@ -

InstantCoder

+

Codi Mini

- Generate small apps with one prompt. Powered by the Gemini API. + Generate small apps with one prompt. Powered by the Codinera

-Try it in https://huggingface.co/spaces/osanseviero/InstantCoder - -This project is fully based on [llamacoder](https://github.com/Nutlope/llamacoder). Please follow [Nutlope](https://github.com/Nutlope) and give them a star. - -## Tech stack - -- [Gemini API](https://ai.google.dev/gemini-api/docs) to use Gemini 1.5 Pro, Gemini 1.5 Flash, and Gemini 2.0 Flash Experimental -- [Sandpack](https://sandpack.codesandbox.io/) for the code sandbox -- Next.js app router with Tailwind - -You can also experiment with Gemini in [Google AI Studio](https://aistudio.google.com/). - -## Cloning & running - -1. Clone the repo: `git clone https://github.com/osanseviero/GemCoder` -2. Create a `.env` file and add your [Google AI Studio API key](https://aistudio.google.com/app/apikey): `GOOGLE_AI_API_KEY=` -3. Run `npm install` and `npm run dev` to install dependencies and run locally - -**This is a personal project and not a Google official project** + + \ No newline at end of file diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index a99f04ec4..47eb46d59 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -10,7 +10,9 @@ export default function Layout({ children: React.ReactNode; }>) { return ( - + // Removed flex properties from body + + {/* Background elements */}
-
-
-
- -
+ {/* Main content container - Removed min-h-screen */} +
+ {/* 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

-
-
+ {/* Adjusted background blur */} +
+ {/* Adjusted background, text, placeholder, focus colors */} +