kalhdrawi commited on
Commit
c89a5a0
·
1 Parent(s): 72eb26e

Reupload OmniDev clean version

Browse files
app/api/augment/route.ts CHANGED
@@ -254,7 +254,7 @@ export async function POST(req: NextRequest) {
254
  function generateFallbackFiles(framework: string, language: string, title: string): FileUpdate[] {
255
  const isTs = language.toLowerCase().includes('ts');
256
  if (framework?.startsWith('express')) {
257
- const server = `import express from 'express';\nimport cors from 'cors';\nconst app = express();\napp.use(cors());\napp.get('/', (_, res) => res.send('OK'));\napp.get('/health', (_, res) => res.json({ ok: true }));\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => console.log('Server listening on', PORT));\n`;
258
  const indexHtml = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<title>${title}</title>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n</head>\n<body class=\"bg-neutral-950 text-white\">\n<div id=\"root\"></div>\n<script type=\"module\" src=\"/src/main.${isTs ? 'tsx' : 'jsx'}\"></script>\n</body>\n</html>`;
259
  const main = `import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport App from './App';\ncreateRoot(document.getElementById('root')).render(<App />);\n`;
260
  const app = `export default function App(){\n return (<main className=\"min-h-screen grid place-content-center\">\n <h1 className=\"text-3xl font-bold\">${title}</h1>\n <p className=\"text-neutral-400\">Frontend + Backend scaffolded by OmniDev</p>\n </main>);\n}\n`;
 
254
  function generateFallbackFiles(framework: string, language: string, title: string): FileUpdate[] {
255
  const isTs = language.toLowerCase().includes('ts');
256
  if (framework?.startsWith('express')) {
257
+ const server = `import express from 'express';\nimport cors from 'cors';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst app = express();\napp.use(cors());\nconst distPath = path.join(__dirname, '..', 'frontend', 'dist');\napp.use(express.static(distPath));\napp.get('/health', (_, res) => res.json({ ok: true }));\napp.get('*', (req, res) => { try { res.sendFile(path.join(distPath, 'index.html')); } catch { res.send('OK'); } });\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => console.log('Server listening on', PORT));\n`;
258
  const indexHtml = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<title>${title}</title>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n</head>\n<body class=\"bg-neutral-950 text-white\">\n<div id=\"root\"></div>\n<script type=\"module\" src=\"/src/main.${isTs ? 'tsx' : 'jsx'}\"></script>\n</body>\n</html>`;
259
  const main = `import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport App from './App';\ncreateRoot(document.getElementById('root')).render(<App />);\n`;
260
  const app = `export default function App(){\n return (<main className=\"min-h-screen grid place-content-center\">\n <h1 className=\"text-3xl font-bold\">${title}</h1>\n <p className=\"text-neutral-400\">Frontend + Backend scaffolded by OmniDev</p>\n </main>);\n}\n`;
app/api/me/projects/[namespace]/[repoId]/file/route.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { downloadFile, spaceInfo } from "@huggingface/hub";
3
+ import { isAuthenticated } from "@/lib/auth";
4
+
5
+ export async function GET(
6
+ req: NextRequest,
7
+ { params }: { params: Promise<{ namespace: string; repoId: string }> }
8
+ ) {
9
+ const user = await isAuthenticated();
10
+ if (!user || user instanceof NextResponse) {
11
+ return NextResponse.json({ ok: false, error: "Unauthorized" }, { status: 401 });
12
+ }
13
+ const { namespace, repoId } = await params;
14
+ const path = req.nextUrl.searchParams.get('path');
15
+ if (!path) return NextResponse.json({ ok: false, error: 'Missing path' }, { status: 400 });
16
+
17
+ try {
18
+ const space = await spaceInfo({ name: `${namespace}/${repoId}`, accessToken: user.token as string });
19
+ if (!space) return NextResponse.json({ ok: false, error: "Space not found" }, { status: 404 });
20
+
21
+ const blob = await downloadFile({ repo: { type: 'space', name: `${namespace}/${repoId}` }, accessToken: user.token as string, path, raw: true });
22
+ if (!blob) return NextResponse.json({ ok: false, error: 'File not found' }, { status: 404 });
23
+ const text = await blob.text();
24
+ return NextResponse.json({ ok: true, path, content: text });
25
+ } catch (e: any) {
26
+ return NextResponse.json({ ok: false, error: e?.message || 'Failed to fetch file' }, { status: 500 });
27
+ }
28
+ }
29
+
app/api/me/projects/[namespace]/[repoId]/files/route.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { listFiles, spaceInfo } from "@huggingface/hub";
3
+ import { isAuthenticated } from "@/lib/auth";
4
+
5
+ export async function GET(
6
+ _req: NextRequest,
7
+ { params }: { params: Promise<{ namespace: string; repoId: string }> }
8
+ ) {
9
+ const user = await isAuthenticated();
10
+ if (!user || user instanceof NextResponse) {
11
+ return NextResponse.json({ ok: false, error: "Unauthorized" }, { status: 401 });
12
+ }
13
+
14
+ const { namespace, repoId } = await params;
15
+ try {
16
+ const space = await spaceInfo({ name: `${namespace}/${repoId}`, accessToken: user.token as string });
17
+ if (!space) return NextResponse.json({ ok: false, error: "Space not found" }, { status: 404 });
18
+
19
+ const paths: { path: string; type: string }[] = [];
20
+ for await (const entry of listFiles({ repo: { type: 'space', name: `${namespace}/${repoId}` }, accessToken: user.token as string })) {
21
+ if (entry.path.startsWith(".git")) continue;
22
+ paths.push({ path: entry.path, type: entry.type });
23
+ }
24
+ return NextResponse.json({ ok: true, paths });
25
+ } catch (e: any) {
26
+ return NextResponse.json({ ok: false, error: e?.message || 'Failed to list files' }, { status: 500 });
27
+ }
28
+ }
29
+
app/api/me/projects/route.ts CHANGED
@@ -41,7 +41,7 @@ title: ${title}
41
  colorFrom: ${colorFrom}
42
  colorTo: ${colorTo}
43
  emoji: 🐳
44
- sdk: static
45
  pinned: false
46
  tags:
47
  - deepsite-v3
@@ -119,3 +119,4 @@ This project was created with [DeepSite](https://deepsite.hf.co).
119
  );
120
  }
121
  }
 
 
41
  colorFrom: ${colorFrom}
42
  colorTo: ${colorTo}
43
  emoji: 🐳
44
+ sdk: docker
45
  pinned: false
46
  tags:
47
  - deepsite-v3
 
119
  );
120
  }
121
  }
122
+
components/editor/files-explorer/index.tsx ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Button } from "@/components/ui/button";
4
+ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
5
+ import Editor from "@monaco-editor/react";
6
+ import { useEditor } from "@/hooks/useEditor";
7
+
8
+ const TEXT_EXT = ["js","ts","tsx","jsx","json","md","css","html","txt","mjs","cjs","tsconfig","eslintrc","prettierrc"];
9
+ function isTextFile(path: string) {
10
+ const ext = (path.split('.').pop() || '').toLowerCase();
11
+ return TEXT_EXT.includes(ext) || path.endsWith('.d.ts');
12
+ }
13
+
14
+ export function FilesExplorerButton() {
15
+ const [open, setOpen] = useState(false);
16
+ return (
17
+ <>
18
+ <Button size="xs" variant="outline" className="!rounded-md" onClick={() => setOpen(true)}>Files</Button>
19
+ {open && <FilesExplorer open={open} onClose={() => setOpen(false)} />}
20
+ </>
21
+ );
22
+ }
23
+
24
+ function FilesExplorer({ open, onClose }: { open: boolean; onClose: () => void }) {
25
+ const { project } = useEditor();
26
+ const space = project?.space_id || '';
27
+ const [namespace, repoId] = space.split('/');
28
+ const [paths, setPaths] = useState<{ path: string; type: string }[]>([]);
29
+ const [selected, setSelected] = useState<string | null>(null);
30
+ const [content, setContent] = useState<string>('');
31
+ const [loading, setLoading] = useState(false);
32
+ const [saving, setSaving] = useState(false);
33
+
34
+ const canSave = useMemo(() => !!selected && isTextFile(selected!), [selected]);
35
+
36
+ useEffect(() => {
37
+ (async () => {
38
+ if (!namespace || !repoId) return;
39
+ const res = await fetch(`/api/me/projects/${namespace}/${repoId}/files`);
40
+ const data = await res.json();
41
+ if (data?.ok) setPaths(data.paths || []);
42
+ })();
43
+ }, [namespace, repoId]);
44
+
45
+ const openFile = async (p: string) => {
46
+ setSelected(p);
47
+ setContent('');
48
+ setLoading(true);
49
+ try {
50
+ if (isTextFile(p)) {
51
+ const res = await fetch(`/api/me/projects/${namespace}/${repoId}/file?path=${encodeURIComponent(p)}`);
52
+ const data = await res.json();
53
+ if (data?.ok) setContent(data.content || '');
54
+ }
55
+ } finally {
56
+ setLoading(false);
57
+ }
58
+ };
59
+
60
+ const saveFile = async () => {
61
+ if (!selected) return;
62
+ setSaving(true);
63
+ try {
64
+ const res = await fetch(`/api/me/projects/${namespace}/${repoId}/apply`, {
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
+ body: JSON.stringify({ files: [{ path: selected, action: 'update', content }], commitTitle: `Edit ${selected}` })
68
+ });
69
+ await res.json();
70
+ } finally {
71
+ setSaving(false);
72
+ }
73
+ };
74
+
75
+ return (
76
+ <Dialog open={open} onOpenChange={() => onClose()}>
77
+ <DialogContent className="sm:max-w-5xl !bg-neutral-900 !border-neutral-800">
78
+ <DialogHeader>
79
+ <DialogTitle className="text-neutral-100">Project Files</DialogTitle>
80
+ </DialogHeader>
81
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-3 min-h-[420px]">
82
+ <div className="border border-neutral-800 rounded-lg p-2 overflow-y-auto">
83
+ <ul className="text-sm text-neutral-300">
84
+ {paths.map(p => (
85
+ <li key={p.path} className="py-1 px-2 rounded hover:bg-neutral-800 cursor-pointer" onClick={() => openFile(p.path)}>
86
+ {p.path}
87
+ </li>
88
+ ))}
89
+ </ul>
90
+ </div>
91
+ <div className="md:col-span-2 border border-neutral-800 rounded-lg overflow-hidden relative">
92
+ {!selected && <div className="p-4 text-neutral-400 text-sm">Select a file to preview/edit</div>}
93
+ {selected && isTextFile(selected) && (
94
+ <Editor
95
+ defaultLanguage="plaintext"
96
+ theme="vs-dark"
97
+ value={content}
98
+ loading={<div className="p-4 text-neutral-400 text-sm">{loading ? 'Loading…' : ''}</div>}
99
+ onChange={(v) => setContent(v || '')}
100
+ options={{ minimap: { enabled: false }, wordWrap: 'on' }}
101
+ />
102
+ )}
103
+ {selected && !isTextFile(selected) && (
104
+ <div className="p-4 text-neutral-400 text-sm">Binary/non-text file. Download/edit locally if needed.</div>
105
+ )}
106
+ </div>
107
+ </div>
108
+ <DialogFooter>
109
+ <Button variant="bordered" size="sm" onClick={() => onClose()}>Close</Button>
110
+ <Button size="sm" onClick={saveFile} disabled={!canSave || saving}>{saving ? 'Saving…' : 'Save File'}</Button>
111
+ </DialogFooter>
112
+ </DialogContent>
113
+ </Dialog>
114
+ );
115
+ }
116
+
components/editor/index.tsx CHANGED
@@ -18,6 +18,7 @@ import { Preview } from "./preview";
18
  import { SaveChangesPopup } from "./save-changes-popup";
19
  import Loading from "../loading";
20
  import { Page } from "@/types";
 
21
 
22
  export const AppEditor = ({
23
  namespace,
@@ -78,6 +79,9 @@ export const AppEditor = ({
78
  )}
79
  >
80
  <ListPages />
 
 
 
81
  <CopyIcon
82
  className="size-4 absolute top-14 right-5 text-neutral-500 hover:text-neutral-300 z-2 cursor-pointer"
83
  onClick={() => {
 
18
  import { SaveChangesPopup } from "./save-changes-popup";
19
  import Loading from "../loading";
20
  import { Page } from "@/types";
21
+ import { FilesExplorerButton } from "@/components/editor/files-explorer";
22
 
23
  export const AppEditor = ({
24
  namespace,
 
79
  )}
80
  >
81
  <ListPages />
82
+ <div className="absolute top-12 left-3 z-10">
83
+ <FilesExplorerButton />
84
+ </div>
85
  <CopyIcon
86
  className="size-4 absolute top-14 right-5 text-neutral-500 hover:text-neutral-300 z-2 cursor-pointer"
87
  onClick={() => {