diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..406c10849e0ff8b05e8dcd6de5a88e3ea33ea7e5 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +OAUTH_CLIENT_ID= +OAUTH_CLIENT_SECRET= +APP_PORT=5173 +REDIRECT_URI=http://localhost:5173/auth/login +DEFAULT_HF_TOKEN= + +# Optional +# By setting this variable, you will bypass the login page + the free requests +# and will use the token directly. +# This is useful for testing purposes or local use. +HF_TOKEN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5ef6a520780202a1d6addd833d800ccb1ecac0bb..38d4117ba770f7518a4bc651b80446a4f2f218d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,26 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env +.aider* diff --git a/Dockerfile b/Dockerfile index cbe0188aaee92186937765d2c85d76f7b212c537..8003b5cb2da5b411b794263c7d70bae1f6866ae0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ -FROM node:20-alpine +# Dockerfile +# Use an official Node.js runtime as the base image +FROM node:22.1.0 USER root +RUN apt-get update USER 1000 WORKDIR /usr/src/app # Copy package.json and package-lock.json to the container @@ -13,7 +16,7 @@ RUN npm install RUN npm run build # Expose the application port (assuming your app runs on port 3000) -EXPOSE 3000 +EXPOSE 5173 # Start the application CMD ["npm", "start"] \ No newline at end of file diff --git a/Jornal Oliveira b/Jornal Oliveira new file mode 100644 index 0000000000000000000000000000000000000000..4cc007591d0ec03b25e13ba8b8a8e7b5e84e02f2 --- /dev/null +++ b/Jornal Oliveira @@ -0,0 +1,93 @@ + + + + + Jornal Oliveira + + + +
+

Jornal Oliveira

+

O Jornal da Escola Fala Sobre Você!

+
+

Sobre o Jornal

+

No Jornal da Escola, destacamos os projetos dos alunos, suas conquistas, suas ideias e tudo o que acontece no nosso dia a dia. Aqui, cada estudante tem vez e voz!

+ +
+

Envie sua Matéria

+ + + + +
+

Banco de Matérias (Acesso Restrito)

+ + +
+
+

Siga no Instagram

+ @jornal_oliveira +
+ \ No newline at end of file diff --git a/README.md b/README.md index 5ab2231fc7dc96070548f1d03ab1d0f73a799600..606f2489d425f097ed31a2c0be520bff4b1c1201 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ --- -title: DeepSite v2 +title: DeepSite emoji: 🐳 colorFrom: blue colorTo: blue sdk: docker pinned: true -app_port: 3000 +app_port: 5173 license: mit short_description: Generate any application with DeepSeek models: - deepseek-ai/DeepSeek-V3-0324 - - deepseek-ai/DeepSeek-R1-0528 --- # DeepSite 🐳 - DeepSite is a coding platform powered by DeepSeek AI, designed to make coding smarter and more efficient. Tailored for developers, data scientists, and AI engineers, it integrates generative AI into your coding projects to enhance creativity and productivity. ## How to use it locally - -Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74) +Follow [this discussion](https://huggingface.co/spaces/enzostvs/deepsite/discussions/74) \ No newline at end of file diff --git a/app/(public)/layout.tsx b/app/(public)/layout.tsx deleted file mode 100644 index 4a4ec57d2609c783602beb6c06c8dca6a1e6192d..0000000000000000000000000000000000000000 --- a/app/(public)/layout.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import Navigation from "@/components/public/navigation"; - -export default async function PublicLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( -
-
- - {children} -
- ); -} diff --git a/app/(public)/page.tsx b/app/(public)/page.tsx deleted file mode 100644 index c0849e72cf29027524ec9ebc3818e80a8aee5ef3..0000000000000000000000000000000000000000 --- a/app/(public)/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { AskAi } from "@/components/space/ask-ai"; -import { redirect } from "next/navigation"; -export default function Home() { - redirect("/projects/new"); - return ( - <> -
-
- ✨ DeepSite Public Beta -
-

- Code your website with AI in seconds -

-

- Vibe Coding has never been so easy. -

-
- -
-
-
-
-
-
-
-
-
-

- Community Driven -

-
-
-

- Deploy your website in seconds -

-
-
-

- Features that make you smile -

-
- - ); -} diff --git a/app/(public)/projects/page.tsx b/app/(public)/projects/page.tsx deleted file mode 100644 index 374dc6b1194256c5b142a62168ce0f414f6098be..0000000000000000000000000000000000000000 --- a/app/(public)/projects/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { redirect } from "next/navigation"; - -import { MyProjects } from "@/components/my-projects"; -import { getProjects } from "@/app/actions/projects"; - -export default async function ProjectsPage() { - const { ok, projects } = await getProjects(); - if (!ok) { - redirect("/"); - } - - return ; -} diff --git a/app/actions/auth.ts b/app/actions/auth.ts deleted file mode 100644 index a343e65e6726b35b32f022c117d3f3b5187d78e6..0000000000000000000000000000000000000000 --- a/app/actions/auth.ts +++ /dev/null @@ -1,18 +0,0 @@ -"use server"; - -import { headers } from "next/headers"; - -export async function getAuth() { - const authList = await headers(); - const host = authList.get("host") ?? "localhost:3000"; - const url = host.includes("/spaces/enzostvs") - ? "enzostvs-deepsite.hf.space" - : host; - const redirect_uri = - `${host.includes("localhost") ? "http://" : "https://"}` + - url + - "/auth/callback"; - - 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`; - return loginRedirectUrl; -} diff --git a/app/actions/projects.ts b/app/actions/projects.ts deleted file mode 100644 index 209b16d9d9960eeafb9e0b02d7b1b3eda638338d..0000000000000000000000000000000000000000 --- a/app/actions/projects.ts +++ /dev/null @@ -1,63 +0,0 @@ -"use server"; - -import { isAuthenticated } from "@/lib/auth"; -import { NextResponse } from "next/server"; -import dbConnect from "@/lib/mongodb"; -import Project from "@/models/Project"; -import { Project as ProjectType } from "@/types"; - -export async function getProjects(): Promise<{ - ok: boolean; - projects: ProjectType[]; -}> { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return { - ok: false, - projects: [], - }; - } - - await dbConnect(); - const projects = await Project.find({ - user_id: user?.id, - }) - .sort({ _createdAt: -1 }) - .limit(100) - .lean(); - if (!projects) { - return { - ok: false, - projects: [], - }; - } - return { - ok: true, - projects: JSON.parse(JSON.stringify(projects)) as ProjectType[], - }; -} - -export async function getProject( - namespace: string, - repoId: string -): Promise { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return null; - } - - await dbConnect(); - const project = await Project.findOne({ - user_id: user.id, - namespace, - repoId, - }).lean(); - - if (!project) { - return null; - } - - return JSON.parse(JSON.stringify(project)) as ProjectType; -} diff --git a/app/actions/rewrite-prompt.ts b/app/actions/rewrite-prompt.ts deleted file mode 100644 index 08bec54038b85c24c3f0d993ec388344d054fd09..0000000000000000000000000000000000000000 --- a/app/actions/rewrite-prompt.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { InferenceClient } from "@huggingface/inference"; - -const START_REWRITE_PROMPT = ">>>>>>> START PROMPT >>>>>>"; -const END_REWRITE_PROMPT = ">>>>>>> END PROMPT >>>>>>"; - -export const callAiRewritePrompt = async (prompt: string, { token, billTo }: { token: string, billTo?: string | null }) => { - const client = new InferenceClient(token); - const response = await client.chatCompletion( - { - model: "deepseek-ai/DeepSeek-V3.1", - provider: "novita", - messages: [{ - role: "system", - content: `You are a helpful assistant that rewrites prompts to make them better. All the prompts will be about creating a website or app. -Try to make the prompt more detailed and specific to create a good UI/UX Design and good code. -Format the result by following this format: -${START_REWRITE_PROMPT} -new prompt here -${END_REWRITE_PROMPT} -If you don't rewrite the prompt, return the original prompt. -Make sure to return the prompt in the same language as the prompt you are given. Also IMPORTANT: Make sure to keep the original intent of the prompt. Improve it it needed, but don't change the original intent. -` - },{ role: "user", content: prompt }], - }, - billTo ? { billTo } : {} - ); - - const responseContent = response.choices[0]?.message?.content; - if (!responseContent) { - return prompt; - } - const startIndex = responseContent.indexOf(START_REWRITE_PROMPT); - const endIndex = responseContent.indexOf(END_REWRITE_PROMPT); - return responseContent.substring(startIndex + START_REWRITE_PROMPT.length, endIndex); -}; \ No newline at end of file diff --git a/app/api/ask-ai/route.ts b/app/api/ask-ai/route.ts deleted file mode 100644 index 25a34108f99e8e3d0c6ccc00f345055445abcb47..0000000000000000000000000000000000000000 --- a/app/api/ask-ai/route.ts +++ /dev/null @@ -1,510 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { headers } from "next/headers"; -import { InferenceClient } from "@huggingface/inference"; - -import { MODELS, PROVIDERS } from "@/lib/providers"; -import { - DIVIDER, - FOLLOW_UP_SYSTEM_PROMPT, - INITIAL_SYSTEM_PROMPT, - MAX_REQUESTS_PER_IP, - NEW_PAGE_END, - NEW_PAGE_START, - REPLACE_END, - SEARCH_START, - UPDATE_PAGE_START, - UPDATE_PAGE_END, -} from "@/lib/prompts"; -import MY_TOKEN_KEY from "@/lib/get-cookie-name"; -import { Page } from "@/types"; - -const ipAddresses = new Map(); - -export async function POST(request: NextRequest) { - const authHeaders = await headers(); - const userToken = request.cookies.get(MY_TOKEN_KEY())?.value; - - const body = await request.json(); - const { prompt, provider, model, redesignMarkdown, previousPrompts, pages } = body; - - if (!model || (!prompt && !redesignMarkdown)) { - return NextResponse.json( - { ok: false, error: "Missing required fields" }, - { status: 400 } - ); - } - - const selectedModel = MODELS.find( - (m) => m.value === model || m.label === model - ); - - if (!selectedModel) { - return NextResponse.json( - { ok: false, error: "Invalid model selected" }, - { status: 400 } - ); - } - - if (!selectedModel.providers.includes(provider) && provider !== "auto") { - return NextResponse.json( - { - ok: false, - error: `The selected model does not support the ${provider} provider.`, - openSelectProvider: true, - }, - { status: 400 } - ); - } - - let token = userToken; - let billTo: string | null = null; - - /** - * Handle local usage token, this bypass the need for a user token - * and allows local testing without authentication. - * This is useful for development and testing purposes. - */ - if (process.env.HF_TOKEN && process.env.HF_TOKEN.length > 0) { - token = process.env.HF_TOKEN; - } - - const ip = authHeaders.get("x-forwarded-for")?.includes(",") - ? authHeaders.get("x-forwarded-for")?.split(",")[1].trim() - : authHeaders.get("x-forwarded-for"); - - if (!token) { - ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1); - if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) { - return NextResponse.json( - { - ok: false, - openLogin: true, - message: "Log In to continue using the service", - }, - { status: 429 } - ); - } - - token = process.env.DEFAULT_HF_TOKEN as string; - billTo = "huggingface"; - } - - const DEFAULT_PROVIDER = PROVIDERS.novita; - const selectedProvider = - provider === "auto" - ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS] - : PROVIDERS[provider as keyof typeof PROVIDERS] ?? DEFAULT_PROVIDER; - - const rewrittenPrompt = prompt; - - // if (prompt?.length < 240) { - - //rewrittenPrompt = await callAiRewritePrompt(prompt, { token, billTo }); - // } - - try { - const encoder = new TextEncoder(); - const stream = new TransformStream(); - const writer = stream.writable.getWriter(); - - const response = new NextResponse(stream.readable, { - headers: { - "Content-Type": "text/plain; charset=utf-8", - "Cache-Control": "no-cache", - Connection: "keep-alive", - }, - }); - - (async () => { - // let completeResponse = ""; - try { - const client = new InferenceClient(token); - const chatCompletion = client.chatCompletionStream( - { - model: selectedModel.value, - provider: selectedProvider.id as any, - messages: [ - { - role: "system", - content: INITIAL_SYSTEM_PROMPT, - }, - ...(pages?.length > 1 ? [{ - role: "assistant", - content: `Here are the current pages:\n\n${pages.map((p: Page) => `- ${p.path} \n${p.html}`).join("\n")}\n\nNow, please create a new page based on this code. Also here are the previous prompts:\n\n${previousPrompts.map((p: string) => `- ${p}`).join("\n")}` - }] : []), - { - role: "user", - content: redesignMarkdown - ? `Here is my current design as a markdown:\n\n${redesignMarkdown}\n\nNow, please create a new design based on this markdown.` - : rewrittenPrompt, - }, - ], - max_tokens: selectedProvider.max_tokens, - }, - billTo ? { billTo } : {} - ); - - while (true) { - const { done, value } = await chatCompletion.next(); - if (done) { - break; - } - - const chunk = value.choices[0]?.delta?.content; - if (chunk) { - await writer.write(encoder.encode(chunk)); - } - } - } catch (error: any) { - if (error.message?.includes("exceeded your monthly included credits")) { - await writer.write( - encoder.encode( - JSON.stringify({ - ok: false, - openProModal: true, - message: error.message, - }) - ) - ); - } else { - await writer.write( - encoder.encode( - JSON.stringify({ - ok: false, - message: - error.message || - "An error occurred while processing your request.", - }) - ) - ); - } - } finally { - await writer?.close(); - } - })(); - - return response; - } catch (error: any) { - return NextResponse.json( - { - ok: false, - openSelectProvider: true, - message: - error?.message || "An error occurred while processing your request.", - }, - { status: 500 } - ); - } -} - -export async function PUT(request: NextRequest) { - const authHeaders = await headers(); - const userToken = request.cookies.get(MY_TOKEN_KEY())?.value; - - const body = await request.json(); - const { prompt, previousPrompts, provider, selectedElementHtml, model, pages, files, } = - body; - - if (!prompt || pages.length === 0) { - return NextResponse.json( - { ok: false, error: "Missing required fields" }, - { status: 400 } - ); - } - - const selectedModel = MODELS.find( - (m) => m.value === model || m.label === model - ); - if (!selectedModel) { - return NextResponse.json( - { ok: false, error: "Invalid model selected" }, - { status: 400 } - ); - } - - let token = userToken; - let billTo: string | null = null; - - /** - * Handle local usage token, this bypass the need for a user token - * and allows local testing without authentication. - * This is useful for development and testing purposes. - */ - if (process.env.HF_TOKEN && process.env.HF_TOKEN.length > 0) { - token = process.env.HF_TOKEN; - } - - const ip = authHeaders.get("x-forwarded-for")?.includes(",") - ? authHeaders.get("x-forwarded-for")?.split(",")[1].trim() - : authHeaders.get("x-forwarded-for"); - - if (!token) { - ipAddresses.set(ip, (ipAddresses.get(ip) || 0) + 1); - if (ipAddresses.get(ip) > MAX_REQUESTS_PER_IP) { - return NextResponse.json( - { - ok: false, - openLogin: true, - message: "Log In to continue using the service", - }, - { status: 429 } - ); - } - - token = process.env.DEFAULT_HF_TOKEN as string; - billTo = "huggingface"; - } - - const client = new InferenceClient(token); - - const DEFAULT_PROVIDER = PROVIDERS.novita; - const selectedProvider = - provider === "auto" - ? PROVIDERS[selectedModel.autoProvider as keyof typeof PROVIDERS] - : PROVIDERS[provider as keyof typeof PROVIDERS] ?? DEFAULT_PROVIDER; - - try { - const response = await client.chatCompletion( - { - model: selectedModel.value, - provider: selectedProvider.id as any, - messages: [ - { - role: "system", - content: FOLLOW_UP_SYSTEM_PROMPT, - }, - { - role: "user", - content: previousPrompts - ? `Also here are the previous prompts:\n\n${previousPrompts.map((p: string) => `- ${p}`).join("\n")}` - : "You are modifying the HTML file based on the user's request.", - }, - { - role: "assistant", - - content: `${ - selectedElementHtml - ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\`` - : "" - }. Current pages: ${pages?.map((p: Page) => `- ${p.path} \n${p.html}`).join("\n")}. ${files?.length > 0 ? `Current images: ${files?.map((f: string) => `- ${f}`).join("\n")}.` : ""}`, - }, - { - role: "user", - content: prompt, - }, - ], - ...(selectedProvider.id !== "sambanova" - ? { - max_tokens: selectedProvider.max_tokens, - } - : {}), - }, - billTo ? { billTo } : {} - ); - - const chunk = response.choices[0]?.message?.content; - if (!chunk) { - return NextResponse.json( - { ok: false, message: "No content returned from the model" }, - { status: 400 } - ); - } - - if (chunk) { - const updatedLines: number[][] = []; - let newHtml = ""; - const updatedPages = [...(pages || [])]; - - const updatePageRegex = new RegExp(`${UPDATE_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^\\s]+)\\s*${UPDATE_PAGE_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([\\s\\S]*?)(?=${UPDATE_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|${NEW_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|$)`, 'g'); - let updatePageMatch; - - while ((updatePageMatch = updatePageRegex.exec(chunk)) !== null) { - const [, pagePath, pageContent] = updatePageMatch; - - const pageIndex = updatedPages.findIndex(p => p.path === pagePath); - if (pageIndex !== -1) { - let pageHtml = updatedPages[pageIndex].html; - - let processedContent = pageContent; - const htmlMatch = pageContent.match(/```html\s*([\s\S]*?)\s*```/); - if (htmlMatch) { - processedContent = htmlMatch[1]; - } - let position = 0; - let moreBlocks = true; - - while (moreBlocks) { - const searchStartIndex = processedContent.indexOf(SEARCH_START, position); - if (searchStartIndex === -1) { - moreBlocks = false; - continue; - } - - const dividerIndex = processedContent.indexOf(DIVIDER, searchStartIndex); - if (dividerIndex === -1) { - moreBlocks = false; - continue; - } - - const replaceEndIndex = processedContent.indexOf(REPLACE_END, dividerIndex); - if (replaceEndIndex === -1) { - moreBlocks = false; - continue; - } - - const searchBlock = processedContent.substring( - searchStartIndex + SEARCH_START.length, - dividerIndex - ); - const replaceBlock = processedContent.substring( - dividerIndex + DIVIDER.length, - replaceEndIndex - ); - - if (searchBlock.trim() === "") { - pageHtml = `${replaceBlock}\n${pageHtml}`; - updatedLines.push([1, replaceBlock.split("\n").length]); - } else { - const blockPosition = pageHtml.indexOf(searchBlock); - if (blockPosition !== -1) { - const beforeText = pageHtml.substring(0, blockPosition); - const startLineNumber = beforeText.split("\n").length; - const replaceLines = replaceBlock.split("\n").length; - const endLineNumber = startLineNumber + replaceLines - 1; - - updatedLines.push([startLineNumber, endLineNumber]); - pageHtml = pageHtml.replace(searchBlock, replaceBlock); - } - } - - position = replaceEndIndex + REPLACE_END.length; - } - - updatedPages[pageIndex].html = pageHtml; - - if (pagePath === '/' || pagePath === '/index' || pagePath === 'index') { - newHtml = pageHtml; - } - } - } - - const newPageRegex = new RegExp(`${NEW_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^\\s]+)\\s*${NEW_PAGE_END.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([\\s\\S]*?)(?=${UPDATE_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|${NEW_PAGE_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}|$)`, 'g'); - let newPageMatch; - - while ((newPageMatch = newPageRegex.exec(chunk)) !== null) { - const [, pagePath, pageContent] = newPageMatch; - - let pageHtml = pageContent; - const htmlMatch = pageContent.match(/```html\s*([\s\S]*?)\s*```/); - if (htmlMatch) { - pageHtml = htmlMatch[1]; - } - - const existingPageIndex = updatedPages.findIndex(p => p.path === pagePath); - - if (existingPageIndex !== -1) { - updatedPages[existingPageIndex] = { - path: pagePath, - html: pageHtml.trim() - }; - } else { - updatedPages.push({ - path: pagePath, - html: pageHtml.trim() - }); - } - } - - if (updatedPages.length === pages?.length && !chunk.includes(UPDATE_PAGE_START)) { - let position = 0; - let moreBlocks = true; - - while (moreBlocks) { - const searchStartIndex = chunk.indexOf(SEARCH_START, position); - if (searchStartIndex === -1) { - moreBlocks = false; - continue; - } - - const dividerIndex = chunk.indexOf(DIVIDER, searchStartIndex); - if (dividerIndex === -1) { - moreBlocks = false; - continue; - } - - const replaceEndIndex = chunk.indexOf(REPLACE_END, dividerIndex); - if (replaceEndIndex === -1) { - moreBlocks = false; - continue; - } - - const searchBlock = chunk.substring( - searchStartIndex + SEARCH_START.length, - dividerIndex - ); - const replaceBlock = chunk.substring( - dividerIndex + DIVIDER.length, - replaceEndIndex - ); - - if (searchBlock.trim() === "") { - newHtml = `${replaceBlock}\n${newHtml}`; - updatedLines.push([1, replaceBlock.split("\n").length]); - } else { - const blockPosition = newHtml.indexOf(searchBlock); - if (blockPosition !== -1) { - const beforeText = newHtml.substring(0, blockPosition); - const startLineNumber = beforeText.split("\n").length; - const replaceLines = replaceBlock.split("\n").length; - const endLineNumber = startLineNumber + replaceLines - 1; - - updatedLines.push([startLineNumber, endLineNumber]); - newHtml = newHtml.replace(searchBlock, replaceBlock); - } - } - - position = replaceEndIndex + REPLACE_END.length; - } - - // Update the main HTML if it's the index page - const mainPageIndex = updatedPages.findIndex(p => p.path === '/' || p.path === '/index' || p.path === 'index'); - if (mainPageIndex !== -1) { - updatedPages[mainPageIndex].html = newHtml; - } - } - - return NextResponse.json({ - ok: true, - updatedLines, - pages: updatedPages, - }); - } else { - return NextResponse.json( - { ok: false, message: "No content returned from the model" }, - { status: 400 } - ); - } - } catch (error: any) { - if (error.message?.includes("exceeded your monthly included credits")) { - return NextResponse.json( - { - ok: false, - openProModal: true, - message: error.message, - }, - { status: 402 } - ); - } - return NextResponse.json( - { - ok: false, - openSelectProvider: true, - message: - error.message || "An error occurred while processing your request.", - }, - { status: 500 } - ); - } -} diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts deleted file mode 100644 index 221e0c266bd2e6673779b9e81caf333243147f60..0000000000000000000000000000000000000000 --- a/app/api/auth/route.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -export async function POST(req: NextRequest) { - const body = await req.json(); - const { code } = body; - - if (!code) { - return NextResponse.json( - { error: "Code is required" }, - { - status: 400, - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - const Authorization = `Basic ${Buffer.from( - `${process.env.OAUTH_CLIENT_ID}:${process.env.OAUTH_CLIENT_SECRET}` - ).toString("base64")}`; - - const host = - req.headers.get("host") ?? req.headers.get("origin") ?? "localhost:3000"; - - const url = host.includes("/spaces/enzostvs") - ? "enzostvs-deepsite.hf.space" - : host; - const redirect_uri = - `${host.includes("localhost") ? "http://" : "https://"}` + - url + - "/auth/callback"; - const request_auth = await fetch("https://huggingface.co/oauth/token", { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Authorization, - }, - body: new URLSearchParams({ - grant_type: "authorization_code", - code, - redirect_uri, - }), - }); - - const response = await request_auth.json(); - if (!response.access_token) { - return NextResponse.json( - { error: "Failed to retrieve access token" }, - { - status: 400, - headers: { - "Content-Type": "application/json", - }, - } - ); - } - - const userResponse = await fetch("https://huggingface.co/api/whoami-v2", { - headers: { - Authorization: `Bearer ${response.access_token}`, - }, - }); - - if (!userResponse.ok) { - return NextResponse.json( - { user: null, errCode: userResponse.status }, - { status: userResponse.status } - ); - } - const user = await userResponse.json(); - - return NextResponse.json( - { - access_token: response.access_token, - expires_in: response.expires_in, - user, - }, - { - status: 200, - headers: { - "Content-Type": "application/json", - }, - } - ); -} diff --git a/app/api/me/projects/[namespace]/[repoId]/images/route.ts b/app/api/me/projects/[namespace]/[repoId]/images/route.ts deleted file mode 100644 index 4ab46939bdde0aaeb7163af0e96e94651bfccfd7..0000000000000000000000000000000000000000 --- a/app/api/me/projects/[namespace]/[repoId]/images/route.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { RepoDesignation, uploadFiles } from "@huggingface/hub"; - -import { isAuthenticated } from "@/lib/auth"; -import Project from "@/models/Project"; -import dbConnect from "@/lib/mongodb"; - -// No longer need the ImageUpload interface since we're handling FormData with File objects - -export async function POST( - req: NextRequest, - { params }: { params: Promise<{ namespace: string; repoId: string }> } -) { - try { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - await dbConnect(); - const param = await params; - const { namespace, repoId } = param; - - const project = await Project.findOne({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - }).lean(); - - if (!project) { - return NextResponse.json( - { - ok: false, - error: "Project not found", - }, - { status: 404 } - ); - } - - // Parse the FormData to get the images - const formData = await req.formData(); - const imageFiles = formData.getAll("images") as File[]; - - if (!imageFiles || imageFiles.length === 0) { - return NextResponse.json( - { - ok: false, - error: "At least one image file is required under the 'images' key", - }, - { status: 400 } - ); - } - - const files: File[] = []; - for (const file of imageFiles) { - if (!(file instanceof File)) { - return NextResponse.json( - { - ok: false, - error: "Invalid file format - all items under 'images' key must be files", - }, - { status: 400 } - ); - } - - if (!file.type.startsWith('image/')) { - return NextResponse.json( - { - ok: false, - error: `File ${file.name} is not an image`, - }, - { status: 400 } - ); - } - - // Create File object with images/ folder prefix - const fileName = `images/${file.name}`; - const processedFile = new File([file], fileName, { type: file.type }); - files.push(processedFile); - } - - // Upload files to HuggingFace space - const repo: RepoDesignation = { - type: "space", - name: `${namespace}/${repoId}`, - }; - - await uploadFiles({ - repo, - files, - accessToken: user.token as string, - commitTitle: `Upload ${files.length} image(s)`, - }); - - return NextResponse.json({ - ok: true, - message: `Successfully uploaded ${files.length} image(s) to ${namespace}/${repoId}/images/`, - uploadedFiles: files.map((file) => `https://huggingface.co/spaces/${namespace}/${repoId}/resolve/main/${file.name}`), - }, { status: 200 }); - - } catch (error) { - console.error('Error uploading images:', error); - return NextResponse.json( - { - ok: false, - error: "Failed to upload images", - }, - { status: 500 } - ); - } -} diff --git a/app/api/me/projects/[namespace]/[repoId]/route.ts b/app/api/me/projects/[namespace]/[repoId]/route.ts deleted file mode 100644 index feecdd6215cc28e8bf952448636d781dff372d5a..0000000000000000000000000000000000000000 --- a/app/api/me/projects/[namespace]/[repoId]/route.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { RepoDesignation, spaceInfo, uploadFiles, listFiles } from "@huggingface/hub"; - -import { isAuthenticated } from "@/lib/auth"; -import Project from "@/models/Project"; -import dbConnect from "@/lib/mongodb"; -import { Page } from "@/types"; - -export async function GET( - req: NextRequest, - { params }: { params: Promise<{ namespace: string; repoId: string }> } -) { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - await dbConnect(); - const param = await params; - const { namespace, repoId } = param; - - const project = await Project.findOne({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - }).lean(); - if (!project) { - return NextResponse.json( - { - ok: false, - error: "Project not found", - }, - { status: 404 } - ); - } - try { - const space = await spaceInfo({ - name: namespace + "/" + repoId, - accessToken: user.token as string, - additionalFields: ["author"], - }); - - if (!space || space.sdk !== "static") { - return NextResponse.json( - { - ok: false, - error: "Space is not a static space", - }, - { status: 404 } - ); - } - if (space.author !== user.name) { - return NextResponse.json( - { - ok: false, - error: "Space does not belong to the authenticated user", - }, - { status: 403 } - ); - } - - const repo: RepoDesignation = { - type: "space", - name: `${namespace}/${repoId}`, - }; - - const htmlFiles: Page[] = []; - const images: string[] = []; - - const allowedImagesExtensions = ["jpg", "jpeg", "png", "gif", "svg", "webp", "avif", "heic", "heif", "ico", "bmp", "tiff", "tif"]; - - for await (const fileInfo of listFiles({repo, accessToken: user.token as string})) { - if (fileInfo.path.endsWith(".html")) { - const res = await fetch(`https://huggingface.co/spaces/${namespace}/${repoId}/raw/main/${fileInfo.path}`); - if (res.ok) { - const html = await res.text(); - if (fileInfo.path === "index.html") { - htmlFiles.unshift({ - path: fileInfo.path, - html, - }); - } else { - htmlFiles.push({ - path: fileInfo.path, - html, - }); - } - } - } - if (fileInfo.type === "directory" && fileInfo.path === "images") { - for await (const imageInfo of listFiles({repo, accessToken: user.token as string, path: fileInfo.path})) { - if (allowedImagesExtensions.includes(imageInfo.path.split(".").pop() || "")) { - images.push(`https://huggingface.co/spaces/${namespace}/${repoId}/resolve/main/${imageInfo.path}`); - } - } - } - } - - if (htmlFiles.length === 0) { - return NextResponse.json( - { - ok: false, - error: "No HTML files found", - }, - { status: 404 } - ); - } - - return NextResponse.json( - { - project: { - ...project, - pages: htmlFiles, - images, - }, - ok: true, - }, - { status: 200 } - ); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - if (error.statusCode === 404) { - await Project.deleteOne({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - }); - return NextResponse.json( - { error: "Space not found", ok: false }, - { status: 404 } - ); - } - return NextResponse.json( - { error: error.message, ok: false }, - { status: 500 } - ); - } -} - -export async function PUT( - req: NextRequest, - { params }: { params: Promise<{ namespace: string; repoId: string }> } -) { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - await dbConnect(); - const param = await params; - const { namespace, repoId } = param; - const { pages, prompts } = await req.json(); - - const project = await Project.findOne({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - }).lean(); - if (!project) { - return NextResponse.json( - { - ok: false, - error: "Project not found", - }, - { status: 404 } - ); - } - - const repo: RepoDesignation = { - type: "space", - name: `${namespace}/${repoId}`, - }; - - const files: File[] = []; - const promptsFile = new File([prompts.join("\n")], "prompts.txt", { - type: "text/plain", - }); - files.push(promptsFile); - pages.forEach((page: Page) => { - const file = new File([page.html], page.path, { type: "text/html" }); - files.push(file); - }); - await uploadFiles({ - repo, - files, - accessToken: user.token as string, - commitTitle: `${prompts[prompts.length - 1]} - Follow Up Deployment`, - }); - - await Project.updateOne( - { user_id: user.id, space_id: `${namespace}/${repoId}` }, - { - $set: { - prompts: [ - ...prompts, - ], - }, - } - ); - return NextResponse.json({ ok: true }, { status: 200 }); -} - -export async function POST( - req: NextRequest, - { params }: { params: Promise<{ namespace: string; repoId: string }> } -) { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - await dbConnect(); - const param = await params; - const { namespace, repoId } = param; - - const space = await spaceInfo({ - name: namespace + "/" + repoId, - accessToken: user.token as string, - additionalFields: ["author"], - }); - - if (!space || space.sdk !== "static") { - return NextResponse.json( - { - ok: false, - error: "Space is not a static space", - }, - { status: 404 } - ); - } - if (space.author !== user.name) { - return NextResponse.json( - { - ok: false, - error: "Space does not belong to the authenticated user", - }, - { status: 403 } - ); - } - - const project = await Project.findOne({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - }).lean(); - if (project) { - // redirect to the project page if it already exists - return NextResponse.json( - { - ok: false, - error: "Project already exists", - redirect: `/projects/${namespace}/${repoId}`, - }, - { status: 400 } - ); - } - - const newProject = new Project({ - user_id: user.id, - space_id: `${namespace}/${repoId}`, - prompts: [], - }); - - await newProject.save(); - return NextResponse.json( - { - ok: true, - project: { - id: newProject._id, - space_id: newProject.space_id, - prompts: newProject.prompts, - }, - }, - { status: 201 } - ); -} diff --git a/app/api/me/projects/route.ts b/app/api/me/projects/route.ts deleted file mode 100644 index cc3a3907101a1c7c79a7e1968b508936fa0668e4..0000000000000000000000000000000000000000 --- a/app/api/me/projects/route.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { createRepo, RepoDesignation, uploadFiles } from "@huggingface/hub"; - -import { isAuthenticated } from "@/lib/auth"; -import Project from "@/models/Project"; -import dbConnect from "@/lib/mongodb"; -import { COLORS } from "@/lib/utils"; -import { Page } from "@/types"; - -export async function GET() { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - await dbConnect(); - - const projects = await Project.find({ - user_id: user?.id, - }) - .sort({ _createdAt: -1 }) - .limit(100) - .lean(); - if (!projects) { - return NextResponse.json( - { - ok: false, - projects: [], - }, - { status: 404 } - ); - } - return NextResponse.json( - { - ok: true, - projects, - }, - { status: 200 } - ); -} - -export async function POST(request: NextRequest) { - const user = await isAuthenticated(); - - if (user instanceof NextResponse || !user) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - - const { title, pages, prompts } = await request.json(); - - if (!title || !pages || pages.length === 0) { - return NextResponse.json( - { message: "Title and HTML content are required.", ok: false }, - { status: 400 } - ); - } - - await dbConnect(); - - try { - let readme = ""; - - const newTitle = title - .toLowerCase() - .replace(/[^a-z0-9]+/g, "-") - .split("-") - .filter(Boolean) - .join("-") - .slice(0, 96); - - const repo: RepoDesignation = { - type: "space", - name: `${user.name}/${newTitle}`, - }; - - const { repoUrl } = await createRepo({ - repo, - accessToken: user.token as string, - }); - const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)]; - const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)]; - readme = `--- -title: ${newTitle} -emoji: 🐳 -colorFrom: ${colorFrom} -colorTo: ${colorTo} -sdk: static -pinned: false -tags: - - deepsite ---- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference`; - - const readmeFile = new File([readme], "README.md", { - type: "text/markdown", - }); - const promptsFile = new File([prompts.join("\n")], "prompts.txt", { - type: "text/plain", - }); - const files = [readmeFile, promptsFile]; - pages.forEach((page: Page) => { - const file = new File([page.html], page.path, { type: "text/html" }); - files.push(file); - }); - await uploadFiles({ - repo, - files, - accessToken: user.token as string, - commitTitle: `${prompts[prompts.length - 1]} - Initial Deployment`, - }); - const path = repoUrl.split("/").slice(-2).join("/"); - const project = await Project.create({ - user_id: user.id, - space_id: path, - prompts, - }); - return NextResponse.json({ project, path, ok: true }, { status: 201 }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - return NextResponse.json( - { error: err.message, ok: false }, - { status: 500 } - ); - } -} diff --git a/app/api/me/route.ts b/app/api/me/route.ts deleted file mode 100644 index c4164daba5c58bb2fe7f4f7508de7165f32ca443..0000000000000000000000000000000000000000 --- a/app/api/me/route.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { headers } from "next/headers"; -import { NextResponse } from "next/server"; - -export async function GET() { - const authHeaders = await headers(); - const token = authHeaders.get("Authorization"); - if (!token) { - return NextResponse.json({ user: null, errCode: 401 }, { status: 401 }); - } - - const userResponse = await fetch("https://huggingface.co/api/whoami-v2", { - headers: { - Authorization: `${token}`, - }, - }); - - if (!userResponse.ok) { - return NextResponse.json( - { user: null, errCode: userResponse.status }, - { status: userResponse.status } - ); - } - const user = await userResponse.json(); - return NextResponse.json({ user, errCode: null }, { status: 200 }); -} diff --git a/app/api/re-design/route.ts b/app/api/re-design/route.ts deleted file mode 100644 index 777c2cbd18f95592080c0fe1ad2cab23a5264397..0000000000000000000000000000000000000000 --- a/app/api/re-design/route.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -export async function PUT(request: NextRequest) { - const body = await request.json(); - const { url } = body; - - if (!url) { - return NextResponse.json({ error: "URL is required" }, { status: 400 }); - } - - try { - const response = await fetch( - `https://r.jina.ai/${encodeURIComponent(url)}`, - { - method: "POST", - } - ); - if (!response.ok) { - return NextResponse.json( - { error: "Failed to fetch redesign" }, - { status: 500 } - ); - } - const markdown = await response.text(); - return NextResponse.json( - { - ok: true, - markdown, - }, - { status: 200 } - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { - return NextResponse.json( - { error: error.message || "An error occurred" }, - { status: 500 } - ); - } -} diff --git a/app/auth/callback/page.tsx b/app/auth/callback/page.tsx deleted file mode 100644 index ac44f365b707908d6003a4266db1269d297a4336..0000000000000000000000000000000000000000 --- a/app/auth/callback/page.tsx +++ /dev/null @@ -1,72 +0,0 @@ -"use client"; -import Link from "next/link"; -import { useUser } from "@/hooks/useUser"; -import { use, useState } from "react"; -import { useMount, useTimeoutFn } from "react-use"; - -import { Button } from "@/components/ui/button"; -export default function AuthCallback({ - searchParams, -}: { - searchParams: Promise<{ code: string }>; -}) { - const [showButton, setShowButton] = useState(false); - const { code } = use(searchParams); - const { loginFromCode } = useUser(); - - useMount(async () => { - if (code) { - await loginFromCode(code); - } - }); - - useTimeoutFn( - () => setShowButton(true), - 7000 // Show button after 5 seconds - ); - - return ( -
-
-
-
-
- 🚀 -
-
- 👋 -
-
- 🙌 -
-
-

- Login In Progress... -

-

- Wait a moment while we log you in with your code. -

-
-
-
-

- If you are not redirected automatically in the next 5 seconds, - please click the button below -

- {showButton ? ( - - - - ) : ( -

- Please wait, we are logging you in... -

- )} -
-
-
-
- ); -} diff --git a/app/auth/page.tsx b/app/auth/page.tsx deleted file mode 100644 index a45a6bc6f58907b4ee5efbf0f70a51ee153625c7..0000000000000000000000000000000000000000 --- a/app/auth/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { redirect } from "next/navigation"; -import { Metadata } from "next"; - -import { getAuth } from "@/app/actions/auth"; - -export const revalidate = 1; - -export const metadata: Metadata = { - robots: "noindex, nofollow", -}; - -export default async function Auth() { - const loginRedirectUrl = await getAuth(); - if (loginRedirectUrl) { - redirect(loginRedirectUrl); - } - - return ( -
-
-

Error

-

- An error occurred while trying to log in. Please try again later. -

-
-
- ); -} diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 Binary files a/app/favicon.ico and /dev/null differ diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index d960a121272f5b976ae70dbd1c7656e181ca6e6d..0000000000000000000000000000000000000000 --- a/app/layout.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { Metadata, Viewport } from "next"; -import { Inter, PT_Sans } from "next/font/google"; -import { cookies } from "next/headers"; - -import TanstackProvider from "@/components/providers/tanstack-query-provider"; -import "@/assets/globals.css"; -import { Toaster } from "@/components/ui/sonner"; -import MY_TOKEN_KEY from "@/lib/get-cookie-name"; -import { apiServer } from "@/lib/api"; -import AppContext from "@/components/contexts/app-context"; -import Script from "next/script"; -import IframeDetector from "@/components/iframe-detector"; - -const inter = Inter({ - variable: "--font-inter-sans", - subsets: ["latin"], -}); - -const ptSans = PT_Sans({ - variable: "--font-ptSans-mono", - subsets: ["latin"], - weight: ["400", "700"], -}); - -export const metadata: Metadata = { - title: "DeepSite | Build with AI ✨", - description: - "DeepSite is a web development tool that helps you build websites with AI, no code required. Let's deploy your website with DeepSite and enjoy the magic of AI.", - openGraph: { - title: "DeepSite | Build with AI ✨", - description: - "DeepSite is a web development tool that helps you build websites with AI, no code required. Let's deploy your website with DeepSite and enjoy the magic of AI.", - url: "https://deepsite.hf.co", - siteName: "DeepSite", - images: [ - { - url: "https://deepsite.hf.co/banner.png", - width: 1200, - height: 630, - alt: "DeepSite Open Graph Image", - }, - ], - }, - twitter: { - card: "summary_large_image", - title: "DeepSite | Build with AI ✨", - description: - "DeepSite is a web development tool that helps you build websites with AI, no code required. Let's deploy your website with DeepSite and enjoy the magic of AI.", - images: ["https://deepsite.hf.co/banner.png"], - }, - appleWebApp: { - capable: true, - title: "DeepSite", - statusBarStyle: "black-translucent", - }, - icons: { - icon: "/logo.svg", - shortcut: "/logo.svg", - apple: "/logo.svg", - }, -}; - -export const viewport: Viewport = { - initialScale: 1, - maximumScale: 1, - themeColor: "#000000", -}; - -async function getMe() { - const cookieStore = await cookies(); - const token = cookieStore.get(MY_TOKEN_KEY())?.value; - if (!token) return { user: null, errCode: null }; - try { - const res = await apiServer.get("/me", { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return { user: res.data.user, errCode: null }; - } catch (err: any) { - return { user: null, errCode: err.status }; - } -} - -// if domain isn't deepsite.hf.co or enzostvs-deepsite.hf.space redirect to deepsite.hf.co - -export default async function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - const data = await getMe(); - return ( - - - - - - - {children} - - - - ); -} diff --git a/app/projects/[namespace]/[repoId]/page.tsx b/app/projects/[namespace]/[repoId]/page.tsx deleted file mode 100644 index e60c46e774de73ea30c6964ae49c5a32daf6c87d..0000000000000000000000000000000000000000 --- a/app/projects/[namespace]/[repoId]/page.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { cookies } from "next/headers"; -import { redirect } from "next/navigation"; - -import { apiServer } from "@/lib/api"; -import MY_TOKEN_KEY from "@/lib/get-cookie-name"; -import { AppEditor } from "@/components/editor"; - -async function getProject(namespace: string, repoId: string) { - // TODO replace with a server action - const cookieStore = await cookies(); - const token = cookieStore.get(MY_TOKEN_KEY())?.value; - if (!token) return {}; - try { - const { data } = await apiServer.get( - `/me/projects/${namespace}/${repoId}`, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); - - return data.project; - } catch { - return {}; - } -} - -export default async function ProjectNamespacePage({ - params, -}: { - params: Promise<{ namespace: string; repoId: string }>; -}) { - const { namespace, repoId } = await params; - const data = await getProject(namespace, repoId); - if (!data?.pages) { - redirect("/projects"); - } - return ( - - ); -} diff --git a/app/projects/new/page.tsx b/app/projects/new/page.tsx deleted file mode 100644 index 9dabea0dcf9de7c30b66ff35588740ed59de3f0e..0000000000000000000000000000000000000000 --- a/app/projects/new/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { AppEditor } from "@/components/editor"; - -export default function ProjectsNewPage() { - return ; -} diff --git a/assets/globals.css b/assets/globals.css deleted file mode 100644 index 19dd59e7dcc34e453e9850a052ae8f039628e58a..0000000000000000000000000000000000000000 --- a/assets/globals.css +++ /dev/null @@ -1,146 +0,0 @@ -@import "tailwindcss"; -@import "tw-animate-css"; - -@custom-variant dark (&:is(.dark *)); - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-inter-sans); - --font-mono: var(--font-ptSans-mono); - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); -} - -:root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } - html { - @apply scroll-smooth; - } -} - -.background__noisy { - @apply bg-blend-normal pointer-events-none opacity-90; - background-size: 25ww auto; - background-image: url("/background_noisy.webp"); - @apply fixed w-screen h-screen -z-1 top-0 left-0; -} - -.monaco-editor .margin { - @apply !bg-neutral-900; -} -.monaco-editor .monaco-editor-background { - @apply !bg-neutral-900; -} -.monaco-editor .line-numbers { - @apply !text-neutral-500; -} - -.matched-line { - @apply bg-sky-500/30; -} diff --git a/assets/logo.svg b/assets/logo.svg deleted file mode 100644 index e69f057d4d4c256f02881888e781aa0943010c3e..0000000000000000000000000000000000000000 --- a/assets/logo.svg +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/components.json b/components.json deleted file mode 100644 index 335484f9424bf72b98e3b892275740bc8f014754..0000000000000000000000000000000000000000 --- a/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} \ No newline at end of file diff --git a/components/contexts/app-context.tsx b/components/contexts/app-context.tsx deleted file mode 100644 index a97820d9e26fa6197ef583ce88aba66a3bc10082..0000000000000000000000000000000000000000 --- a/components/contexts/app-context.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -"use client"; - -import { useUser } from "@/hooks/useUser"; -import { usePathname, useRouter } from "next/navigation"; -import { useMount } from "react-use"; -import { UserContext } from "@/components/contexts/user-context"; -import { User } from "@/types"; -import { toast } from "sonner"; -import { useBroadcastChannel } from "@/lib/useBroadcastChannel"; - -export default function AppContext({ - children, - me: initialData, -}: { - children: React.ReactNode; - me?: { - user: User | null; - errCode: number | null; - }; -}) { - const { loginFromCode, user, logout, loading, errCode } = - useUser(initialData); - const pathname = usePathname(); - const router = useRouter(); - - useMount(() => { - if (!initialData?.user && !user) { - if ([401, 403].includes(errCode as number)) { - logout(); - } else if (pathname.includes("/spaces")) { - if (errCode) { - toast.error("An error occured while trying to log in"); - } - // If we did not manage to log in (probs because api is down), we simply redirect to the home page - router.push("/"); - } - } - }); - - const events: any = {}; - - useBroadcastChannel("auth", (message) => { - if (pathname.includes("/auth/callback")) return; - - if (!message.code) return; - if (message.type === "user-oauth" && message?.code && !events.code) { - loginFromCode(message.code); - } - }); - - return ( - - {children} - - ); -} diff --git a/components/contexts/user-context.tsx b/components/contexts/user-context.tsx deleted file mode 100644 index 8a3391744618bfcfc979401cdee76051c70fee8f..0000000000000000000000000000000000000000 --- a/components/contexts/user-context.tsx +++ /dev/null @@ -1,8 +0,0 @@ -"use client"; - -import { createContext } from "react"; -import { User } from "@/types"; - -export const UserContext = createContext({ - user: undefined as User | undefined, -}); diff --git a/components/editor/ask-ai/follow-up-tooltip.tsx b/components/editor/ask-ai/follow-up-tooltip.tsx deleted file mode 100644 index 5ebb4a29de5de5cca175eb6795f1d069be6ba02b..0000000000000000000000000000000000000000 --- a/components/editor/ask-ai/follow-up-tooltip.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Info } from "lucide-react"; - -export const FollowUpTooltip = () => { - return ( - - - - - -
-

- ⚡ Faster, Smarter Updates -

-
-
-

- Using the Diff-Patch system, allow DeepSite to intelligently update - your project without rewritting the entire codebase. -

-

- This means faster updates, less data usage, and a more efficient - development process. -

-
-
-
- ); -}; diff --git a/components/editor/ask-ai/index.tsx b/components/editor/ask-ai/index.tsx deleted file mode 100644 index 2076173a327338cdc34a3d8c66c22cc3e72bc408..0000000000000000000000000000000000000000 --- a/components/editor/ask-ai/index.tsx +++ /dev/null @@ -1,500 +0,0 @@ -"use client"; -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { useState, useMemo, useRef } from "react"; -import classNames from "classnames"; -import { toast } from "sonner"; -import { useLocalStorage, useUpdateEffect } from "react-use"; -import { ArrowUp, ChevronDown, Crosshair } from "lucide-react"; -import { FaStopCircle } from "react-icons/fa"; - -import ProModal from "@/components/pro-modal"; -import { Button } from "@/components/ui/button"; -import { MODELS } from "@/lib/providers"; -import { HtmlHistory, Page, Project } from "@/types"; -// import { InviteFriends } from "@/components/invite-friends"; -import { Settings } from "@/components/editor/ask-ai/settings"; -import { LoginModal } from "@/components/login-modal"; -import { ReImagine } from "@/components/editor/ask-ai/re-imagine"; -import Loading from "@/components/loading"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Tooltip, TooltipTrigger } from "@/components/ui/tooltip"; -import { TooltipContent } from "@radix-ui/react-tooltip"; -import { SelectedHtmlElement } from "./selected-html-element"; -import { FollowUpTooltip } from "./follow-up-tooltip"; -import { isTheSameHtml } from "@/lib/compare-html-diff"; -import { useCallAi } from "@/hooks/useCallAi"; -import { SelectedFiles } from "./selected-files"; -import { Uploader } from "./uploader"; - -export function AskAI({ - isNew, - project, - images, - currentPage, - previousPrompts, - onScrollToBottom, - isAiWorking, - setisAiWorking, - isEditableModeEnabled = false, - pages, - htmlHistory, - selectedElement, - setSelectedElement, - selectedFiles, - setSelectedFiles, - setIsEditableModeEnabled, - onNewPrompt, - onSuccess, - setPages, - setCurrentPage, -}: { - project?: Project | null; - currentPage: Page; - images?: string[]; - pages: Page[]; - onScrollToBottom: () => void; - previousPrompts: string[]; - isAiWorking: boolean; - onNewPrompt: (prompt: string) => void; - htmlHistory?: HtmlHistory[]; - setisAiWorking: React.Dispatch>; - isNew?: boolean; - onSuccess: (page: Page[], p: string, n?: number[][]) => void; - isEditableModeEnabled: boolean; - setIsEditableModeEnabled: React.Dispatch>; - selectedElement?: HTMLElement | null; - setSelectedElement: React.Dispatch>; - selectedFiles: string[]; - setSelectedFiles: React.Dispatch>; - setPages: React.Dispatch>; - setCurrentPage: React.Dispatch>; -}) { - const refThink = useRef(null); - - const [open, setOpen] = useState(false); - const [prompt, setPrompt] = useState(""); - const [provider, setProvider] = useLocalStorage("provider", "auto"); - const [model, setModel] = useLocalStorage("model", MODELS[0].value); - const [openProvider, setOpenProvider] = useState(false); - const [providerError, setProviderError] = useState(""); - const [openProModal, setOpenProModal] = useState(false); - const [openThink, setOpenThink] = useState(false); - const [isThinking, setIsThinking] = useState(true); - const [think, setThink] = useState(""); - const [isFollowUp, setIsFollowUp] = useState(true); - const [isUploading, setIsUploading] = useState(false); - const [files, setFiles] = useState(images ?? []); - - const { - callAiNewProject, - callAiFollowUp, - callAiNewPage, - stopController, - audio: hookAudio, - } = useCallAi({ - onNewPrompt, - onSuccess, - onScrollToBottom, - setPages, - setCurrentPage, - currentPage, - pages, - isAiWorking, - setisAiWorking, - }); - - const selectedModel = useMemo(() => { - return MODELS.find((m: { value: string }) => m.value === model); - }, [model]); - - const callAi = async (redesignMarkdown?: string) => { - if (isAiWorking) return; - if (!redesignMarkdown && !prompt.trim()) return; - - if (isFollowUp && !redesignMarkdown && !isSameHtml) { - // Use follow-up function for existing projects - const selectedElementHtml = selectedElement - ? selectedElement.outerHTML - : ""; - - const result = await callAiFollowUp( - prompt, - model, - provider, - previousPrompts, - selectedElementHtml, - selectedFiles - ); - - if (result?.error) { - handleError(result.error, result.message); - return; - } - - if (result?.success) { - setPrompt(""); - } - } else if (isFollowUp && pages.length > 1 && isSameHtml) { - const result = await callAiNewPage( - prompt, - model, - provider, - currentPage.path, - [ - ...(previousPrompts ?? []), - ...(htmlHistory?.map((h) => h.prompt) ?? []), - ] - ); - if (result?.error) { - handleError(result.error, result.message); - return; - } - - if (result?.success) { - setPrompt(""); - } - } else { - const result = await callAiNewProject( - prompt, - model, - provider, - redesignMarkdown, - handleThink, - () => { - setIsThinking(false); - } - ); - - if (result?.error) { - handleError(result.error, result.message); - return; - } - - if (result?.success) { - setPrompt(""); - if (selectedModel?.isThinker) { - setModel(MODELS[0].value); - } - } - } - }; - - const handleThink = (think: string) => { - setThink(think); - setIsThinking(true); - setOpenThink(true); - }; - - const handleError = (error: string, message?: string) => { - switch (error) { - case "login_required": - setOpen(true); - break; - case "provider_required": - setOpenProvider(true); - setProviderError(message || ""); - break; - case "pro_required": - setOpenProModal(true); - break; - case "api_error": - toast.error(message || "An error occurred"); - break; - case "network_error": - toast.error(message || "Network error occurred"); - break; - default: - toast.error("An unexpected error occurred"); - } - }; - - useUpdateEffect(() => { - if (refThink.current) { - refThink.current.scrollTop = refThink.current.scrollHeight; - } - }, [think]); - - useUpdateEffect(() => { - if (!isThinking) { - setOpenThink(false); - } - }, [isThinking]); - - const isSameHtml = useMemo(() => { - return isTheSameHtml(currentPage.html); - }, [currentPage.html]); - - return ( -
-
- {think && ( -
-
{ - setOpenThink(!openThink); - }} - > -

- {isThinking ? "DeepSite is thinking..." : "DeepSite's plan"} -

- -
-
-

- {think} -

-
-
- )} - - setSelectedFiles((prev) => prev.filter((f) => f !== file)) - } - /> - {selectedElement && ( -
- setSelectedElement(null)} - /> -
- )} -
- {(isAiWorking || isUploading) && ( -
-
- -

- {isUploading ? ( - "Uploading images..." - ) : isAiWorking && !isSameHtml ? ( - "AI is working..." - ) : ( - - {[ - "D", - "e", - "e", - "p", - "S", - "i", - "t", - "e", - " ", - "i", - "s", - " ", - "T", - "h", - "i", - "n", - "k", - "i", - "n", - "g", - ".", - ".", - ".", - " ", - "W", - "a", - "i", - "t", - " ", - "a", - " ", - "m", - "o", - "m", - "e", - "n", - "t", - ".", - ".", - ".", - ].map((char, index) => ( - - {char === " " ? "\u00A0" : char} - - ))} - - )} -

-
- {isAiWorking && ( -
- - Stop generation -
- )} -
- )} -