diff --git a/.py b/.py
new file mode 100644
index 000000000..ded9a0078
--- /dev/null
+++ b/.py
@@ -0,0 +1,20 @@
+from openai import OpenAI
+
+client = OpenAI(
+ api_key="AIzaSyCRLceQ6TDxRrZVhNZT94gdS_o4wmEu2cA",
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
+)
+
+response = client.chat.completions.create(
+ model="gemini-2.5-flash",
+ reasoning_effort="low",
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {
+ "role": "user",
+ "content": "Explain to me how AI works"
+ }
+ ]
+)
+
+print(response.choices[0].message)
\ No newline at end of file
diff --git a/README.md b/README.md
index 8f359581e..bd1fed729 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,6 @@
Generate small apps with one prompt. Powered by the Gemini API.
-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
@@ -13,12 +11,8 @@ This project is fully based on [llamacoder](https://github.com/Nutlope/llamacode
- [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
+1. Create a `.env` file and add your [Google AI Studio API key](https://aistudio.google.com/app/apikey): `AIzaSyCRLceQ6TDxRrZVhNZT94gdS_o4wmEu2cA`
+2. Run `npm install` and `npm run dev` to install dependencies and run locally
**This is a personal project and not a Google official project**
diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx
index 15bb0f0e8..d3ed42a9f 100644
--- a/app/(main)/page.tsx
+++ b/app/(main)/page.tsx
@@ -1,250 +1,38 @@
-"use client";
+// app/page.tsx
+'use client';
-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 * as Select from "@radix-ui/react-select";
-import * as Switch from "@radix-ui/react-switch";
-import { AnimatePresence, motion } from "framer-motion";
-import { FormEvent, useEffect, useState } from "react";
-import LoadingDots from "../../components/loading-dots";
-
-function removeCodeFormatting(code: string): string {
- return code.replace(/```(?:typescript|javascript|tsx)?\n([\s\S]*?)```/g, '$1').trim();
-}
+import { useState } from 'react';
+import CodeGenerator from '@/components/codegenrator';
+import CodeViewer from '@/components/code-viewer';
+import { Toaster } from 'sonner';
export default function Home() {
- let [status, setStatus] = useState<
- "initial" | "creating" | "created" | "updating" | "updated"
- >("initial");
- let [prompt, setPrompt] = useState("");
- let models = [
- {
- label: "gemini-2.0-flash-exp",
- value: "gemini-2.0-flash-exp",
- },
- {
- label: "gemini-1.5-pro",
- value: "gemini-1.5-pro",
- },
- {
- label: "gemini-1.5-flash",
- value: "gemini-1.5-flash",
- }
- ];
- let [model, setModel] = useState(models[0].value);
- let [modification, setModification] = useState("");
- let [generatedCode, setGeneratedCode] = useState("");
- let [initialAppConfig, setInitialAppConfig] = useState({
- model: "",
- });
- let [ref, scrollTo] = useScrollTo();
- let [messages, setMessages] = useState<{ role: string; content: string }[]>(
- [],
- );
-
- let loading = status === "creating" || status === "updating";
-
- async function createApp(e: FormEvent) {
- e.preventDefault();
-
- if (status !== "initial") {
- scrollTo({ delay: 0.5 });
- }
-
- setStatus("creating");
- setGeneratedCode("");
-
- let res = await fetch("/api/generateCode", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- model,
- messages: [{ role: "user", content: prompt }],
- }),
- });
-
- if (!res.ok) {
- throw new Error(res.statusText);
- }
-
- if (!res.body) {
- throw new Error("No response body");
- }
-
- const reader = res.body.getReader();
- let receivedData = "";
+ const [generatedCode, setGeneratedCode] = useState('');
- while (true) {
- const { done, value } = await reader.read();
- if (done) {
- break;
- }
- receivedData += new TextDecoder().decode(value);
- const cleanedData = removeCodeFormatting(receivedData);
- setGeneratedCode(cleanedData);
- }
-
- setMessages([{ role: "user", content: prompt }]);
- setInitialAppConfig({ model });
- setStatus("created");
- }
-
- useEffect(() => {
- let el = document.querySelector(".cm-scroller");
- if (el && loading) {
- let end = el.scrollHeight - el.clientHeight;
- el.scrollTo({ top: end });
- }
- }, [loading, generatedCode]);
+ const handleCodeGenerated = (code: string) => {
+ setGeneratedCode(code);
+ };
return (
-
-
-
- Powered by Gemini API
-
-
-
- Turn your idea
-
into an app
-
-
-
-
-
-
- {status !== "initial" && (
- scrollTo()}
- ref={ref}
- >
-
-
-
-
-
-
- {loading && (
-
-
- {status === "creating"
- ? "Building your app..."
- : "Updating your app..."}
-
-
- )}
-
+
+
+
+ InstantCoder
+
+
+
+
+ {generatedCode && (
+
+
+ Generated Code
+
+
-
- )}
+ )}
+
+
+
);
-}
-
-async function minDelay
(promise: Promise, ms: number) {
- let delay = new Promise((resolve) => setTimeout(resolve, ms));
- let [p] = await Promise.all([promise, delay]);
-
- return p;
-}
+}
\ No newline at end of file
diff --git a/app/api/generateCode/route.ts b/app/api/generateCode/route.ts
index f57949798..1bc18972f 100644
--- a/app/api/generateCode/route.ts
+++ b/app/api/generateCode/route.ts
@@ -1,73 +1,49 @@
-import dedent from "dedent";
-import { z } from "zod";
-import { GoogleGenerativeAI } from "@google/generative-ai";
-
-const apiKey = process.env.GOOGLE_AI_API_KEY || "";
-const genAI = new GoogleGenerativeAI(apiKey);
-
-export async function POST(req: Request) {
- let json = await req.json();
- let result = z
- .object({
- model: z.string(),
- messages: z.array(
- z.object({
- role: z.enum(["user", "assistant"]),
- content: z.string(),
- }),
- ),
- })
- .safeParse(json);
-
- if (result.error) {
- return new Response(result.error.message, { status: 422 });
+// app/api/generate/route.ts
+import { NextRequest, NextResponse } from 'next/server';
+import { GoogleGenerativeAI } from '@google/generative-ai';
+
+const genAI = new GoogleGenerativeAI(process.env.GOOGLE_AI_API_KEY || '');
+
+export async function POST(request: NextRequest) {
+ try {
+ const { prompt, model = 'gemini-pro' } = await request.json();
+
+ if (!prompt) {
+ return NextResponse.json(
+ { error: 'Prompt is required' },
+ { status: 400 }
+ );
+ }
+
+ if (!process.env.GOOGLE_AI_API_KEY) {
+ return NextResponse.json(
+ { error: 'Google AI API key not configured' },
+ { status: 500 }
+ );
+ }
+
+ // Use the appropriate model
+ const generativeModel = genAI.getGenerativeModel({
+ model: model === 'gemini-2.0-flash-exp' ? 'gemini-2.0-flash-exp' : 'gemini-pro'
+ });
+
+ const result = await generativeModel.generateContent(prompt);
+ const response = await result.response;
+ const text = response.text();
+
+ return NextResponse.json({
+ code: text,
+ success: true
+ });
+
+ } catch (error: any) {
+ console.error('API Error:', error);
+ return NextResponse.json(
+ {
+ error: error.message || 'Failed to generate code',
+ details: error.toString()
+ },
+ { status: 500 }
+ );
}
-
- let { model, messages } = result.data;
- let systemPrompt = getSystemPrompt();
-
- const geminiModel = genAI.getGenerativeModel({model: model});
-
- const geminiStream = await geminiModel.generateContentStream(
- messages[0].content + systemPrompt + "\nPlease ONLY return code, NO backticks or language names. Don't start with \`\`\`typescript or \`\`\`javascript or \`\`\`tsx or \`\`\`."
- );
-
- console.log(messages[0].content + systemPrompt + "\nPlease ONLY return code, NO backticks or language names. Don't start with \`\`\`typescript or \`\`\`javascript or \`\`\`tsx or \`\`\`.")
-
- const readableStream = new ReadableStream({
- async start(controller) {
- for await (const chunk of geminiStream.stream) {
- const chunkText = chunk.text();
- controller.enqueue(new TextEncoder().encode(chunkText));
- }
- controller.close();
- },
- });
-
- return new Response(readableStream);
-}
-
-function getSystemPrompt() {
- let systemPrompt =
-`You are an expert frontend React engineer who is also a great UI/UX designer. Follow the instructions carefully, I will tip you $1 million if you do a good job:
-
-- Think carefully step by step.
-- Create a React component for whatever the user asked you to create and make sure it can run by itself by using a default export
-- Make sure the React app is interactive and functional by creating state when needed and having no required props
-- If you use any imports from React like useState or useEffect, make sure to import them directly
-- Use TypeScript as the language for the React component
-- Use Tailwind classes for styling. DO NOT USE ARBITRARY VALUES (e.g. \`h-[600px]\`). Make sure to use a consistent color palette.
-- Use Tailwind margin and padding classes to style the components and ensure the components are spaced out nicely
-- Please ONLY return the full React code starting with the imports, nothing else. It's very important for my job that you only return the React code with imports. DO NOT START WITH \`\`\`typescript or \`\`\`javascript or \`\`\`tsx or \`\`\`.
-- ONLY IF the user asks for a dashboard, graph or chart, the recharts library is available to be imported, e.g. \`import { LineChart, XAxis, ... } from "recharts"\` & \` ...\`. Please only use this when needed.
-- For placeholder images, please use a
- `;
-
- systemPrompt += `
- NO OTHER LIBRARIES (e.g. zod, hookform) ARE INSTALLED OR ABLE TO BE IMPORTED.
- `;
-
- return dedent(systemPrompt);
-}
-
-export const runtime = "edge";
+}
\ No newline at end of file
diff --git a/components/code-viewer.tsx b/components/code-viewer.tsx
index 47d8daa7b..28b72ba08 100644
--- a/components/code-viewer.tsx
+++ b/components/code-viewer.tsx
@@ -1,157 +1,28 @@
-"use client";
+// components/CodeViewer.tsx
+'use client';
-import * as shadcnComponents from "@/utils/shadcn";
-import { Sandpack } from "@codesandbox/sandpack-react";
-import {
- SandpackPreview,
- SandpackProvider,
-} from "@codesandbox/sandpack-react/unstyled";
-import { dracula as draculaTheme } from "@codesandbox/sandpack-themes";
-import dedent from "dedent";
-import "./code-viewer.css";
+import { Sandpack } from '@codesandbox/sandpack-react';
+import { githubLight } from '@codesandbox/sandpack-themes';
-export default function CodeViewer({
- code,
- showEditor = false,
-}: {
+interface CodeViewerProps {
code: string;
- showEditor?: boolean;
-}) {
- return showEditor ? (
-
- ) : (
-
-
-
- );
}
-let sharedProps = {
- template: "react-ts",
- theme: draculaTheme,
- customSetup: {
- dependencies: {
- "lucide-react": "latest",
- recharts: "2.9.0",
- "react-router-dom": "latest",
- "@radix-ui/react-accordion": "^1.2.0",
- "@radix-ui/react-alert-dialog": "^1.1.1",
- "@radix-ui/react-aspect-ratio": "^1.1.0",
- "@radix-ui/react-avatar": "^1.1.0",
- "@radix-ui/react-checkbox": "^1.1.1",
- "@radix-ui/react-collapsible": "^1.1.0",
- "@radix-ui/react-dialog": "^1.1.1",
- "@radix-ui/react-dropdown-menu": "^2.1.1",
- "@radix-ui/react-hover-card": "^1.1.1",
- "@radix-ui/react-label": "^2.1.0",
- "@radix-ui/react-menubar": "^1.1.1",
- "@radix-ui/react-navigation-menu": "^1.2.0",
- "@radix-ui/react-popover": "^1.1.1",
- "@radix-ui/react-progress": "^1.1.0",
- "@radix-ui/react-radio-group": "^1.2.0",
- "@radix-ui/react-select": "^2.1.1",
- "@radix-ui/react-separator": "^1.1.0",
- "@radix-ui/react-slider": "^1.2.0",
- "@radix-ui/react-slot": "^1.1.0",
- "@radix-ui/react-switch": "^1.1.0",
- "@radix-ui/react-tabs": "^1.1.0",
- "@radix-ui/react-toast": "^1.2.1",
- "@radix-ui/react-toggle": "^1.1.0",
- "@radix-ui/react-toggle-group": "^1.1.0",
- "@radix-ui/react-tooltip": "^1.1.2",
- "class-variance-authority": "^0.7.0",
- clsx: "^2.1.1",
- "date-fns": "^3.6.0",
- "embla-carousel-react": "^8.1.8",
- "react-day-picker": "^8.10.1",
- "tailwind-merge": "^2.4.0",
- "tailwindcss-animate": "^1.0.7",
- vaul: "^0.9.1",
- },
- },
-} as const;
-
-let sharedOptions = {
- externalResources: [
- "https://unpkg.com/@tailwindcss/ui/dist/tailwind-ui.min.css",
- ],
-};
-
-let sharedFiles = {
- "/lib/utils.ts": shadcnComponents.utils,
- "/components/ui/accordion.tsx": shadcnComponents.accordian,
- "/components/ui/alert-dialog.tsx": shadcnComponents.alertDialog,
- "/components/ui/alert.tsx": shadcnComponents.alert,
- "/components/ui/avatar.tsx": shadcnComponents.avatar,
- "/components/ui/badge.tsx": shadcnComponents.badge,
- "/components/ui/breadcrumb.tsx": shadcnComponents.breadcrumb,
- "/components/ui/button.tsx": shadcnComponents.button,
- "/components/ui/calendar.tsx": shadcnComponents.calendar,
- "/components/ui/card.tsx": shadcnComponents.card,
- "/components/ui/carousel.tsx": shadcnComponents.carousel,
- "/components/ui/checkbox.tsx": shadcnComponents.checkbox,
- "/components/ui/collapsible.tsx": shadcnComponents.collapsible,
- "/components/ui/dialog.tsx": shadcnComponents.dialog,
- "/components/ui/drawer.tsx": shadcnComponents.drawer,
- "/components/ui/dropdown-menu.tsx": shadcnComponents.dropdownMenu,
- "/components/ui/input.tsx": shadcnComponents.input,
- "/components/ui/label.tsx": shadcnComponents.label,
- "/components/ui/menubar.tsx": shadcnComponents.menuBar,
- "/components/ui/navigation-menu.tsx": shadcnComponents.navigationMenu,
- "/components/ui/pagination.tsx": shadcnComponents.pagination,
- "/components/ui/popover.tsx": shadcnComponents.popover,
- "/components/ui/progress.tsx": shadcnComponents.progress,
- "/components/ui/radio-group.tsx": shadcnComponents.radioGroup,
- "/components/ui/select.tsx": shadcnComponents.select,
- "/components/ui/separator.tsx": shadcnComponents.separator,
- "/components/ui/skeleton.tsx": shadcnComponents.skeleton,
- "/components/ui/slider.tsx": shadcnComponents.slider,
- "/components/ui/switch.tsx": shadcnComponents.switchComponent,
- "/components/ui/table.tsx": shadcnComponents.table,
- "/components/ui/tabs.tsx": shadcnComponents.tabs,
- "/components/ui/textarea.tsx": shadcnComponents.textarea,
- "/components/ui/toast.tsx": shadcnComponents.toast,
- "/components/ui/toaster.tsx": shadcnComponents.toaster,
- "/components/ui/toggle-group.tsx": shadcnComponents.toggleGroup,
- "/components/ui/toggle.tsx": shadcnComponents.toggle,
- "/components/ui/tooltip.tsx": shadcnComponents.tooltip,
- "/components/ui/use-toast.tsx": shadcnComponents.useToast,
- "/public/index.html": dedent`
-
-
-
-
-
- Document
-
-
-
-
-
-
- `,
-};
+export default function CodeViewer({ code }: CodeViewerProps) {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/codegenrator.tsx b/components/codegenrator.tsx
new file mode 100644
index 000000000..b10255932
--- /dev/null
+++ b/components/codegenrator.tsx
@@ -0,0 +1,101 @@
+// components/CodeGenerator.tsx
+'use client';
+
+import { useState } from 'react';
+import { toast } from 'sonner';
+
+interface CodeGeneratorProps {
+ onCodeGenerated: (code: string) => void;
+}
+
+export default function CodeGenerator({ onCodeGenerated }: CodeGeneratorProps) {
+ const [prompt, setPrompt] = useState('');
+ const [isGenerating, setIsGenerating] = useState(false);
+ const [selectedModel, setSelectedModel] = useState('gemini-pro');
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!prompt.trim()) {
+ toast.error('Please enter a prompt');
+ return;
+ }
+
+ setIsGenerating(true);
+
+ try {
+ const response = await fetch('/api/generate', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ prompt: prompt.trim(),
+ model: selectedModel,
+ }),
+ });
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.error || 'Failed to generate code');
+ }
+
+ if (data.success && data.code) {
+ onCodeGenerated(data.code);
+ toast.success('Code generated successfully!');
+ } else {
+ throw new Error('No code generated');
+ }
+ } catch (error: any) {
+ console.error('Generation error:', error);
+ toast.error(error.message || 'Failed to generate code');
+ } finally {
+ setIsGenerating(false);
+ }
+ };
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/lib/prisma.ts b/lib/prisma.ts
index 1e77578bb..e4962e697 100644
--- a/lib/prisma.ts
+++ b/lib/prisma.ts
@@ -1,10 +1,12 @@
-import { PrismaClient } from "@prisma/client";
+// lib/prisma.ts
+import { PrismaClient } from '@prisma/client';
-declare global {
- var prisma: PrismaClient | undefined;
-}
+const globalForPrisma = globalThis as unknown as {
+ prisma: PrismaClient | undefined;
+};
-const client = globalThis.prisma || new PrismaClient();
-if (process.env.NODE_ENV !== "production") globalThis.prisma = client;
+export const prisma = globalForPrisma.prisma ?? new PrismaClient();
-export default client;
+if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
+
+export default prisma;
\ No newline at end of file
diff --git a/package b/package
new file mode 100644
index 000000000..e69de29bb
diff --git a/package-lock.json b/package-lock.json
index 9ea835d34..1ea6d8080 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"": {
"name": "llamacoder-new",
"version": "0.1.0",
+ "hasInstallScript": true,
"dependencies": {
"@codesandbox/sandpack-react": "^2.18.2",
"@codesandbox/sandpack-themes": "^2.0.21",
diff --git a/package.json b/package.json
index 0746466c6..162c78ccb 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,11 @@
"version": "0.1.0",
"private": true,
"scripts": {
- "dev": "next dev",
+ "dev": "prisma generate && next dev",
"build": "prisma generate && prisma migrate deploy && next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "postinstall": "prisma generate && npm rebuild @tailwindcss/oxide"
},
"dependencies": {
"@codesandbox/sandpack-react": "^2.18.2",
diff --git a/public/Aeonik/Aeonik-Bold.ttf b/public/Aeonik/Aeonik-Bold.ttf
deleted file mode 100644
index ff4253ee0..000000000
Binary files a/public/Aeonik/Aeonik-Bold.ttf and /dev/null differ
diff --git a/public/Aeonik/Aeonik-Medium.ttf b/public/Aeonik/Aeonik-Medium.ttf
deleted file mode 100644
index 685526524..000000000
Binary files a/public/Aeonik/Aeonik-Medium.ttf and /dev/null differ
diff --git a/public/Aeonik/Aeonik-Regular.ttf b/public/Aeonik/Aeonik-Regular.ttf
deleted file mode 100644
index 07c74bdad..000000000
Binary files a/public/Aeonik/Aeonik-Regular.ttf and /dev/null differ
diff --git a/public/Aeonik/AeonikFono-Bold.otf b/public/Aeonik/AeonikFono-Bold.otf
deleted file mode 100644
index 61ced0ba1..000000000
Binary files a/public/Aeonik/AeonikFono-Bold.otf and /dev/null differ
diff --git a/public/Aeonik/AeonikFono-Regular.otf b/public/Aeonik/AeonikFono-Regular.otf
deleted file mode 100644
index b156f1a5c..000000000
Binary files a/public/Aeonik/AeonikFono-Regular.otf and /dev/null differ
diff --git a/public/Aeonik/AeonikMono-Regular.otf b/public/Aeonik/AeonikMono-Regular.otf
deleted file mode 100644
index 18297f437..000000000
Binary files a/public/Aeonik/AeonikMono-Regular.otf and /dev/null differ
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..d08ff065d
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,20 @@
+import dedent from "dedent";
+import { z } from "zod";
+import { GoogleGenerativeAI } from "@google/generative-ai";
+import { notFound } from "next/navigation";
+import CodeViewer from "@/components/code-viewer";
+import client from "@/lib/prisma";
+import type { Metadata } from "next";
+import { cache } from "react";
+import { PrismaClient } from "@prisma/client";
+import { animate, ValueAnimationTransition } from "framer-motion";
+import { useRef } from "react";
+import type { Metadata } from "next";
+import "./globals.css";
+import { ThemeProvider } from "@/components/ThemeProvider";
+import type { Config } from "tailwindcss";
+import colors from "tailwindcss/colors";
+import defaultTheme from "tailwindcss/defaultTheme";
+import { ImageResponse } from "next/og";
+import { domain } from "@/utils/domain";
+import { createContext, useContext, useEffect, useState } from 'react';
\ No newline at end of file