kalhdrawi commited on
Commit
1e075e6
·
1 Parent(s): 743dad3

Reupload OmniDev clean version

Browse files
app/actions/auth.ts CHANGED
@@ -5,13 +5,12 @@ import { headers } from "next/headers";
5
  export async function getAuth() {
6
  const authList = await headers();
7
  const host = authList.get("host") ?? "localhost:3000";
8
- const url = host.includes("/spaces/enzostvs")
9
- ? "enzostvs-deepsite.hf.space"
10
- : host;
11
- const redirect_uri =
12
- `${host.includes("localhost") ? "http://" : "https://"}` +
13
- url +
14
- "/auth/callback";
15
 
16
  const loginRedirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${redirect_uri}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
17
  return loginRedirectUrl;
 
5
  export async function getAuth() {
6
  const authList = await headers();
7
  const host = authList.get("host") ?? "localhost:3000";
8
+ const baseFromEnv = process.env.PUBLIC_BASE_URL?.trim();
9
+ const isLocal = (baseFromEnv || host).includes("localhost");
10
+ const protocol = isLocal ? "http" : "https";
11
+ const baseUrl = baseFromEnv || `${protocol}://${host}`;
12
+ const redirectPath = process.env.AUTH_REDIRECT_PATH || "/auth/callback";
13
+ const redirect_uri = `${baseUrl}${redirectPath}`;
 
14
 
15
  const loginRedirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${redirect_uri}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
16
  return loginRedirectUrl;
app/api/ask/route.ts CHANGED
@@ -30,6 +30,7 @@ import { COLORS } from "@/lib/utils";
30
  import { templates } from "@/lib/templates";
31
 
32
  const ipAddresses = new Map();
 
33
 
34
  export async function POST(request: NextRequest) {
35
  const authHeaders = await headers();
@@ -74,8 +75,10 @@ export async function POST(request: NextRequest) {
74
  : authHeaders.get("x-forwarded-for");
75
 
76
  if (!token) {
77
- ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
78
- if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
 
 
79
  return NextResponse.json(
80
  {
81
  ok: false,
@@ -124,18 +127,20 @@ export async function POST(request: NextRequest) {
124
  }
125
  const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
126
  const contents = [
127
- { role: "user", parts: [{ text: systemPrompt }] },
128
  { role: "user", parts: [{ text: userPrompt }] },
129
  ];
130
  const estimatedInputTokens = estimateInputTokens(systemPrompt, userPrompt);
131
  const dynamicMaxTokens = calculateMaxTokens(selectedProvider, estimatedInputTokens, true);
132
  const config: any = {
133
- generationConfig: { maxOutputTokens: dynamicMaxTokens },
134
  };
135
  const response = await ai.models.generateContentStream({
136
  model: selectedModel.value,
137
  config,
138
- contents,
 
 
 
139
  });
140
 
141
  // Newer SDKs return an async iterable of chunks with `.text`
@@ -177,7 +182,7 @@ export async function POST(request: NextRequest) {
177
  // Explicitly close the writer after successful completion
178
  await writer.close();
179
  } catch (error: any) {
180
- if (error.message?.includes("exceeded your monthly included credits")) {
181
  await writer.write(
182
  encoder.encode(
183
  JSON.stringify({
@@ -283,8 +288,10 @@ export async function PUT(request: NextRequest) {
283
  : authHeaders.get("x-forwarded-for");
284
 
285
  if (!token) {
286
- ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
287
- if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
 
 
288
  return NextResponse.json(
289
  {
290
  ok: false,
@@ -343,13 +350,19 @@ export async function PUT(request: NextRequest) {
343
  }
344
  const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
345
  const contents = [
346
- { role: "user", parts: [{ text: systemPrompt }] },
347
  { role: "user", parts: [{ text: userContext }] },
348
  { role: "model", parts: [{ text: assistantContext }] },
349
  { role: "user", parts: [{ text: prompt }] },
350
  ];
351
- const config: any = { generationConfig: { maxOutputTokens: dynamicMaxTokens } };
352
- const response = await ai.models.generateContentStream({ model: selectedModel.value, config, contents });
 
 
 
 
 
 
 
353
  const iterable: any = (response as any)?.stream ?? response;
354
  for await (const c of iterable as AsyncIterable<any>) {
355
  const text = (c && (c.text ?? c?.candidates?.[0]?.content?.parts?.[0]?.text)) || "";
@@ -624,7 +637,7 @@ This project was created with [DeepSite](https://deepsite.hf.co).
624
  );
625
  }
626
  } catch (error: any) {
627
- if (error.message?.includes("exceeded your monthly included credits")) {
628
  return NextResponse.json(
629
  {
630
  ok: false,
 
30
  import { templates } from "@/lib/templates";
31
 
32
  const ipAddresses = new Map();
33
+ const IS_UNLIMITED_MODE = (process.env.UNLIMITED_MODE || '').toLowerCase() === 'true';
34
 
35
  export async function POST(request: NextRequest) {
36
  const authHeaders = await headers();
 
75
  : authHeaders.get("x-forwarded-for");
76
 
77
  if (!token) {
78
+ if (!IS_UNLIMITED_MODE) {
79
+ ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
80
+ }
81
+ if (!IS_UNLIMITED_MODE && ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
82
  return NextResponse.json(
83
  {
84
  ok: false,
 
127
  }
128
  const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
129
  const contents = [
 
130
  { role: "user", parts: [{ text: userPrompt }] },
131
  ];
132
  const estimatedInputTokens = estimateInputTokens(systemPrompt, userPrompt);
133
  const dynamicMaxTokens = calculateMaxTokens(selectedProvider, estimatedInputTokens, true);
134
  const config: any = {
135
+ maxOutputTokens: dynamicMaxTokens,
136
  };
137
  const response = await ai.models.generateContentStream({
138
  model: selectedModel.value,
139
  config,
140
+ contents: [
141
+ { role: "user", parts: [{ text: systemPrompt }] },
142
+ ...contents,
143
+ ],
144
  });
145
 
146
  // Newer SDKs return an async iterable of chunks with `.text`
 
182
  // Explicitly close the writer after successful completion
183
  await writer.close();
184
  } catch (error: any) {
185
+ if (!IS_UNLIMITED_MODE && error.message?.includes("exceeded your monthly included credits")) {
186
  await writer.write(
187
  encoder.encode(
188
  JSON.stringify({
 
288
  : authHeaders.get("x-forwarded-for");
289
 
290
  if (!token) {
291
+ if (!IS_UNLIMITED_MODE) {
292
+ ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1);
293
+ }
294
+ if (!IS_UNLIMITED_MODE && ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) {
295
  return NextResponse.json(
296
  {
297
  ok: false,
 
350
  }
351
  const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
352
  const contents = [
 
353
  { role: "user", parts: [{ text: userContext }] },
354
  { role: "model", parts: [{ text: assistantContext }] },
355
  { role: "user", parts: [{ text: prompt }] },
356
  ];
357
+ const config: any = { maxOutputTokens: dynamicMaxTokens };
358
+ const response = await ai.models.generateContentStream({
359
+ model: selectedModel.value,
360
+ config,
361
+ contents: [
362
+ { role: "user", parts: [{ text: systemPrompt }] },
363
+ ...contents,
364
+ ],
365
+ });
366
  const iterable: any = (response as any)?.stream ?? response;
367
  for await (const c of iterable as AsyncIterable<any>) {
368
  const text = (c && (c.text ?? c?.candidates?.[0]?.content?.parts?.[0]?.text)) || "";
 
637
  );
638
  }
639
  } catch (error: any) {
640
+ if (!IS_UNLIMITED_MODE && error.message?.includes("exceeded your monthly included credits")) {
641
  return NextResponse.json(
642
  {
643
  ok: false,
app/api/auth/login-url/route.ts CHANGED
@@ -2,20 +2,12 @@ import { NextRequest, NextResponse } from "next/server";
2
 
3
  export async function GET(req: NextRequest) {
4
  const host = req.headers.get("host") ?? "localhost:3000";
5
-
6
- let url: string;
7
- if (host.includes("localhost")) {
8
- url = host;
9
- } else if (host.includes("hf.space") || host.includes("/spaces/enzostvs")) {
10
- url = "enzostvs-deepsite.hf.space";
11
- } else {
12
- url = "deepsite.hf.co";
13
- }
14
-
15
- const redirect_uri =
16
- `${host.includes("localhost") ? "http://" : "https://"}` +
17
- url +
18
- "/auth/callback";
19
 
20
  const loginRedirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${redirect_uri}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
21
 
 
2
 
3
  export async function GET(req: NextRequest) {
4
  const host = req.headers.get("host") ?? "localhost:3000";
5
+ const baseFromEnv = process.env.PUBLIC_BASE_URL?.trim();
6
+ const isLocal = (baseFromEnv || host).includes("localhost");
7
+ const protocol = isLocal ? "http" : "https";
8
+ const baseUrl = baseFromEnv || `${protocol}://${host}`;
9
+ const redirectPath = process.env.AUTH_REDIRECT_PATH || "/auth/callback";
10
+ const redirect_uri = `${baseUrl}${redirectPath}`;
 
 
 
 
 
 
 
 
11
 
12
  const loginRedirectUrl = `https://huggingface.co/oauth/authorize?client_id=${process.env.OAUTH_CLIENT_ID}&redirect_uri=${redirect_uri}&response_type=code&scope=openid%20profile%20write-repos%20manage-repos%20inference-api&prompt=consent&state=1234567890`;
13
 
app/api/auth/route.ts CHANGED
@@ -21,16 +21,13 @@ export async function POST(req: NextRequest) {
21
  `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}`
22
  ).toString("base64")}`;
23
 
24
- const host =
25
- req.headers.get("host") ?? req.headers.get("origin") ?? "localhost:3000";
26
-
27
- const url = host.includes("/spaces/enzostvs")
28
- ? "enzostvs-deepsite.hf.space"
29
- : host;
30
- const redirect_uri =
31
- `${host.includes("localhost") ? "http://" : "https://"}` +
32
- url +
33
- "/auth/callback";
34
  const request_auth = await fetch("https://huggingface.co/oauth/token", {
35
  method: "POST",
36
  headers: {
 
21
  `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}`
22
  ).toString("base64")}`;
23
 
24
+ const host = req.headers.get("host") ?? req.headers.get("origin") ?? "localhost:3000";
25
+ const baseFromEnv = process.env.PUBLIC_BASE_URL?.trim();
26
+ const isLocal = (baseFromEnv || host).includes("localhost");
27
+ const protocol = isLocal ? "http" : "https";
28
+ const baseUrl = baseFromEnv || `${protocol}://${host}`;
29
+ const redirectPath = process.env.AUTH_REDIRECT_PATH || "/auth/callback";
30
+ const redirect_uri = `${baseUrl}${redirectPath}`;
 
 
 
31
  const request_auth = await fetch("https://huggingface.co/oauth/token", {
32
  method: "POST",
33
  headers: {
app/api/enhance/route.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { GoogleGenAI } from "@google/genai";
3
+ import { InferenceClient } from "@huggingface/inference";
4
+
5
+ export async function POST(req: NextRequest) {
6
+ try {
7
+ const { prompt, model, provider, enhancedSettings } = await req.json();
8
+ if (!prompt || typeof prompt !== 'string') {
9
+ return NextResponse.json({ ok: false, message: 'Missing prompt' }, { status: 400 });
10
+ }
11
+
12
+ const system = `You are a senior product designer and prompt engineer. Rewrite the user's prompt to make it clearer, more actionable and specific for an AI that builds complete web applications (frontend-first with modern, responsive, accessible UI; optionally include backend notes if requested). Always preserve the original intent but add concrete details, constraints, and acceptance criteria. If enhanced settings are provided (colors, theme, etc.), weave them naturally into the prompt. Return ONLY the rewritten prompt, nothing else.`;
13
+
14
+ const userParts: string[] = [
15
+ `Original prompt: ${prompt}`
16
+ ];
17
+ if (enhancedSettings?.isActive) {
18
+ userParts.push(
19
+ `Enhanced settings: primary=${enhancedSettings?.primaryColor || 'auto'}, secondary=${enhancedSettings?.secondaryColor || 'auto'}, theme=${enhancedSettings?.theme || 'auto'}`
20
+ );
21
+ }
22
+
23
+ const selectedProvider = (provider || '').toLowerCase();
24
+ let text = '';
25
+
26
+ if (selectedProvider === 'google' || (model || '').toLowerCase().startsWith('gemini-')) {
27
+ const apiKey = process.env.GEMINI_API_KEY;
28
+ if (!apiKey) return NextResponse.json({ ok: false, message: 'Missing GEMINI_API_KEY' }, { status: 500 });
29
+ const ai = new GoogleGenAI({ apiKey });
30
+ const res = await ai.models.generateContent({
31
+ model: model || 'gemini-2.5-flash',
32
+ contents: [
33
+ { role: 'user', parts: [{ text: system }] },
34
+ { role: 'user', parts: [{ text: userParts.join('\n') }] },
35
+ ],
36
+ config: { maxOutputTokens: 1024 },
37
+ } as any);
38
+ text = (res as any)?.candidates?.[0]?.content?.parts?.[0]?.text || '';
39
+ } else {
40
+ const token = process.env.HF_TOKEN || process.env.DEFAULT_HF_TOKEN;
41
+ const client = new InferenceClient(token);
42
+ const res = await client.chatCompletion({
43
+ model: model,
44
+ provider: provider as any,
45
+ messages: [
46
+ { role: 'system', content: system },
47
+ { role: 'user', content: userParts.join('\n') },
48
+ ],
49
+ });
50
+ text = res.choices?.[0]?.message?.content || '';
51
+ }
52
+
53
+ if (!text.trim()) {
54
+ return NextResponse.json({ ok: false, message: 'No content returned' }, { status: 500 });
55
+ }
56
+ return NextResponse.json({ ok: true, prompt: text });
57
+ } catch (e: any) {
58
+ return NextResponse.json({ ok: false, message: e?.message || 'Internal error' }, { status: 500 });
59
+ }
60
+ }
61
+
app/new/page.tsx CHANGED
@@ -3,9 +3,9 @@ import { Metadata } from "next";
3
  import { generateSEO } from "@/lib/seo";
4
 
5
  export const metadata: Metadata = generateSEO({
6
- title: "Create New Project - DeepSite",
7
  description:
8
- "Start building your next website with AI. Create a new project on DeepSite and experience the power of AI-driven web development.",
9
  path: "/new",
10
  });
11
 
 
3
  import { generateSEO } from "@/lib/seo";
4
 
5
  export const metadata: Metadata = generateSEO({
6
+ title: "Create New Project - OmniDev",
7
  description:
8
+ "Start building your next website with AI. Create a new project on OmniDev and experience the power of AI-driven web development.",
9
  path: "/new",
10
  });
11
 
components/animated-text/index.tsx CHANGED
@@ -100,7 +100,7 @@ export function AnimatedText({ className = "" }: AnimatedTextProps) {
100
 
101
  return (
102
  <p className={`font-mono ${className}`}>
103
- Hey DeepSite,&nbsp;
104
  {displayText.split("").map((char, index) => (
105
  <span
106
  key={`${currentSuggestionIndex}-${index}`}
 
100
 
101
  return (
102
  <p className={`font-mono ${className}`}>
103
+ Hey OmniDev,&nbsp;
104
  {displayText.split("").map((char, index) => (
105
  <span
106
  key={`${currentSuggestionIndex}-${index}`}
components/contexts/pro-context.tsx CHANGED
@@ -16,6 +16,7 @@ const ProContext = createContext<ProContextType | undefined>(undefined);
16
  export function ProProvider({ children }: { children: ReactNode }) {
17
  const [isOpen, setIsOpen] = useState(false);
18
  const { pages } = useEditor();
 
19
 
20
  const openProModal = () => {
21
  setIsOpen(true);
@@ -34,7 +35,9 @@ export function ProProvider({ children }: { children: ReactNode }) {
34
  return (
35
  <ProContext.Provider value={value}>
36
  {children}
37
- <ProModal open={isOpen} onClose={setIsOpen} pages={pages} />
 
 
38
  </ProContext.Provider>
39
  );
40
  }
 
16
  export function ProProvider({ children }: { children: ReactNode }) {
17
  const [isOpen, setIsOpen] = useState(false);
18
  const { pages } = useEditor();
19
+ const isUnlimited = (process.env.UNLIMITED_MODE || '').toLowerCase() === 'true';
20
 
21
  const openProModal = () => {
22
  setIsOpen(true);
 
35
  return (
36
  <ProContext.Provider value={value}>
37
  {children}
38
+ {!isUnlimited && (
39
+ <ProModal open={isOpen} onClose={setIsOpen} pages={pages} />
40
+ )}
41
  </ProContext.Provider>
42
  );
43
  }
components/editor/ask-ai/index.tsx CHANGED
@@ -15,11 +15,12 @@ import { Uploader } from "@/components/editor/ask-ai/uploader";
15
  import { ReImagine } from "@/components/editor/ask-ai/re-imagine";
16
  import { Selector } from "@/components/editor/ask-ai/selector";
17
  import { PromptBuilder } from "@/components/editor/ask-ai/prompt-builder";
 
18
  import { useUser } from "@/hooks/useUser";
19
  import { useLoginModal } from "@/components/contexts/login-context";
20
  import { Settings } from "./settings";
21
  import { useProModal } from "@/components/contexts/pro-context";
22
- import { MAX_FREE_PROJECTS } from "@/lib/utils";
23
  import { PROMPTS_FOR_AI } from "@/lib/prompts";
24
 
25
  export const AskAi = ({
@@ -80,7 +81,7 @@ export const AskAi = ({
80
 
81
  const callAi = async (redesignMarkdown?: string) => {
82
  removePromptStorage();
83
- if (user && !user.isPro && projects.length >= MAX_FREE_PROJECTS)
84
  return openProModal([]);
85
  if (isAiWorking) return;
86
  if (!redesignMarkdown && !prompt.trim()) return;
@@ -170,7 +171,7 @@ export const AskAi = ({
170
  }}
171
  >
172
  <p className="text-sm font-medium text-neutral-300 group-hover:text-neutral-200 transition-colors duration-200">
173
- {isThinking ? "DeepSite is thinking..." : "DeepSite's plan"}
174
  </p>
175
  <ChevronDown
176
  className={classNames(
@@ -224,8 +225,8 @@ export const AskAi = ({
224
  : isUploading
225
  ? "Uploading images..."
226
  : isAiWorking && !isSameHtml
227
- ? "DeepSite is working..."
228
- : "DeepSite is thinking..."
229
  }
230
  />
231
  {isAiWorking && (
@@ -254,10 +255,10 @@ export const AskAi = ({
254
  )}
255
  placeholder={
256
  selectedElement
257
- ? `Ask DeepSite about ${selectedElement.tagName.toLowerCase()}...`
258
  : isFollowUp && (!isSameHtml || pages?.length > 1)
259
- ? "Ask DeepSite for edits"
260
- : "Ask DeepSite anything..."
261
  }
262
  value={prompt}
263
  onChange={(e) => setPrompt(e.target.value)}
@@ -288,6 +289,7 @@ export const AskAi = ({
288
  enhancedSettings={enhancedSettings!}
289
  setEnhancedSettings={setEnhancedSettings}
290
  />
 
291
  <Settings
292
  open={openProvider}
293
  error={providerError}
@@ -320,3 +322,4 @@ export const AskAi = ({
320
  </div>
321
  );
322
  };
 
 
15
  import { ReImagine } from "@/components/editor/ask-ai/re-imagine";
16
  import { Selector } from "@/components/editor/ask-ai/selector";
17
  import { PromptBuilder } from "@/components/editor/ask-ai/prompt-builder";
18
+ import { PromptEnhancer } from "@/components/editor/ask-ai/prompt-enhancer";
19
  import { useUser } from "@/hooks/useUser";
20
  import { useLoginModal } from "@/components/contexts/login-context";
21
  import { Settings } from "./settings";
22
  import { useProModal } from "@/components/contexts/pro-context";
23
+ import { IS_UNLIMITED, MAX_FREE_PROJECTS } from "@/lib/utils";
24
  import { PROMPTS_FOR_AI } from "@/lib/prompts";
25
 
26
  export const AskAi = ({
 
81
 
82
  const callAi = async (redesignMarkdown?: string) => {
83
  removePromptStorage();
84
+ if (!IS_UNLIMITED && user && !user.isPro && projects.length >= MAX_FREE_PROJECTS)
85
  return openProModal([]);
86
  if (isAiWorking) return;
87
  if (!redesignMarkdown && !prompt.trim()) return;
 
171
  }}
172
  >
173
  <p className="text-sm font-medium text-neutral-300 group-hover:text-neutral-200 transition-colors duration-200">
174
+ {isThinking ? "OmniDev is thinking..." : "OmniDev's plan"}
175
  </p>
176
  <ChevronDown
177
  className={classNames(
 
225
  : isUploading
226
  ? "Uploading images..."
227
  : isAiWorking && !isSameHtml
228
+ ? "OmniDev is working..."
229
+ : "OmniDev is thinking..."
230
  }
231
  />
232
  {isAiWorking && (
 
255
  )}
256
  placeholder={
257
  selectedElement
258
+ ? `Ask OmniDev about ${selectedElement.tagName.toLowerCase()}...`
259
  : isFollowUp && (!isSameHtml || pages?.length > 1)
260
+ ? "Ask OmniDev for edits"
261
+ : "Ask OmniDev anything..."
262
  }
263
  value={prompt}
264
  onChange={(e) => setPrompt(e.target.value)}
 
289
  enhancedSettings={enhancedSettings!}
290
  setEnhancedSettings={setEnhancedSettings}
291
  />
292
+ <PromptEnhancer prompt={prompt} setPrompt={setPrompt} enhancedSettings={enhancedSettings!} />
293
  <Settings
294
  open={openProvider}
295
  error={providerError}
 
322
  </div>
323
  );
324
  };
325
+
components/editor/ask-ai/prompt-enhancer.tsx ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+ import { WandSparkles } from "lucide-react";
3
+ import { Button } from "@/components/ui/button";
4
+ import { useState } from "react";
5
+ import { useAi } from "@/hooks/useAi";
6
+ import { EnhancedSettings } from "@/types";
7
+ import { toast } from "sonner";
8
+
9
+ export function PromptEnhancer({
10
+ prompt,
11
+ setPrompt,
12
+ enhancedSettings,
13
+ }: {
14
+ prompt: string;
15
+ setPrompt: (p: string) => void;
16
+ enhancedSettings: EnhancedSettings;
17
+ }) {
18
+ const { model, provider, globalAiLoading } = useAi();
19
+ const [loading, setLoading] = useState(false);
20
+
21
+ const enhance = async () => {
22
+ if (!prompt.trim()) return;
23
+ try {
24
+ setLoading(true);
25
+ const res = await fetch('/api/enhance', {
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json' },
28
+ body: JSON.stringify({ prompt, model, provider, enhancedSettings }),
29
+ });
30
+ const data = await res.json();
31
+ if (!res.ok || !data.ok) throw new Error(data.message || 'Failed to enhance prompt');
32
+ setPrompt(data.prompt);
33
+ toast.success('Prompt enhanced');
34
+ } catch (e: any) {
35
+ toast.error(e?.message || 'Failed to enhance');
36
+ } finally {
37
+ setLoading(false);
38
+ }
39
+ };
40
+
41
+ return (
42
+ <Button
43
+ size="xs"
44
+ variant="outline"
45
+ className="!rounded-md"
46
+ disabled={loading || globalAiLoading || !prompt.trim()}
47
+ onClick={enhance}
48
+ >
49
+ <WandSparkles className="size-3.5" /> Improve
50
+ </Button>
51
+ );
52
+ }
53
+
components/editor/index.tsx CHANGED
@@ -103,7 +103,7 @@ export const AppEditor = ({
103
  readOnlyMessage: {
104
  value: currentCommit
105
  ? "You can't edit the code, as this is an old version of the project."
106
- : "Wait for DeepSite to finish working...",
107
  isTrusted: true,
108
  },
109
  }}
 
103
  readOnlyMessage: {
104
  value: currentCommit
105
  ? "You can't edit the code, as this is an old version of the project."
106
+ : "Wait for OmniDev to finish working...",
107
  isTrusted: true,
108
  },
109
  }}
components/iframe-detector/index.tsx CHANGED
@@ -15,8 +15,7 @@ export default function IframeDetector() {
15
  host.endsWith(".hf.co") ||
16
  host === "huggingface.co" ||
17
  host === "hf.co" ||
18
- host === "enzostvs-deepsite.hf.space" ||
19
- host === "deepsite.hf.co"
20
  );
21
  };
22
 
 
15
  host.endsWith(".hf.co") ||
16
  host === "huggingface.co" ||
17
  host === "hf.co" ||
18
+ host === (process.env.NEXT_PUBLIC_ALLOWED_IFRAME_HOST || "omnidev.hf.co")
 
19
  );
20
  };
21
 
components/iframe-detector/modal.tsx CHANGED
@@ -21,7 +21,7 @@ export default function IframeWarningModal({
21
  }: // onOpenChange,
22
  IframeWarningModalProps) {
23
  const handleVisitSite = () => {
24
- window.open("https://deepsite.hf.co", "_blank");
25
  };
26
 
27
  return (
@@ -33,7 +33,7 @@ IframeWarningModalProps) {
33
  <DialogTitle>Unauthorized Embedding</DialogTitle>
34
  </div>
35
  <DialogDescription className="text-left">
36
- You&apos;re viewing DeepSite through an unauthorized iframe. For the
37
  best experience and security, please visit the official website
38
  directly.
39
  </DialogDescription>
@@ -52,7 +52,7 @@ IframeWarningModalProps) {
52
  <DialogFooter className="flex-col sm:flex-row gap-2">
53
  <Button onClick={handleVisitSite} className="w-full sm:w-auto">
54
  <ExternalLink className="mr-2 h-4 w-4" />
55
- Visit Deepsite.hf.co
56
  </Button>
57
  </DialogFooter>
58
  </DialogContent>
 
21
  }: // onOpenChange,
22
  IframeWarningModalProps) {
23
  const handleVisitSite = () => {
24
+ window.open(process.env.NEXT_PUBLIC_HOME_URL || "https://omnidev.hf.co", "_blank");
25
  };
26
 
27
  return (
 
33
  <DialogTitle>Unauthorized Embedding</DialogTitle>
34
  </div>
35
  <DialogDescription className="text-left">
36
+ You&apos;re viewing OmniDev through an unauthorized iframe. For the
37
  best experience and security, please visit the official website
38
  directly.
39
  </DialogDescription>
 
52
  <DialogFooter className="flex-col sm:flex-row gap-2">
53
  <Button onClick={handleVisitSite} className="w-full sm:w-auto">
54
  <ExternalLink className="mr-2 h-4 w-4" />
55
+ Visit OmniDev
56
  </Button>
57
  </DialogFooter>
58
  </DialogContent>
components/login-modal/index.tsx CHANGED
@@ -9,8 +9,8 @@ import { useEditor } from "@/hooks/useEditor";
9
  export const LoginModal = ({
10
  open,
11
  onClose,
12
- title = "Log In to use DeepSite for free",
13
- description = "Log In through your Hugging Face account to continue using DeepSite and increase your monthly free limit.",
14
  prompt,
15
  }: {
16
  open: boolean;
 
9
  export const LoginModal = ({
10
  open,
11
  onClose,
12
+ title = "Log In to use OmniDev",
13
+ description = "Log In through your Hugging Face account to continue using OmniDev.",
14
  prompt,
15
  }: {
16
  open: boolean;
components/my-projects/index.tsx CHANGED
@@ -7,7 +7,7 @@ import { toast } from "sonner";
7
  import { useUser } from "@/hooks/useUser";
8
  import { ProjectType } from "@/types";
9
  import { ProjectCard } from "./project-card";
10
- import { MAX_FREE_PROJECTS } from "@/lib/utils";
11
  import { ProTag } from "@/components/pro-modal";
12
  import { Button } from "@/components/ui/button";
13
  import { useProModal } from "@/components/contexts/pro-context";
@@ -39,28 +39,26 @@ export function MyProjects() {
39
  <div className="text-left">
40
  <h1 className="text-3xl font-bold text-white">
41
  <span className="capitalize">{user?.fullname}</span>&apos;s
42
- DeepSite Projects
43
- {user?.isPro ? (
44
- ""
45
- ) : (
46
  <span className="text-neutral-400 text-2xl ml-2">
47
  ({projects.length}/{MAX_FREE_PROJECTS})
48
  </span>
49
  )}
50
  </h1>
51
  <p className="text-muted-foreground text-lg mt-1 max-w-xl">
52
- {user?.isPro ? (
53
- "Create, manage, and explore your DeepSite projects."
54
- ) : (
55
  <span>
56
  Upgrade to{" "}
57
  <ProTag className="mx-1" onClick={() => openProModal([])} />{" "}
58
- to create unlimited projects with DeepSite.
59
  </span>
60
  )}
61
  </p>
62
  </div>
63
- {projects?.length >= MAX_FREE_PROJECTS && !user?.isPro ? (
64
  <Button
65
  size="default"
66
  variant="default"
@@ -74,7 +72,7 @@ export function MyProjects() {
74
  )}
75
  </header>
76
  <div className="mt-8 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
77
- {projects.length < MAX_FREE_PROJECTS || user?.isPro ? (
78
  <Link
79
  href="/new"
80
  className="bg-neutral-900 rounded-xl h-64 lg:h-44 flex items-center justify-center text-neutral-300 border border-neutral-800 hover:brightness-110 transition-all duration-200"
 
7
  import { useUser } from "@/hooks/useUser";
8
  import { ProjectType } from "@/types";
9
  import { ProjectCard } from "./project-card";
10
+ import { IS_UNLIMITED, MAX_FREE_PROJECTS } from "@/lib/utils";
11
  import { ProTag } from "@/components/pro-modal";
12
  import { Button } from "@/components/ui/button";
13
  import { useProModal } from "@/components/contexts/pro-context";
 
39
  <div className="text-left">
40
  <h1 className="text-3xl font-bold text-white">
41
  <span className="capitalize">{user?.fullname}</span>&apos;s
42
+ OmniDev Projects
43
+ {!IS_UNLIMITED && !user?.isPro && (
 
 
44
  <span className="text-neutral-400 text-2xl ml-2">
45
  ({projects.length}/{MAX_FREE_PROJECTS})
46
  </span>
47
  )}
48
  </h1>
49
  <p className="text-muted-foreground text-lg mt-1 max-w-xl">
50
+ {IS_UNLIMITED || user?.isPro
51
+ ? "Create, manage, and explore your OmniDev projects."
52
+ : (
53
  <span>
54
  Upgrade to{" "}
55
  <ProTag className="mx-1" onClick={() => openProModal([])} />{" "}
56
+ to create more projects.
57
  </span>
58
  )}
59
  </p>
60
  </div>
61
+ {projects?.length >= MAX_FREE_PROJECTS && !user?.isPro && !IS_UNLIMITED ? (
62
  <Button
63
  size="default"
64
  variant="default"
 
72
  )}
73
  </header>
74
  <div className="mt-8 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
75
+ {IS_UNLIMITED || projects.length < MAX_FREE_PROJECTS || user?.isPro ? (
76
  <Link
77
  href="/new"
78
  className="bg-neutral-900 rounded-xl h-64 lg:h-44 flex items-center justify-center text-neutral-300 border border-neutral-800 hover:brightness-110 transition-all duration-200"
lib/api.ts CHANGED
@@ -2,14 +2,14 @@ import axios from "axios";
2
  import MY_TOKEN_KEY from "./get-cookie-name";
3
 
4
  export const api = axios.create({
5
- baseURL: `/api`,
6
  headers: {
7
  cache: "no-store",
8
  },
9
  });
10
 
11
  export const apiServer = axios.create({
12
- baseURL: process.env.NEXT_APP_API_URL as string,
13
  headers: {
14
  cache: "no-store",
15
  },
 
2
  import MY_TOKEN_KEY from "./get-cookie-name";
3
 
4
  export const api = axios.create({
5
+ baseURL: (typeof window !== 'undefined' ? (process.env.NEXT_PUBLIC_APP_API_URL as string) : undefined) || `/api`,
6
  headers: {
7
  cache: "no-store",
8
  },
9
  });
10
 
11
  export const apiServer = axios.create({
12
+ baseURL: (process.env.NEXT_APP_API_URL as string) || `http://localhost:3000/api`,
13
  headers: {
14
  cache: "no-store",
15
  },
lib/prompts.ts CHANGED
@@ -25,7 +25,7 @@ You create website in a way a designer would, using ONLY HTML, CSS and Javascrip
25
  Try to create the best UI possible. Important: Make the website responsive by using TailwindCSS. Use it as much as you can, if you can't use it, use custom css (make sure to import tailwind with <script src="https://cdn.tailwindcss.com"></script> in the head).
26
  Also try to elaborate as much as you can, to create something unique, with a great design.
27
  If you want to use ICONS import Feather Icons (Make sure to add <script src="https://unpkg.com/feather-icons"></script> and <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> in the head., and <script>feather.replace();</script> in the body. Ex : <i data-feather="user"></i>).
28
- For interactive animations you can use: Vanta.js (Make sure to add <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> and <script>VANTA.GLOBE({...</script> in the body.).
29
  Don't hesitate to use real public API for the datas, you can find good ones here https://github.com/public-apis/public-apis depending on what the user asks for.
30
  You can create multiple pages website at once (following the format rules below) or a Single Page Application. But make sure to create multiple pages if the user asks for different pages.
31
  ${PROMPT_FOR_IMAGE_GENERATION}
@@ -167,4 +167,4 @@ export const PROMPTS_FOR_AI = [
167
  "Create a Password Generator, with a password generator section, and a history section.",
168
  "Create a Currency Converter, with a currency converter section, and a history section.",
169
  "Create a Dictionary, with a dictionary section, and a history section.",
170
- ];
 
25
  Try to create the best UI possible. Important: Make the website responsive by using TailwindCSS. Use it as much as you can, if you can't use it, use custom css (make sure to import tailwind with <script src="https://cdn.tailwindcss.com"></script> in the head).
26
  Also try to elaborate as much as you can, to create something unique, with a great design.
27
  If you want to use ICONS import Feather Icons (Make sure to add <script src="https://unpkg.com/feather-icons"></script> and <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> in the head., and <script>feather.replace();</script> in the body. Ex : <i data-feather="user"></i>).
28
+ For interactive animations you can use: Vanta.js (Make sure to add <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> and <script>VANTA.GLOBE({...</script> in the body.). Prefer stunning hero sections with animated backgrounds (e.g., animated gradients, particles, waves, noise overlay, parallax) while keeping performance and accessibility in mind.
29
  Don't hesitate to use real public API for the datas, you can find good ones here https://github.com/public-apis/public-apis depending on what the user asks for.
30
  You can create multiple pages website at once (following the format rules below) or a Single Page Application. But make sure to create multiple pages if the user asks for different pages.
31
  ${PROMPT_FOR_IMAGE_GENERATION}
 
167
  "Create a Password Generator, with a password generator section, and a history section.",
168
  "Create a Currency Converter, with a currency converter section, and a history section.",
169
  "Create a Dictionary, with a dictionary section, and a history section.",
170
+ ];
lib/seo.ts CHANGED
@@ -17,7 +17,7 @@ export function generateSEO({
17
  noIndex = false,
18
  canonical,
19
  }: SEOParams = {}): Metadata {
20
- const baseUrl = "https://deepsite.hf.co";
21
  const fullUrl = `${baseUrl}${path}`;
22
  const canonicalUrl = canonical || fullUrl;
23
 
@@ -96,7 +96,7 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
96
  ...baseStructuredData,
97
  name: 'DeepSite',
98
  description: 'Build websites with AI, no code required',
99
- url: 'https://deepsite.hf.co',
100
  applicationCategory: 'DeveloperApplication',
101
  operatingSystem: 'Web',
102
  offers: {
@@ -107,7 +107,7 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
107
  creator: {
108
  '@type': 'Organization',
109
  name: 'DeepSite',
110
- url: 'https://deepsite.hf.co',
111
  },
112
  ...data,
113
  };
@@ -116,8 +116,8 @@ export function generateStructuredData(type: 'WebApplication' | 'Organization' |
116
  return {
117
  ...baseStructuredData,
118
  name: 'DeepSite',
119
- url: 'https://deepsite.hf.co',
120
- logo: 'https://deepsite.hf.co/logo.svg',
121
  description: 'AI-powered web development platform',
122
  sameAs: [
123
  // Add social media links here if available
 
17
  noIndex = false,
18
  canonical,
19
  }: SEOParams = {}): Metadata {
20
+ const baseUrl = process.env.PUBLIC_BASE_URL || "https://deepsite.hf.co";
21
  const fullUrl = `${baseUrl}${path}`;
22
  const canonicalUrl = canonical || fullUrl;
23
 
 
96
  ...baseStructuredData,
97
  name: 'DeepSite',
98
  description: 'Build websites with AI, no code required',
99
+ url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
100
  applicationCategory: 'DeveloperApplication',
101
  operatingSystem: 'Web',
102
  offers: {
 
107
  creator: {
108
  '@type': 'Organization',
109
  name: 'DeepSite',
110
+ url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
111
  },
112
  ...data,
113
  };
 
116
  return {
117
  ...baseStructuredData,
118
  name: 'DeepSite',
119
+ url: process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co',
120
+ logo: `${process.env.PUBLIC_BASE_URL || 'https://deepsite.hf.co'}/logo.svg`,
121
  description: 'AI-powered web development platform',
122
  sameAs: [
123
  // Add social media links here if available
lib/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { clsx, type ClassValue } from "clsx";
2
  import { twMerge } from "tailwind-merge";
3
 
4
  export function cn(...inputs: ClassValue[]) {
@@ -16,7 +16,9 @@ export const COLORS = [
16
  ];
17
 
18
  export const getPTag = (repoId: string) => {
19
- return `<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=${repoId}" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p>`;
 
20
  };
21
 
22
- export const MAX_FREE_PROJECTS = 3;
 
 
1
+ import { clsx, type ClassValue } from "clsx";
2
  import { twMerge } from "tailwind-merge";
3
 
4
  export function cn(...inputs: ClassValue[]) {
 
16
  ];
17
 
18
  export const getPTag = (repoId: string) => {
19
+ const base = process.env.PUBLIC_BASE_URL || "https://omnidev.hf.co";
20
+ return `<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="${base}/logo.svg" alt="OmniDev Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="${base}" style="color: #fff;text-decoration: underline;" target="_blank">OmniDev</a> - <a href="${base}?remix=${repoId}" style="color: #fff;text-decoration: underline;" target="_blank">Remix</a></p>`;
21
  };
22
 
23
+ export const IS_UNLIMITED = (process.env.UNLIMITED_MODE || "").toLowerCase() === "true";
24
+ export const MAX_FREE_PROJECTS = IS_UNLIMITED ? 1000000 : 3;
middleware.ts CHANGED
@@ -10,7 +10,8 @@ export function middleware(request: NextRequest) {
10
  if (request.nextUrl.pathname.startsWith('/_next/static')) {
11
  response.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
12
  }
13
- response.headers.set('X-Canonical-URL', `https://deepsite.hf.co${request.nextUrl.pathname}`);
 
14
 
15
  return response;
16
  }
 
10
  if (request.nextUrl.pathname.startsWith('/_next/static')) {
11
  response.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
12
  }
13
+ const canonicalBase = process.env.PUBLIC_BASE_URL || `https://${request.nextUrl.host}`;
14
+ response.headers.set('X-Canonical-URL', `${canonicalBase}${request.nextUrl.pathname}`);
15
 
16
  return response;
17
  }