Spaces:
Running
Running
Reupload OmniDev clean version
Browse files- app/api/me/projects/route.ts +16 -7
- app/new/page.tsx +2 -2
- components/new/scaffold.tsx +98 -0
app/api/me/projects/route.ts
CHANGED
|
@@ -13,7 +13,7 @@ export async function POST(
|
|
| 13 |
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
|
| 14 |
}
|
| 15 |
|
| 16 |
-
const { title: titleFromRequest, pages, prompt } = await req.json();
|
| 17 |
|
| 18 |
const title = titleFromRequest ?? "DeepSite Project";
|
| 19 |
|
|
@@ -49,17 +49,26 @@ This project was created with [DeepSite](https://deepsite.hf.co).
|
|
| 49 |
const files: File[] = [];
|
| 50 |
const readmeFile = new File([README], "README.md", { type: "text/markdown" });
|
| 51 |
files.push(readmeFile);
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
try {
|
| 58 |
const { repoUrl} = await createRepo({
|
| 59 |
repo,
|
| 60 |
accessToken: user.token as string,
|
| 61 |
});
|
| 62 |
-
const commitTitle = !prompt || prompt.trim() === "" ? "Redesign my website" : prompt;
|
| 63 |
await uploadFiles({
|
| 64 |
repo,
|
| 65 |
files,
|
|
@@ -104,4 +113,4 @@ This project was created with [DeepSite](https://deepsite.hf.co).
|
|
| 104 |
{ status: 500 }
|
| 105 |
);
|
| 106 |
}
|
| 107 |
-
}
|
|
|
|
| 13 |
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
|
| 14 |
}
|
| 15 |
|
| 16 |
+
const { title: titleFromRequest, pages, prompt, initialFiles } = await req.json();
|
| 17 |
|
| 18 |
const title = titleFromRequest ?? "DeepSite Project";
|
| 19 |
|
|
|
|
| 49 |
const files: File[] = [];
|
| 50 |
const readmeFile = new File([README], "README.md", { type: "text/markdown" });
|
| 51 |
files.push(readmeFile);
|
| 52 |
+
if (Array.isArray(pages)) {
|
| 53 |
+
pages.forEach((page: Page) => {
|
| 54 |
+
const file = new File([page.html], page.path, { type: "text/html" });
|
| 55 |
+
files.push(file);
|
| 56 |
+
});
|
| 57 |
+
}
|
| 58 |
+
if (Array.isArray(initialFiles)) {
|
| 59 |
+
initialFiles.forEach((f: { path: string; content: string; type?: string }) => {
|
| 60 |
+
const path = (f.path || '').replace(/^\/+/, '');
|
| 61 |
+
const type = f.type || (path.endsWith('.html') ? 'text/html' : path.endsWith('.json') ? 'application/json' : 'text/plain');
|
| 62 |
+
files.push(new File([f.content || ''], path, { type }));
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
|
| 66 |
try {
|
| 67 |
const { repoUrl} = await createRepo({
|
| 68 |
repo,
|
| 69 |
accessToken: user.token as string,
|
| 70 |
});
|
| 71 |
+
const commitTitle = (!prompt || prompt.trim() === "") ? (Array.isArray(initialFiles) ? "OmniDev: Initialize project" : "Redesign my website") : prompt;
|
| 72 |
await uploadFiles({
|
| 73 |
repo,
|
| 74 |
files,
|
|
|
|
| 113 |
{ status: 500 }
|
| 114 |
);
|
| 115 |
}
|
| 116 |
+
}
|
app/new/page.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
-
import { AppEditor } from "@/components/editor";
|
| 2 |
import { Metadata } from "next";
|
| 3 |
import { generateSEO } from "@/lib/seo";
|
|
|
|
| 4 |
|
| 5 |
export const metadata: Metadata = generateSEO({
|
| 6 |
title: "Create New Project - OmniDev",
|
|
@@ -10,5 +10,5 @@ export const metadata: Metadata = generateSEO({
|
|
| 10 |
});
|
| 11 |
|
| 12 |
export default function NewProjectPage() {
|
| 13 |
-
return <
|
| 14 |
}
|
|
|
|
|
|
|
| 1 |
import { Metadata } from "next";
|
| 2 |
import { generateSEO } from "@/lib/seo";
|
| 3 |
+
import ScaffoldNew from "@/components/new/scaffold";
|
| 4 |
|
| 5 |
export const metadata: Metadata = generateSEO({
|
| 6 |
title: "Create New Project - OmniDev",
|
|
|
|
| 10 |
});
|
| 11 |
|
| 12 |
export default function NewProjectPage() {
|
| 13 |
+
return <ScaffoldNew />;
|
| 14 |
}
|
components/new/scaffold.tsx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
import { useEffect, useState } from "react";
|
| 3 |
+
import { useUser } from "@/hooks/useUser";
|
| 4 |
+
import { useAi } from "@/hooks/useAi";
|
| 5 |
+
import { useRouter } from "next/navigation";
|
| 6 |
+
import { Button } from "@/components/ui/button";
|
| 7 |
+
import { toast } from "sonner";
|
| 8 |
+
|
| 9 |
+
const DEFAULT_INSTRUCTION = `Initialize a complete full-stack web project with the following structure and runnable code:
|
| 10 |
+
|
| 11 |
+
Requirements:
|
| 12 |
+
- Frontend under /frontend using React + Vite (JavaScript), TailwindCSS preconfigured.
|
| 13 |
+
- Backend under /backend using Express (JavaScript, ESM), with basic routes (GET /, GET /health) and CORS enabled.
|
| 14 |
+
- Add a minimal README.md at root.
|
| 15 |
+
- Provide package.json in both /frontend and /backend with scripts to start dev/prod.
|
| 16 |
+
- Provide /frontend/index.html and /frontend/src/main.jsx, /frontend/src/App.jsx.
|
| 17 |
+
- Provide /backend/server.js.
|
| 18 |
+
- Use ports 5173 for frontend and 3000 for backend.
|
| 19 |
+
- Keep everything simple and runnable.
|
| 20 |
+
- Return JSON strictly as file updates (no markdown), paths rooted from repo, e.g. /frontend/..., /backend/...`;
|
| 21 |
+
|
| 22 |
+
export default function ScaffoldNew() {
|
| 23 |
+
const { user, openLoginWindow } = useUser();
|
| 24 |
+
const { model, provider } = useAi();
|
| 25 |
+
const router = useRouter();
|
| 26 |
+
const [loading, setLoading] = useState(false);
|
| 27 |
+
const [error, setError] = useState<string | null>(null);
|
| 28 |
+
const [logs, setLogs] = useState<string[]>([]);
|
| 29 |
+
|
| 30 |
+
async function runScaffold() {
|
| 31 |
+
if (!user) {
|
| 32 |
+
await openLoginWindow();
|
| 33 |
+
return;
|
| 34 |
+
}
|
| 35 |
+
setLoading(true);
|
| 36 |
+
setError(null);
|
| 37 |
+
setLogs(["Starting scaffold via Augment..."]);
|
| 38 |
+
try {
|
| 39 |
+
// 1) Call augment to generate full-stack files
|
| 40 |
+
const aug = await fetch('/api/augment', {
|
| 41 |
+
method: 'POST',
|
| 42 |
+
headers: { 'Content-Type': 'application/json' },
|
| 43 |
+
body: JSON.stringify({
|
| 44 |
+
context: '/ (empty project)',
|
| 45 |
+
instruction: DEFAULT_INSTRUCTION,
|
| 46 |
+
language: 'javascript',
|
| 47 |
+
framework: 'express-react',
|
| 48 |
+
response_type: 'file_updates',
|
| 49 |
+
model,
|
| 50 |
+
provider,
|
| 51 |
+
}),
|
| 52 |
+
}).then(r => r.json());
|
| 53 |
+
if (!aug?.ok || !Array.isArray(aug.files) || aug.files.length === 0) {
|
| 54 |
+
throw new Error(aug?.message || 'Augment did not return files');
|
| 55 |
+
}
|
| 56 |
+
setLogs(prev => [...prev, `Augment produced ${aug.files.length} files`]);
|
| 57 |
+
|
| 58 |
+
// 2) Create space with initial files
|
| 59 |
+
const title = 'OmniDev Full-Stack Project';
|
| 60 |
+
const created = await fetch('/api/me/projects', {
|
| 61 |
+
method: 'POST',
|
| 62 |
+
headers: { 'Content-Type': 'application/json' },
|
| 63 |
+
body: JSON.stringify({ title, initialFiles: aug.files.map((f: any) => ({ path: f.path, content: f.content })) })
|
| 64 |
+
}).then(r => r.json());
|
| 65 |
+
if (!created?.space?.project?.space_id) {
|
| 66 |
+
throw new Error(created?.error || 'Failed to create project');
|
| 67 |
+
}
|
| 68 |
+
setLogs(prev => [...prev, 'Project created, redirecting...']);
|
| 69 |
+
router.push(`/${created.space.project.space_id}`);
|
| 70 |
+
} catch (e: any) {
|
| 71 |
+
setError(e?.message || 'Scaffold failed');
|
| 72 |
+
toast.error(e?.message || 'Scaffold failed');
|
| 73 |
+
} finally {
|
| 74 |
+
setLoading(false);
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
useEffect(() => {
|
| 79 |
+
// Auto-run once when visiting /new
|
| 80 |
+
runScaffold();
|
| 81 |
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
| 82 |
+
}, []);
|
| 83 |
+
|
| 84 |
+
return (
|
| 85 |
+
<section className="max-w-2xl mx-auto p-6 text-neutral-200">
|
| 86 |
+
<h1 className="text-2xl font-semibold mb-2">Initializing Full-Stack Project...</h1>
|
| 87 |
+
<p className="text-sm text-neutral-400 mb-4">OmniDev is generating a complete frontend + backend project. This may take a moment.</p>
|
| 88 |
+
{error && <p className="text-red-400 text-sm mb-2">{error}</p>}
|
| 89 |
+
<ul className="text-sm text-neutral-400 space-y-1">
|
| 90 |
+
{logs.map((l, i) => (<li key={i}>• {l}</li>))}
|
| 91 |
+
</ul>
|
| 92 |
+
<div className="mt-4">
|
| 93 |
+
<Button size="sm" onClick={runScaffold} disabled={loading}>{loading ? 'Working...' : 'Retry'}</Button>
|
| 94 |
+
</div>
|
| 95 |
+
</section>
|
| 96 |
+
);
|
| 97 |
+
}
|
| 98 |
+
|