diff --git a/.env.example b/.env.example
index d7419a983b3e8d6606db990addea8d78dd660692..406c10849e0ff8b05e8dcd6de5a88e3ea33ea7e5 100644
--- a/.env.example
+++ b/.env.example
@@ -1,4 +1,11 @@
-AUTH_HUGGINGFACE_ID=
-AUTH_HUGGINGFACE_SECRET=
-NEXTAUTH_URL=http://localhost:3001
-AUTH_SECRET=
\ No newline at end of file
+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/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml
deleted file mode 100644
index 88d7e85dd2f708097a42154a349c9cb217479120..0000000000000000000000000000000000000000
--- a/.github/workflows/deploy-prod.yml
+++ /dev/null
@@ -1,77 +0,0 @@
-name: Deploy to k8s
-on:
- # run this workflow manually from the Actions tab
- workflow_dispatch:
-
-jobs:
- build-and-publish:
- runs-on:
- group: cpu-high
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Login to Registry
- uses: docker/login-action@v3
- with:
- registry: registry.internal.huggingface.tech
- username: ${{ secrets.DOCKER_INTERNAL_USERNAME }}
- password: ${{ secrets.DOCKER_INTERNAL_PASSWORD }}
-
- - name: Docker metadata
- id: meta
- uses: docker/metadata-action@v5
- with:
- images: |
- registry.internal.huggingface.tech/deepsite/deepsite
- tags: |
- type=raw,value=latest,enable={{is_default_branch}}
- type=sha,enable=true,prefix=sha-,format=short,sha-len=8
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Inject slug/short variables
- uses: rlespinasse/github-slug-action@v4
-
- - name: Build and Publish image
- uses: docker/build-push-action@v5
- with:
- context: .
- file: Dockerfile
- push: ${{ github.event_name != 'pull_request' }}
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- platforms: linux/amd64
- cache-to: type=gha,mode=max,scope=amd64
- cache-from: type=gha,scope=amd64
- provenance: false
-
- deploy:
- name: Deploy on prod
- runs-on: ubuntu-latest
- needs: ["build-and-publish"]
- steps:
- - name: Inject slug/short variables
- uses: rlespinasse/github-slug-action@v4
-
- - name: Gen values
- run: |
- VALUES=$(cat <<-END
- image:
- tag: "sha-${{ env.GITHUB_SHA_SHORT }}"
- END
- )
- echo "VALUES=$(echo "$VALUES" | yq -o=json | jq tostring)" >> $GITHUB_ENV
-
- - name: Deploy on infra-deployments
- uses: the-actions-org/workflow-dispatch@v2
- with:
- workflow: Update application single value
- repo: huggingface/infra-deployments
- wait-for-completion: true
- wait-for-completion-interval: 10s
- display-workflow-run-url-interval: 10s
- ref: refs/heads/main
- token: ${{ secrets.GIT_TOKEN_INFRA_DEPLOYMENT }}
- inputs: '{"path": "hub/deepsite/deepsite.yaml", "value": ${{ env.VALUES }}, "url": "${{ github.event.head_commit.url }}"}'
diff --git a/.gitignore b/.gitignore
index b8b89cbb382fac7dea7fdeb461bd43beb9937c49..38d4117ba770f7518a4bc651b80446a4f2f218d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,47 +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*
+pnpm-debug.log*
+lerna-debug.log*
-# env files (can opt-in for committing if needed)
-.env
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
+node_modules
+dist
+dist-ssr
+*.local
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
.idea
-
-# binary assets (hosted on CDN)
-assets/assistant.jpg
-.gitattributes
\ No newline at end of file
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.env
+.aider*
diff --git a/Dockerfile b/Dockerfile
index a2b0759c0a612c3be02d8c1eb3021252cdd4a7f8..8003b5cb2da5b411b794263c7d70bae1f6866ae0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,22 +1,22 @@
-FROM node:20-alpine
+# Dockerfile
+# Use an official Node.js runtime as the base image
+FROM node:22.1.0
USER root
-# Install pnpm
-RUN corepack enable && corepack prepare pnpm@latest --activate
-
+RUN apt-get update
USER 1000
WORKDIR /usr/src/app
-# Copy package.json and pnpm-lock.yaml to the container
-COPY --chown=1000 package.json pnpm-lock.yaml ./
+# Copy package.json and package-lock.json to the container
+COPY --chown=1000 package.json package-lock.json ./
# Copy the rest of the application files to the container
COPY --chown=1000 . .
-RUN pnpm install
-RUN pnpm run build
+RUN npm install
+RUN npm run build
# Expose the application port (assuming your app runs on port 3000)
-EXPOSE 3001
+EXPOSE 5173
# Start the application
-CMD ["pnpm", "start"]
\ No newline at end of file
+CMD ["npm", "start"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 6b13fd8b95aeb699f979cbc2cbb8d7495aeacdf5..606f2489d425f097ed31a2c0be520bff4b1c1201 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,19 @@
---
-title: DeepSite v4
+title: DeepSite
emoji: 🐳
colorFrom: blue
colorTo: blue
sdk: docker
pinned: true
-app_port: 3001
+app_port: 5173
license: mit
-failure_strategy: rollback
-short_description: Generate any application by Vibe Coding it
+short_description: Generate any application with DeepSeek
models:
- deepseek-ai/DeepSeek-V3-0324
- - deepseek-ai/DeepSeek-V3.2
- - Qwen/Qwen3-Coder-30B-A3B-Instruct
- - moonshotai/Kimi-K2-Instruct-0905
- - zai-org/GLM-4.7
- - MiniMaxAI/MiniMax-M2.1
---
# 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.
-DeepSite is a Vibe Coding Platform 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)
\ No newline at end of file
diff --git a/actions/mentions.ts b/actions/mentions.ts
deleted file mode 100644
index c6b7dabfbaa6f0efe0d492dfe5470353b0139bae..0000000000000000000000000000000000000000
--- a/actions/mentions.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-"use client";
-
-import { File } from "@/lib/type";
-
-export const searchMentions = async (query: string) => {
- const promises = [searchModels(query), searchDatasets(query)];
- const results = await Promise.all(promises);
- return { models: results[0], datasets: results[1] };
-};
-
-const searchModels = async (query: string) => {
- const response = await fetch(
- `https://huggingface.co/api/quicksearch?q=${query}&type=model&limit=3`
- );
- const data = await response.json();
- return data?.models ?? [];
-};
-
-const searchDatasets = async (query: string) => {
- const response = await fetch(
- `https://huggingface.co/api/quicksearch?q=${query}&type=dataset&limit=3`
- );
- const data = await response.json();
- return data?.datasets ?? [];
-};
-
-export const searchFilesMentions = async (query: string, files: File[]) => {
- if (!query) return files;
- const lowerQuery = query.toLowerCase();
- return files.filter((file) => file.path.toLowerCase().includes(lowerQuery));
-};
diff --git a/actions/projects.ts b/actions/projects.ts
deleted file mode 100644
index d6f685b63290792fc597438a53eab0a14d6bbf8e..0000000000000000000000000000000000000000
--- a/actions/projects.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-"use server";
-import {
- downloadFile,
- listCommits,
- listFiles,
- listSpaces,
- RepoDesignation,
- SpaceEntry,
- spaceInfo,
-} from "@huggingface/hub";
-
-import { auth } from "@/lib/auth";
-import { Commit, File } from "@/lib/type";
-
-export interface ProjectWithCommits extends SpaceEntry {
- commits?: Commit[];
- medias?: string[];
-}
-
-const IGNORED_PATHS = ["README.md", ".gitignore", ".gitattributes"];
-const IGNORED_FORMATS = [
- ".png",
- ".jpg",
- ".jpeg",
- ".gif",
- ".svg",
- ".webp",
- ".mp4",
- ".mp3",
- ".wav",
-];
-
-export const getProjects = async () => {
- const projects: SpaceEntry[] = [];
- const session = await auth();
- if (!session?.user) {
- return projects;
- }
- const token = session.accessToken;
- for await (const space of listSpaces({
- accessToken: token,
- additionalFields: ["author", "cardData"],
- search: {
- owner: session.user.username,
- },
- })) {
- if (
- space.sdk === "static" &&
- Array.isArray((space.cardData as { tags?: string[] })?.tags) &&
- (space.cardData as { tags?: string[] })?.tags?.some((tag) =>
- tag.includes("deepsite")
- )
- ) {
- projects.push(space);
- }
- }
- return projects;
-};
-export const getProject = async (id: string, commitId?: string) => {
- const session = await auth();
- if (!session?.user) {
- return null;
- }
- const token = session.accessToken;
- try {
- const project: ProjectWithCommits | null = await spaceInfo({
- name: id,
- accessToken: token,
- additionalFields: ["author", "cardData"],
- });
- const repo: RepoDesignation = {
- type: "space",
- name: id,
- };
- const files: File[] = [];
- const medias: string[] = [];
- const params = { repo, accessToken: token };
- if (commitId) {
- Object.assign(params, { revision: commitId });
- }
- for await (const fileInfo of listFiles(params)) {
- if (IGNORED_PATHS.includes(fileInfo.path)) continue;
- if (IGNORED_FORMATS.some((format) => fileInfo.path.endsWith(format))) {
- medias.push(
- `https://huggingface.co/spaces/${id}/resolve/main/${fileInfo.path}`
- );
- continue;
- }
-
- if (fileInfo.type === "directory") {
- for await (const subFile of listFiles({
- repo,
- accessToken: token,
- path: fileInfo.path,
- })) {
- if (IGNORED_FORMATS.some((format) => subFile.path.endsWith(format))) {
- medias.push(
- `https://huggingface.co/spaces/${id}/resolve/main/${subFile.path}`
- );
- }
- const blob = await downloadFile({
- repo,
- accessToken: token,
- path: subFile.path,
- raw: true,
- ...(commitId ? { revision: commitId } : {}),
- }).catch((_) => {
- return null;
- });
- if (!blob) {
- continue;
- }
- const html = await blob?.text();
- if (!html) {
- continue;
- }
- files[subFile.path === "index.html" ? "unshift" : "push"]({
- path: subFile.path,
- content: html,
- });
- }
- } else {
- const blob = await downloadFile({
- repo,
- accessToken: token,
- path: fileInfo.path,
- raw: true,
- ...(commitId ? { revision: commitId } : {}),
- }).catch((_) => {
- return null;
- });
- if (!blob) {
- continue;
- }
- const html = await blob?.text();
- if (!html) {
- continue;
- }
- files[fileInfo.path === "index.html" ? "unshift" : "push"]({
- path: fileInfo.path,
- content: html,
- });
- }
- }
- const commits: Commit[] = [];
- const commitIterator = listCommits({ repo, accessToken: token });
- for await (const commit of commitIterator) {
- if (
- commit.title?.toLowerCase() === "initial commit" ||
- commit.title
- ?.toLowerCase()
- ?.includes("upload media files through deepsite")
- )
- continue;
- commits.push({
- title: commit.title,
- oid: commit.oid,
- date: commit.date,
- });
- if (commits.length >= 20) {
- break;
- }
- }
-
- project.commits = commits;
- project.medias = medias;
-
- return { project, files };
- } catch (error) {
- return {
- project: null,
- files: [],
- };
- }
-};
diff --git a/app/(public)/layout.tsx b/app/(public)/layout.tsx
deleted file mode 100644
index 0eb2c8fbb85b745d5be01c9e79fa0ebc93a71d00..0000000000000000000000000000000000000000
--- a/app/(public)/layout.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Navigation } from "@/components/public/navigation";
-
-export default 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 86970b7a3dab7e60cce3bbb68c747b525baba45c..0000000000000000000000000000000000000000
--- a/app/(public)/page.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { AnimatedDotsBackground } from "@/components/public/animated-dots-background";
-import { HeroHeader } from "@/components/public/hero-header";
-import { UserProjects } from "@/components/projects/user-projects";
-import { AskAiLanding } from "@/components/ask-ai/ask-ai-landing";
-import { Bento } from "@/components/public/bento";
-
-export const dynamic = "force-dynamic";
-
-export default async function Homepage() {
- return (
- <>
-
-
-
- >
- );
-}
diff --git a/app/(public)/signin/page.tsx b/app/(public)/signin/page.tsx
deleted file mode 100644
index 3200d0755372c79be57b9204331695cd8f0499ac..0000000000000000000000000000000000000000
--- a/app/(public)/signin/page.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { LoginButtons } from "@/components/login/login-buttons";
-
-export default async function SignInPage({
- searchParams,
-}: {
- searchParams: Promise<{ callbackUrl: string }>;
-}) {
- const { callbackUrl } = await searchParams;
- console.log(callbackUrl);
- return (
-
-
-
You shall not pass 🧙
-
- You can't access this resource without being signed in.
-
-
-
-
- );
-}
diff --git a/app/[owner]/[repoId]/page.tsx b/app/[owner]/[repoId]/page.tsx
deleted file mode 100644
index 5082396059ae126dbc2be8e0daf30d1ff014f922..0000000000000000000000000000000000000000
--- a/app/[owner]/[repoId]/page.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { getProject } from "@/actions/projects";
-import { AppEditor } from "@/components/editor";
-import { auth } from "@/lib/auth";
-import { notFound, redirect } from "next/navigation";
-
-export default async function ProjectPage({
- params,
- searchParams,
-}: {
- params: Promise<{ owner: string; repoId: string }>;
- searchParams: Promise<{ commit?: string }>;
-}) {
- const session = await auth();
-
- const { owner, repoId } = await params;
- const { commit } = await searchParams;
- if (!session) {
- redirect(
- `/api/auth/signin?callbackUrl=/${owner}/${repoId}${
- commit ? `?commit=${commit}` : ""
- }`
- );
- }
- const datas = await getProject(`${owner}/${repoId}`, commit);
- if (!datas?.project) {
- return notFound();
- }
- return (
-
- );
-}
diff --git a/app/api/ask/route.ts b/app/api/ask/route.ts
deleted file mode 100644
index 293e8244308cc1191e32b3ef6b30e93ccb963fcf..0000000000000000000000000000000000000000
--- a/app/api/ask/route.ts
+++ /dev/null
@@ -1,183 +0,0 @@
-import { NextResponse } from "next/server";
-import { InferenceClient } from "@huggingface/inference";
-
-import { FOLLOW_UP_SYSTEM_PROMPT, INITIAL_SYSTEM_PROMPT } from "@/lib/prompts";
-import { auth } from "@/lib/auth";
-import { File, Message } from "@/lib/type";
-import { DEFAULT_MODEL, MODELS } from "@/lib/providers";
-
-export async function POST(request: Request) {
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const body = await request.json();
- const {
- prompt,
- previousMessages = [],
- files = [],
- provider,
- model,
- redesignMd,
- medias,
- } = body;
-
- if (!prompt) {
- return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
- }
- if (!model || !MODELS.find((m: (typeof MODELS)[0]) => m.value === model)) {
- return NextResponse.json({ error: "Model is required" }, { status: 400 });
- }
-
- const client = new InferenceClient(token);
-
- 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 hasRetried = false;
- let currentModel = model;
-
- const tryGeneration = async (): Promise => {
- try {
- const chatCompletion = client.chatCompletionStream({
- model: currentModel + (provider !== "auto" ? `:${provider}` : ""),
- messages: [
- {
- role: "system",
- content:
- files.length > 0
- ? FOLLOW_UP_SYSTEM_PROMPT
- : INITIAL_SYSTEM_PROMPT,
- },
- ...previousMessages.map((message: Message) => ({
- role: message.role,
- content: message.content,
- })),
- ...(files?.length > 0
- ? [
- {
- role: "user",
- content: `Here are the files that the user has provider:${files
- .map(
- (file: File) =>
- `File: ${file.path}\nContent: ${file.content}`
- )
- .join("\n")}\n\n${prompt}`,
- },
- ]
- : []),
- {
- role: "user",
- content: `${
- redesignMd?.url &&
- `Redesign the following website ${redesignMd.url}, try to use the same images and content, but you can still improve it if needed. Do the best version possibile. Here is the markdown:\n ${redesignMd.md} \n\n`
- }${prompt} ${
- medias && medias.length > 0
- ? `\nHere is the list of my media files: ${medias.join(
- ", "
- )}\n`
- : ""
- }`,
- }
- ],
- stream: true,
- max_tokens: 16_000,
- });
- 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));
- }
- }
-
- await writer.close();
- } catch (error) {
- const errorMessage =
- error instanceof Error
- ? error.message
- : "An error occurred while processing your request";
-
- if (
- !hasRetried &&
- errorMessage?.includes(
- "Failed to perform inference: Model not found"
- )
- ) {
- hasRetried = true;
- if (model === DEFAULT_MODEL) {
- const availableFallbackModels = MODELS.filter(
- (m) => m.value !== model
- );
- const randomIndex = Math.floor(
- Math.random() * availableFallbackModels.length
- );
- currentModel = availableFallbackModels[randomIndex];
- } else {
- currentModel = DEFAULT_MODEL;
- }
- const switchMessage = `\n\n_Note: The selected model was not available. Switched to \`${currentModel}\`._\n\n`;
- await writer.write(encoder.encode(switchMessage));
-
- return tryGeneration();
- }
-
- try {
- let errorPayload = "";
- if (
- errorMessage?.includes("exceeded your monthly included credits") ||
- errorMessage?.includes("reached the free monthly usage limit")
- ) {
- errorPayload = JSON.stringify({
- messageError: errorMessage,
- showProMessage: true,
- isError: true,
- });
- } else {
- errorPayload = JSON.stringify({
- messageError: errorMessage,
- isError: true,
- });
- }
- await writer.write(encoder.encode(`\n\n__ERROR__:${errorPayload}`));
- await writer.close();
- } catch (closeError) {
- console.error("Failed to send error message:", closeError);
- try {
- await writer.abort(error);
- } catch (abortError) {
- console.error("Failed to abort writer:", abortError);
- }
- }
- }
- };
-
- await tryGeneration();
- })();
-
- return response;
- } catch (error) {
- return NextResponse.json(
- {
- error: error instanceof Error ? error.message : "Internal Server Error",
- },
- { status: 500 }
- );
- }
-}
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
deleted file mode 100644
index 7b38c1bb45e38527a2c595c07df5e70d38ada7d9..0000000000000000000000000000000000000000
--- a/app/api/auth/[...nextauth]/route.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import NextAuth from "next-auth";
-import { authOptions } from "@/lib/auth";
-
-const handler = NextAuth(authOptions);
-
-export { handler as GET, handler as POST };
diff --git a/app/api/healthcheck/route.ts b/app/api/healthcheck/route.ts
deleted file mode 100644
index 35adb415d0f577a7a78dbc338bd6450e78cc9ec3..0000000000000000000000000000000000000000
--- a/app/api/healthcheck/route.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { NextResponse } from "next/server";
-
-export async function GET() {
- return NextResponse.json({ status: "ok" }, { status: 200 });
-}
diff --git a/app/api/projects/[repoId]/[commitId]/route.ts b/app/api/projects/[repoId]/[commitId]/route.ts
deleted file mode 100644
index 0362d6c43ec226bf97837f2f88196ad43eb056f2..0000000000000000000000000000000000000000
--- a/app/api/projects/[repoId]/[commitId]/route.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { auth } from "@/lib/auth";
-import { createBranch, RepoDesignation } from "@huggingface/hub";
-import { format } from "date-fns";
-import { NextResponse } from "next/server";
-
-export async function POST(
- request: Request,
- { params }: { params: Promise<{ repoId: string; commitId: string }> }
-) {
- const { repoId, commitId }: { repoId: string; commitId: string } =
- await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- const commitTitle = `🔖 ${format(new Date(), "dd/MM")} - ${format(
- new Date(),
- "HH:mm"
- )} - Set commit ${commitId} as default.`;
-
- await fetch(
- `https://huggingface.co/api/spaces/${session.user?.username}/${repoId}/branch/main`,
- {
- method: "POST",
- headers: {
- Authorization: `Bearer ${token}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- startingPoint: commitId,
- overwrite: true,
- }),
- }
- ).catch((error) => {
- return NextResponse.json(
- { error: error ?? "Failed to create branch" },
- { status: 500 }
- );
- });
-
- return NextResponse.json({ success: true }, { status: 200 });
-}
diff --git a/app/api/projects/[repoId]/download/route.ts b/app/api/projects/[repoId]/download/route.ts
deleted file mode 100644
index 43e8f6638d5eb2aca8ba3069d696b05056d96633..0000000000000000000000000000000000000000
--- a/app/api/projects/[repoId]/download/route.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { auth } from "@/lib/auth";
-import { downloadFile, listFiles, RepoDesignation } from "@huggingface/hub";
-import { NextResponse } from "next/server";
-import JSZip from "jszip";
-
-export async function GET(
- request: Request,
- { params }: { params: Promise<{ repoId: string }> }
-) {
- const { repoId }: { repoId: string } = await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- try {
- const zip = new JSZip();
- for await (const fileInfo of listFiles({
- repo,
- accessToken: token as string,
- recursive: true,
- })) {
- if (fileInfo.type === "directory" || fileInfo.path.startsWith(".")) {
- continue;
- }
-
- try {
- const blob = await downloadFile({
- repo,
- accessToken: token as string,
- path: fileInfo.path,
- raw: true
- }).catch((error) => {
- return null;
- });
- if (!blob) {
- continue;
- }
-
- if (blob) {
- const arrayBuffer = await blob.arrayBuffer();
- zip.file(fileInfo.path, arrayBuffer);
- }
- } catch (error) {
- console.error(`Error downloading file ${fileInfo.path}:`, error);
- }
- }
-
- const zipBlob = await zip.generateAsync({
- type: "blob",
- compression: "DEFLATE",
- compressionOptions: {
- level: 6
- }
- });
-
- const projectName = `${session.user?.username}-${repoId}`.replace(/[^a-zA-Z0-9-_]/g, '_');
- const filename = `${projectName}.zip`;
-
- return new NextResponse(zipBlob, {
- headers: {
- "Content-Type": "application/zip",
- "Content-Disposition": `attachment; filename="${filename}"`,
- "Content-Length": zipBlob.size.toString(),
- },
- });
- } catch (error) {
- console.error("Error downloading project:", error);
- return NextResponse.json({ error: "Failed to download project" }, { status: 500 });
- }
-}
\ No newline at end of file
diff --git a/app/api/projects/[repoId]/medias/route.ts b/app/api/projects/[repoId]/medias/route.ts
deleted file mode 100644
index 2040623536ab6775152177a317e0b11dc6cae3a2..0000000000000000000000000000000000000000
--- a/app/api/projects/[repoId]/medias/route.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { auth } from "@/lib/auth";
-import { RepoDesignation, uploadFiles } from "@huggingface/hub";
-import { NextResponse } from "next/server";
-
-export async function POST(
- request: Request,
- { params }: { params: Promise<{ repoId: string }> }
-) {
- const { repoId }: { repoId: string } = await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- const formData = await request.formData();
- const newMedias = formData.getAll("images") as File[];
-
- const filesToUpload: File[] = [];
-
- if (!newMedias || newMedias.length === 0) {
- return NextResponse.json(
- {
- ok: false,
- error: "At least one media file is required under the 'images' key",
- },
- { status: 400 }
- );
- }
-
- try {
- for (const media of newMedias) {
- const isImage = media.type.startsWith("image/");
- const isVideo = media.type.startsWith("video/");
- const isAudio = media.type.startsWith("audio/");
-
- const folderPath = isImage
- ? "images/"
- : isVideo
- ? "videos/"
- : isAudio
- ? "audios/"
- : null;
-
- if (!folderPath) {
- return NextResponse.json(
- { ok: false, error: "Unsupported media type: " + media.type },
- { status: 400 }
- );
- }
-
- const mediaName = `${folderPath}${media.name}`;
- const processedFile = new File([media], mediaName, { type: media.type });
- filesToUpload.push(processedFile);
- }
-
- await uploadFiles({
- repo,
- files: filesToUpload,
- accessToken: token,
- commitTitle: `📁 Upload media files through DeepSite`,
- });
-
- return NextResponse.json(
- {
- success: true,
- medias: filesToUpload.map(
- (file) =>
- `https://huggingface.co/spaces/${session.user?.username}/${repoId}/resolve/main/${file.name}`
- ),
- },
- { status: 200 }
- );
- } catch (error) {
- return NextResponse.json(
- { ok: false, error: error ?? "Failed to upload media files" },
- { status: 500 }
- );
- }
-
- return NextResponse.json({ success: true }, { status: 200 });
-}
diff --git a/app/api/projects/[repoId]/rename/route.ts b/app/api/projects/[repoId]/rename/route.ts
deleted file mode 100644
index 1d3a00b676effdc7d9106c02e6d7ec40384026c9..0000000000000000000000000000000000000000
--- a/app/api/projects/[repoId]/rename/route.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { auth } from "@/lib/auth";
-import { downloadFile, RepoDesignation, uploadFile } from "@huggingface/hub";
-import { format } from "date-fns";
-import { NextResponse } from "next/server";
-
-export async function PUT(
- request: Request,
- { params }: { params: Promise<{ repoId: string }> }
-) {
- const { repoId }: { repoId: string } = await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const body = await request.json();
- const { newTitle } = body;
-
- if (!newTitle) {
- return NextResponse.json(
- { error: "newTitle is required" },
- { status: 400 }
- );
- }
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- const blob = await downloadFile({
- repo,
- accessToken: token,
- path: "README.md",
- raw: true,
- }).catch((_) => {
- return null;
- });
-
- if (!blob) {
- return NextResponse.json(
- { error: "Could not fetch README.md" },
- { status: 500 }
- );
- }
-
- const readmeFile = await blob?.text();
- if (!readmeFile) {
- return NextResponse.json(
- { error: "Could not read README.md content" },
- { status: 500 }
- );
- }
-
- // Escape YAML values to prevent injection attacks
- const escapeYamlValue = (value: string): string => {
- if (/[:|>]|^[-*#]|^\s|['"]/.test(value) || value.includes("\n")) {
- return `"${value.replace(/"/g, '\\"')}"`;
- }
- return value;
- };
-
- // Escape commit message to prevent injection
- const escapeCommitMessage = (message: string): string => {
- return message.replace(/[\r\n]/g, " ").slice(0, 200);
- };
-
- const updatedReadmeFile = readmeFile.replace(
- /^title:\s*(.*)$/m,
- `title: ${escapeYamlValue(newTitle)}`
- );
-
- await uploadFile({
- repo,
- accessToken: token,
- file: new File([updatedReadmeFile], "README.md", { type: "text/markdown" }),
- commitTitle: escapeCommitMessage(
- `🐳 ${format(new Date(), "dd/MM")} - ${format(
- new Date(),
- "HH:mm"
- )} - Rename project to "${newTitle}"`
- ),
- });
-
- return NextResponse.json(
- {
- success: true,
- },
- { status: 200 }
- );
-}
diff --git a/app/api/projects/[repoId]/route.ts b/app/api/projects/[repoId]/route.ts
deleted file mode 100644
index e6daee03652b083d7f1d852f7367d7f55fd3f390..0000000000000000000000000000000000000000
--- a/app/api/projects/[repoId]/route.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { auth } from "@/lib/auth";
-import { RepoDesignation, deleteRepo, uploadFiles } from "@huggingface/hub";
-import { format } from "date-fns";
-import { NextResponse } from "next/server";
-
-export async function PUT(
- request: Request,
- { params }: { params: Promise<{ repoId: string }> }
-) {
- const { repoId }: { repoId: string } = await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const body = await request.json();
- const { files, prompt, isManualChanges } = body;
-
- if (!files) {
- return NextResponse.json({ error: "Files are required" }, { status: 400 });
- }
-
- if (!prompt) {
- return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
- }
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- const filesToUpload: File[] = [];
- for (const file of files) {
- let mimeType = "text/x-python";
- if (file.path.endsWith(".txt")) {
- mimeType = "text/plain";
- } else if (file.path.endsWith(".md")) {
- mimeType = "text/markdown";
- } else if (file.path.endsWith(".json")) {
- mimeType = "application/json";
- }
- filesToUpload.push(new File([file.content], file.path, { type: mimeType }));
- }
- // Escape commit title to prevent injection
- const escapeCommitTitle = (title: string): string => {
- return title.replace(/[\r\n]/g, " ").slice(0, 200);
- };
-
- const baseTitle = isManualChanges
- ? ""
- : `🐳 ${format(new Date(), "dd/MM")} - ${format(new Date(), "HH:mm")} - `;
- const commitTitle = escapeCommitTitle(
- baseTitle + (prompt ?? "Follow-up DeepSite commit")
- );
- const response = await uploadFiles({
- repo,
- files: filesToUpload,
- accessToken: token,
- commitTitle,
- });
-
- return NextResponse.json(
- {
- success: true,
- commit: {
- oid: response.commit,
- title: commitTitle,
- date: new Date(),
- },
- },
- { status: 200 }
- );
-}
-
-export async function DELETE(
- request: Request,
- { params }: { params: Promise<{ repoId: string }> }
-) {
- const { repoId }: { repoId: string } = await params;
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + repoId,
- };
-
- try {
- await deleteRepo({
- repo,
- accessToken: token as string,
- });
-
- return NextResponse.json({ success: true }, { status: 200 });
- } catch (error) {
- const errMsg =
- error instanceof Error ? error.message : "Failed to delete project";
- return NextResponse.json({ error: errMsg }, { status: 500 });
- }
-}
diff --git a/app/api/projects/route.ts b/app/api/projects/route.ts
deleted file mode 100644
index b70c310b4167077d4d8bcd15423defb1d158e2dd..0000000000000000000000000000000000000000
--- a/app/api/projects/route.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-import { NextResponse } from "next/server";
-import { RepoDesignation, createRepo, uploadFiles } from "@huggingface/hub";
-
-import { auth } from "@/lib/auth";
-import {
- COLORS,
- EMOJIS_FOR_SPACE,
- injectDeepSiteBadge,
- isIndexPage,
-} from "@/lib/utils";
-
-// todo: catch error while publishing project, and return the error to the user
-// if space has been created, but can't push, try again or catch well the error and return the error to the user
-
-export async function POST(request: Request) {
- const session = await auth();
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
- const token = session.accessToken;
-
- const body = await request.json();
- const { projectTitle, files, prompt } = body;
-
- if (!files) {
- return NextResponse.json(
- { error: "Project title and files are required" },
- { status: 400 }
- );
- }
-
- const title =
- projectTitle || projectTitle !== "" ? projectTitle : "DeepSite Project";
-
- let formattedTitle = title
- .toLowerCase()
- .replace(/[^a-z0-9]+/g, "-")
- .split("-")
- .filter(Boolean)
- .join("-")
- .slice(0, 75);
-
- formattedTitle =
- formattedTitle + "-" + Math.random().toString(36).substring(2, 7);
-
- const repo: RepoDesignation = {
- type: "space",
- name: session.user?.username + "/" + formattedTitle,
- };
-
- // Escape YAML values to prevent injection attacks
- const escapeYamlValue = (value: string): string => {
- if (/[:|>]|^[-*#]|^\s|['"]/.test(value) || value.includes("\n")) {
- return `"${value.replace(/"/g, '\\"')}"`;
- }
- return value;
- };
-
- // Escape markdown headers to prevent injection
- const escapeMarkdownHeader = (value: string): string => {
- return value.replace(/^#+\s*/g, "").replace(/\n/g, " ");
- };
-
- const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)];
- const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)];
- const emoji =
- EMOJIS_FOR_SPACE[Math.floor(Math.random() * EMOJIS_FOR_SPACE.length)];
- const README = `---
-title: ${escapeYamlValue(projectTitle)}
-colorFrom: ${colorFrom}
-colorTo: ${colorTo}
-sdk: static
-emoji: ${emoji}
-tags:
- - deepsite-v4
----
-
-# ${escapeMarkdownHeader(title)}
-
-This project has been created with [DeepSite](https://deepsite.hf.co) AI Vibe Coding.
-`;
-
- const filesToUpload: File[] = [
- new File([README], "README.md", { type: "text/markdown" }),
- ];
- for (const file of files) {
- let mimeType = "text/html";
- if (file.path.endsWith(".css")) {
- mimeType = "text/css";
- } else if (file.path.endsWith(".js")) {
- mimeType = "text/javascript";
- }
- const content =
- mimeType === "text/html" && isIndexPage(file.path)
- ? injectDeepSiteBadge(file.content)
- : file.content;
-
- filesToUpload.push(new File([content], file.path, { type: mimeType }));
- }
-
- let repoUrl: string | undefined;
-
- try {
- // Create the space first
- const createResult = await createRepo({
- accessToken: token as string,
- repo: repo,
- sdk: "static",
- });
- repoUrl = createResult.repoUrl;
-
- // Escape commit message to prevent injection
- const escapeCommitMessage = (message: string): string => {
- return message.replace(/[\r\n]/g, " ").slice(0, 200);
- };
- const commitMessage = escapeCommitMessage(prompt ?? "Initial DeepSite commit");
-
- // Upload files to the created space
- await uploadFiles({
- repo,
- files: filesToUpload,
- accessToken: token as string,
- commitTitle: commitMessage,
- });
-
- const path = repoUrl.split("/").slice(-2).join("/");
-
- return NextResponse.json({ repoUrl: path }, { status: 200 });
- } catch (error) {
- const errMsg =
- error instanceof Error ? error.message : "Failed to create or upload to space";
-
- // If space was created but upload failed, include the repo URL in the error
- if (repoUrl) {
- const path = repoUrl.split("/").slice(-2).join("/");
- return NextResponse.json({
- error: `${errMsg}. Space was created at ${path} but files could not be uploaded.`,
- repoUrl: path,
- partialSuccess: true
- }, { status: 500 });
- }
-
- return NextResponse.json({ error: errMsg }, { status: 500 });
- }
-}
diff --git a/app/api/redesign/route.ts b/app/api/redesign/route.ts
deleted file mode 100644
index 6b898d6fd364c5f3ef267706b62c37ee559e19cd..0000000000000000000000000000000000000000
--- a/app/api/redesign/route.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import { NextRequest, NextResponse } from "next/server";
-
-const FETCH_TIMEOUT = 30_000;
-export const maxDuration = 60;
-
-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 controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
-
- try {
- const response = await fetch(
- `https://r.jina.ai/${encodeURIComponent(url)}`,
- {
- method: "POST",
- signal: controller.signal,
- }
- );
-
- clearTimeout(timeoutId);
-
- 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 }
- );
- } catch (fetchError: any) {
- clearTimeout(timeoutId);
-
- if (fetchError.name === "AbortError") {
- return NextResponse.json(
- {
- error:
- "Request timeout: The external service took too long to respond. Please try again.",
- },
- { status: 504 }
- );
- }
- throw fetchError;
- }
- } catch (error: any) {
- if (error.name === "AbortError" || error.message?.includes("timeout")) {
- return NextResponse.json(
- {
- error:
- "Request timeout: The external service took too long to respond. Please try again.",
- },
- { status: 504 }
- );
- }
- return NextResponse.json(
- { error: error.message || "An error occurred" },
- { status: 500 }
- );
- }
-}
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/globals.css b/app/globals.css
deleted file mode 100644
index 97005be73ce05b7bb1a207464af1945139fde62b..0000000000000000000000000000000000000000
--- a/app/globals.css
+++ /dev/null
@@ -1,168 +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-geist-sans);
- --font-mono: var(--font-geist-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.65rem;
- --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);
- --radius: 0.625rem;
- --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;
- }
-}
-
-.monaco-editor .margin {
- @apply bg-background!;
-}
-.monaco-editor .monaco-editor-background {
- @apply bg-background!;
-}
-.monaco-editor .decorationsOverviewRuler {
- @apply opacity-0!;
-}
-.monaco-editor .view-line {
- /* @apply bg-primary/50!; */
-}
-.monaco-editor .scroll-decoration {
- @apply opacity-0!;
-}
-.monaco-editor .cursors-layer .cursor {
- @apply bg-primary!;
-}
-
-.content-placeholder::before {
- content: attr(data-placeholder);
- position: absolute;
- pointer-events: none;
- opacity: 0.5;
- @apply top-5 left-6;
-}
-
-.sp-layout
- .sp-file-explorer
- .sp-file-explorer-list
- .sp-explorer[data-active="true"] {
- @apply text-indigo-500!;
-}
-
-.sp-layout
- .sp-stack
- .sp-tabs
- .sp-tab-container[aria-selected="true"]
- .sp-tab-button {
- @apply text-indigo-500!;
-}
-.sp-layout .sp-stack .sp-tabs .sp-tab-container:has(button:focus) {
- @apply outline-none! border-none!;
-}
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index 469b6844c7b63d85ad5e9c7ba420add2f7f1125c..0000000000000000000000000000000000000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import type { Metadata, Viewport } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import { NextStepProvider } from "nextstepjs";
-import Script from "next/script";
-
-import "@/app/globals.css";
-import { ThemeProvider } from "@/components/providers/theme";
-import { AuthProvider } from "@/components/providers/session";
-import { Toaster } from "@/components/ui/sonner";
-import { ReactQueryProvider } from "@/components/providers/react-query";
-import { generateSEO, generateStructuredData } from "@/lib/seo";
-import { NotAuthorizedDomain } from "@/components/not-authorized";
-
-const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
-});
-
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
-});
-
-export const metadata: Metadata = {
- ...generateSEO({
- 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.",
- path: "/",
- }),
- appleWebApp: {
- capable: true,
- title: "DeepSite",
- statusBarStyle: "black-translucent",
- },
- icons: {
- icon: "/logo.svg",
- shortcut: "/logo.svg",
- apple: "/logo.svg",
- },
- verification: {
- google: process.env.GOOGLE_SITE_VERIFICATION,
- },
-};
-
-export const viewport: Viewport = {
- initialScale: 1,
- maximumScale: 1,
- themeColor: "#4f46e5",
-};
-
-export default async function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const structuredData = generateStructuredData("WebApplication", {
- name: "DeepSite",
- description: "Build websites with AI, no code required",
- url: "https://deepsite.hf.co",
- });
- const organizationData = generateStructuredData("Organization", {
- name: "DeepSite",
- url: "https://deepsite.hf.co",
- });
-
- return (
-
-
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
- );
-}
diff --git a/app/new/page.tsx b/app/new/page.tsx
deleted file mode 100644
index 920e799168597ccab9e644171bff837961cc45e6..0000000000000000000000000000000000000000
--- a/app/new/page.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { AppEditor } from "@/components/editor";
-import { auth } from "@/lib/auth";
-import { redirect } from "next/navigation";
-
-export default async function NewProjectPage({
- searchParams,
-}: {
- searchParams: Promise<{ prompt: string }>;
-}) {
- const session = await auth();
-
- if (!session) {
- redirect("/api/auth/signin?callbackUrl=/new");
- }
-
- const { prompt } = await searchParams;
- return ;
-}
diff --git a/app/not-found.tsx b/app/not-found.tsx
deleted file mode 100644
index ae102854e6245bcc2f0d55780d9419b2679d6f38..0000000000000000000000000000000000000000
--- a/app/not-found.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { NotFoundButtons } from "@/components/not-found/buttons";
-import { Navigation } from "@/components/public/navigation";
-
-export default function NotFound() {
- return (
-
-
-
-
Oh no! Page not found.
-
- The page you are looking for does not exist.
-
-
-
-
- );
-}
diff --git a/assets/deepseek.svg b/assets/deepseek.svg
deleted file mode 100644
index dc224e43a4d68070ca6eed494476c8ddd900bf80..0000000000000000000000000000000000000000
--- a/assets/deepseek.svg
+++ /dev/null
@@ -1 +0,0 @@
-DeepSeek
\ No newline at end of file
diff --git a/assets/hf-logo.svg b/assets/hf-logo.svg
deleted file mode 100644
index b91ba37b236c18b7483716a33a99c05fc43bd00a..0000000000000000000000000000000000000000
--- a/assets/hf-logo.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/assets/kimi.svg b/assets/kimi.svg
deleted file mode 100644
index 4355c522a2dece99e187d9e5c898a66313f4a374..0000000000000000000000000000000000000000
--- a/assets/kimi.svg
+++ /dev/null
@@ -1 +0,0 @@
-Kimi
\ No newline at end of file
diff --git a/assets/minimax.svg b/assets/minimax.svg
deleted file mode 100644
index 1d32449ab8fb0fe9a6c50006a41e67ef49c8dd1c..0000000000000000000000000000000000000000
--- a/assets/minimax.svg
+++ /dev/null
@@ -1 +0,0 @@
-Minimax
\ No newline at end of file
diff --git a/assets/pro.svg b/assets/pro.svg
deleted file mode 100644
index 4d992b483109757a550a27a05caf48cd09daa892..0000000000000000000000000000000000000000
--- a/assets/pro.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/assets/qwen.svg b/assets/qwen.svg
deleted file mode 100644
index a4bb382a6359b82c581fd3e7fb7169fe8fba1657..0000000000000000000000000000000000000000
--- a/assets/qwen.svg
+++ /dev/null
@@ -1 +0,0 @@
-Qwen
\ No newline at end of file
diff --git a/assets/zai.svg b/assets/zai.svg
deleted file mode 100644
index 2adcac387aaca06eb362a177e45c28ae3429b0d0..0000000000000000000000000000000000000000
--- a/assets/zai.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/chart/Chart.yaml b/chart/Chart.yaml
deleted file mode 100644
index 83483de68757c3ed27fd68ca9bbb51aa48a64d25..0000000000000000000000000000000000000000
--- a/chart/Chart.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-apiVersion: v2
-name: deepsite
-version: 0.0.0-latest
-type: application
-icon: https://huggingface.co/front/assets/huggingface_logo-noborder.svg
diff --git a/chart/env/prod.yaml b/chart/env/prod.yaml
deleted file mode 100644
index 2ac82284c67dda993d0292685a8cc56cda2f25e8..0000000000000000000000000000000000000000
--- a/chart/env/prod.yaml
+++ /dev/null
@@ -1,59 +0,0 @@
-nodeSelector:
- role-deepsite: "true"
-
-tolerations:
- - key: "huggingface.co/deepsite"
- operator: "Equal"
- value: "true"
- effect: "NoSchedule"
-
-serviceAccount:
- enabled: true
- create: true
- name: deepsite-prod
-
-ingress:
- path: "/"
- annotations:
- alb.ingress.kubernetes.io/healthcheck-path: "/api/healthcheck"
- alb.ingress.kubernetes.io/listen-ports: "[{\"HTTP\": 80}, {\"HTTPS\": 443}]"
- alb.ingress.kubernetes.io/load-balancer-name: "hub-utils-prod-cloudfront"
- alb.ingress.kubernetes.io/group.name: "hub-utils-prod-cloudfront"
- alb.ingress.kubernetes.io/scheme: "internal"
- alb.ingress.kubernetes.io/ssl-redirect: "443"
- alb.ingress.kubernetes.io/tags: "Env=prod,Project=hub,Terraform=true"
- alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30
- alb.ingress.kubernetes.io/target-type: "ip"
- alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:us-east-1:707930574880:certificate/5b25b145-75db-4837-b9f3-7f238ba8a9c7,arn:aws:acm:us-east-1:707930574880:certificate/bfdf509c-f44b-400f-b9e1-6f7a861abe91"
- kubernetes.io/ingress.class: "alb"
-
-networkPolicy:
- enabled: true
- allowedBlocks:
- - 10.0.0.0/16
-
-
-ingressInternal:
- enabled: false
-
-envVars:
- NEXTAUTH_URL: https://deepsite.hf.co/api/auth
-
-infisical:
- enabled: true
- env: "prod-us-east-1"
-
-autoscaling:
- enabled: true
- minReplicas: 1
- maxReplicas: 10
- targetMemoryUtilizationPercentage: "50"
- targetCPUUtilizationPercentage: "50"
-
-resources:
- requests:
- cpu: 2
- memory: 4Gi
- limits:
- cpu: 4
- memory: 8Gi
diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl
deleted file mode 100644
index eee5a181d225c2aff53344c446288240d37d3d0b..0000000000000000000000000000000000000000
--- a/chart/templates/_helpers.tpl
+++ /dev/null
@@ -1,22 +0,0 @@
-{{- define "name" -}}
-{{- default $.Release.Name | trunc 63 | trimSuffix "-" -}}
-{{- end -}}
-
-{{- define "app.name" -}}
-chat-ui
-{{- end -}}
-
-{{- define "labels.standard" -}}
-release: {{ $.Release.Name | quote }}
-heritage: {{ $.Release.Service | quote }}
-chart: "{{ include "name" . }}"
-app: "{{ include "app.name" . }}"
-{{- end -}}
-
-{{- define "labels.resolver" -}}
-release: {{ $.Release.Name | quote }}
-heritage: {{ $.Release.Service | quote }}
-chart: "{{ include "name" . }}"
-app: "{{ include "app.name" . }}-resolver"
-{{- end -}}
-
diff --git a/chart/templates/config.yaml b/chart/templates/config.yaml
deleted file mode 100644
index c4c803e9e5f8b473ae216d5a2933cb67d46bc011..0000000000000000000000000000000000000000
--- a/chart/templates/config.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
-data:
- {{- range $key, $value := $.Values.envVars }}
- {{ $key }}: {{ $value | quote }}
- {{- end }}
diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml
deleted file mode 100644
index b487ee6a3e5db9e315dcb024e6fdc6b753eea5a3..0000000000000000000000000000000000000000
--- a/chart/templates/deployment.yaml
+++ /dev/null
@@ -1,81 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
- {{- if .Values.infisical.enabled }}
- annotations:
- secrets.infisical.com/auto-reload: "true"
- {{- end }}
-spec:
- progressDeadlineSeconds: 600
- {{- if not $.Values.autoscaling.enabled }}
- replicas: {{ .Values.replicas }}
- {{- end }}
- revisionHistoryLimit: 10
- selector:
- matchLabels: {{ include "labels.standard" . | nindent 6 }}
- strategy:
- rollingUpdate:
- maxSurge: 25%
- maxUnavailable: 25%
- type: RollingUpdate
- template:
- metadata:
- labels: {{ include "labels.standard" . | nindent 8 }}
- annotations:
- checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
- {{- if $.Values.envVars.NODE_LOG_STRUCTURED_DATA }}
- co.elastic.logs/json.expand_keys: "true"
- {{- end }}
- spec:
- {{- if .Values.serviceAccount.enabled }}
- serviceAccountName: "{{ .Values.serviceAccount.name | default (include "name" .) }}"
- {{- end }}
- containers:
- - name: chat-ui
- image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
- imagePullPolicy: {{ .Values.image.pullPolicy }}
- readinessProbe:
- failureThreshold: 30
- periodSeconds: 10
- httpGet:
- path: {{ $.Values.envVars.APP_BASE | default "" }}/api/healthcheck
- port: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
- livenessProbe:
- failureThreshold: 30
- periodSeconds: 10
- httpGet:
- path: {{ $.Values.envVars.APP_BASE | default "" }}/api/healthcheck
- port: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
- ports:
- - containerPort: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
- name: http
- protocol: TCP
- {{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
- - containerPort: {{ $.Values.envVars.METRICS_PORT | default 5565 | int }}
- name: metrics
- protocol: TCP
- {{- end }}
- resources: {{ toYaml .Values.resources | nindent 12 }}
- {{- with $.Values.extraEnv }}
- env:
- {{- toYaml . | nindent 14 }}
- {{- end }}
- envFrom:
- - configMapRef:
- name: {{ include "name" . }}
- {{- if $.Values.infisical.enabled }}
- - secretRef:
- name: {{ include "name" $ }}-secs
- {{- end }}
- {{- with $.Values.extraEnvFrom }}
- {{- toYaml . | nindent 14 }}
- {{- end }}
- nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }}
- tolerations: {{ toYaml .Values.tolerations | nindent 8 }}
- volumes:
- - name: config
- configMap:
- name: {{ include "name" . }}
diff --git a/chart/templates/hpa.yaml b/chart/templates/hpa.yaml
deleted file mode 100644
index bf7bd3b256b79c54269ae39afb02c816878596dc..0000000000000000000000000000000000000000
--- a/chart/templates/hpa.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-{{- if $.Values.autoscaling.enabled }}
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
-spec:
- scaleTargetRef:
- apiVersion: apps/v1
- kind: Deployment
- name: {{ include "name" . }}
- minReplicas: {{ $.Values.autoscaling.minReplicas }}
- maxReplicas: {{ $.Values.autoscaling.maxReplicas }}
- metrics:
- {{- if ne "" $.Values.autoscaling.targetMemoryUtilizationPercentage }}
- - type: Resource
- resource:
- name: memory
- target:
- type: Utilization
- averageUtilization: {{ $.Values.autoscaling.targetMemoryUtilizationPercentage | int }}
- {{- end }}
- {{- if ne "" $.Values.autoscaling.targetCPUUtilizationPercentage }}
- - type: Resource
- resource:
- name: cpu
- target:
- type: Utilization
- averageUtilization: {{ $.Values.autoscaling.targetCPUUtilizationPercentage | int }}
- {{- end }}
- behavior:
- scaleDown:
- stabilizationWindowSeconds: 600
- policies:
- - type: Percent
- value: 10
- periodSeconds: 60
- scaleUp:
- stabilizationWindowSeconds: 0
- policies:
- - type: Pods
- value: 1
- periodSeconds: 30
-{{- end }}
diff --git a/chart/templates/infisical.yaml b/chart/templates/infisical.yaml
deleted file mode 100644
index 6a11e084f6e0ab300ea4ec2b2694a79dadc1bdf8..0000000000000000000000000000000000000000
--- a/chart/templates/infisical.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-{{- if .Values.infisical.enabled }}
-apiVersion: secrets.infisical.com/v1alpha1
-kind: InfisicalSecret
-metadata:
- name: {{ include "name" $ }}-infisical-secret
- namespace: {{ $.Release.Namespace }}
-spec:
- authentication:
- universalAuth:
- credentialsRef:
- secretName: {{ .Values.infisical.operatorSecretName | quote }}
- secretNamespace: {{ .Values.infisical.operatorSecretNamespace | quote }}
- secretsScope:
- envSlug: {{ .Values.infisical.env | quote }}
- projectSlug: {{ .Values.infisical.project | quote }}
- secretsPath: /
- hostAPI: {{ .Values.infisical.url | quote }}
- managedSecretReference:
- creationPolicy: Owner
- secretName: {{ include "name" $ }}-secs
- secretNamespace: {{ .Release.Namespace | quote }}
- secretType: Opaque
- resyncInterval: {{ .Values.infisical.resyncInterval }}
-{{- end }}
diff --git a/chart/templates/ingress-internal.yaml b/chart/templates/ingress-internal.yaml
deleted file mode 100644
index bf87d0b6c960871327908e243e79e05475b825d5..0000000000000000000000000000000000000000
--- a/chart/templates/ingress-internal.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-{{- if $.Values.ingressInternal.enabled }}
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations: {{ toYaml .Values.ingressInternal.annotations | nindent 4 }}
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}-internal
- namespace: {{ .Release.Namespace }}
-spec:
- {{ if $.Values.ingressInternal.className }}
- ingressClassName: {{ .Values.ingressInternal.className }}
- {{ end }}
- {{- with .Values.ingressInternal.tls }}
- tls:
- - hosts:
- - {{ $.Values.domain | quote }}
- {{- with .secretName }}
- secretName: {{ . }}
- {{- end }}
- {{- end }}
- rules:
- - host: {{ .Values.domain }}
- http:
- paths:
- - backend:
- service:
- name: {{ include "name" . }}
- port:
- name: http
- path: {{ $.Values.ingressInternal.path | default "/" }}
- pathType: Prefix
-{{- end }}
diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml
deleted file mode 100644
index 563c911d72b906e18d716a4b2b4d3d26842d7fd7..0000000000000000000000000000000000000000
--- a/chart/templates/ingress.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-{{- if $.Values.ingress.enabled }}
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- annotations: {{ toYaml .Values.ingress.annotations | nindent 4 }}
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
-spec:
- {{ if $.Values.ingress.className }}
- ingressClassName: {{ .Values.ingress.className }}
- {{ end }}
- {{- with .Values.ingress.tls }}
- tls:
- - hosts:
- - {{ $.Values.domain | quote }}
- {{- with .secretName }}
- secretName: {{ . }}
- {{- end }}
- {{- end }}
- rules:
- - host: {{ .Values.domain }}
- http:
- paths:
- - backend:
- service:
- name: {{ include "name" . }}
- port:
- name: http
- path: {{ $.Values.ingress.path | default "/" }}
- pathType: Prefix
-
- {{- end }}
diff --git a/chart/templates/network-policy.yaml b/chart/templates/network-policy.yaml
deleted file mode 100644
index 59f5df5893a97f4075237ac7cdb4979dce7298a9..0000000000000000000000000000000000000000
--- a/chart/templates/network-policy.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-{{- if $.Values.networkPolicy.enabled }}
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
-spec:
- egress:
- - ports:
- - port: 53
- protocol: UDP
- to:
- - namespaceSelector:
- matchLabels:
- kubernetes.io/metadata.name: kube-system
- podSelector:
- matchLabels:
- k8s-app: kube-dns
- - to:
- {{- range $ip := .Values.networkPolicy.allowedBlocks }}
- - ipBlock:
- cidr: {{ $ip | quote }}
- {{- end }}
- - to:
- - ipBlock:
- cidr: 0.0.0.0/0
- except:
- - 10.0.0.0/8
- - 172.16.0.0/12
- - 192.168.0.0/16
- - 169.254.169.254/32
- podSelector:
- matchLabels: {{ include "labels.standard" . | nindent 6 }}
- policyTypes:
- - Egress
-{{- end }}
diff --git a/chart/templates/service-account.yaml b/chart/templates/service-account.yaml
deleted file mode 100644
index fc3a184c9def4cef61836f8eac152ab61fe4d047..0000000000000000000000000000000000000000
--- a/chart/templates/service-account.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-{{- if and .Values.serviceAccount.enabled .Values.serviceAccount.create }}
-apiVersion: v1
-kind: ServiceAccount
-automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
-metadata:
- name: "{{ .Values.serviceAccount.name | default (include "name" .) }}"
- namespace: {{ .Release.Namespace }}
- labels: {{ include "labels.standard" . | nindent 4 }}
- {{- with .Values.serviceAccount.annotations }}
- annotations:
- {{- toYaml . | nindent 4 }}
- {{- end }}
-{{- end }}
diff --git a/chart/templates/service-monitor.yaml b/chart/templates/service-monitor.yaml
deleted file mode 100644
index 0c8e4dab423946a318a3daee7bd4dfcab0cee151..0000000000000000000000000000000000000000
--- a/chart/templates/service-monitor.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-{{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
-apiVersion: monitoring.coreos.com/v1
-kind: ServiceMonitor
-metadata:
- labels: {{ include "labels.standard" . | nindent 4 }}
- name: {{ include "name" . }}
- namespace: {{ .Release.Namespace }}
-spec:
- selector:
- matchLabels: {{ include "labels.standard" . | nindent 6 }}
- endpoints:
- - port: metrics
- path: /metrics
- interval: 10s
- scheme: http
- scrapeTimeout: 10s
-{{- end }}
diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml
deleted file mode 100644
index ef364f0926861b9e934e9830d7884cdd63ecd6ec..0000000000000000000000000000000000000000
--- a/chart/templates/service.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- name: "{{ include "name" . }}"
- annotations: {{ toYaml .Values.service.annotations | nindent 4 }}
- namespace: {{ .Release.Namespace }}
- labels: {{ include "labels.standard" . | nindent 4 }}
-spec:
- ports:
- - name: http
- port: 80
- protocol: TCP
- targetPort: http
- {{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
- - name: metrics
- port: {{ $.Values.envVars.METRICS_PORT | default 5565 | int }}
- protocol: TCP
- targetPort: metrics
- {{- end }}
- selector: {{ include "labels.standard" . | nindent 4 }}
- type: {{.Values.service.type}}
diff --git a/chart/values.yaml b/chart/values.yaml
deleted file mode 100644
index 5a2bd68b20d5efde44f87d7ffb8278717fa5aaf8..0000000000000000000000000000000000000000
--- a/chart/values.yaml
+++ /dev/null
@@ -1,81 +0,0 @@
-image:
- repository: registry.internal.huggingface.tech/deepsite
- name: deepsite
- tag: 0.0.0-latest
- pullPolicy: IfNotPresent
-
-replicas: 1
-
-domain: deepsite.hf.co
-
-networkPolicy:
- enabled: true
- allowedBlocks: []
- # allowedBlocks:
- # - 10.0.240.0/24
- # - 10.0.241.0/24
- # - 10.0.242.0/24
- # - 10.0.243.0/24
- # - 10.0.244.0/24
- # - 10.240.0.0/24
- # - 10.16.0.0/16
-
-service:
- type: NodePort
- annotations: { }
-
-serviceAccount:
- enabled: false
- create: false
- name: ""
- automountServiceAccountToken: true
- annotations: { }
-
-ingress:
- enabled: true
- path: "/"
- annotations: { }
- # className: "nginx"
- tls: { }
- # secretName: XXX
-
-ingressInternal:
- enabled: false
- path: "/"
- annotations: { }
- # className: "nginx"
- tls: { }
-
-resources:
- requests:
- cpu: 2
- memory: 4Gi
- limits:
- cpu: 2
- memory: 4Gi
-nodeSelector: {}
-tolerations: []
-
-envVars: { }
-
-infisical:
- enabled: false
- env: ""
- project: "deepsite-f-hvj"
- url: ""
- resyncInterval: 60
- operatorSecretName: "deepsite-operator-secrets"
- operatorSecretNamespace: "hub-utils"
-
-# Allow to environment injections on top or instead of infisical
-extraEnvFrom: []
-extraEnv: []
-
-autoscaling:
- enabled: false
- minReplicas: 1
- maxReplicas: 2
- targetMemoryUtilizationPercentage: ""
- targetCPUUtilizationPercentage: ""
-
-## Metrics removed; monitoring configuration no longer used
diff --git a/components.json b/components.json
deleted file mode 100644
index d5005f0974a11b0ea57843b9f52f82f995743963..0000000000000000000000000000000000000000
--- a/components.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "new-york",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "",
- "css": "app/globals.css",
- "baseColor": "zinc",
- "cssVariables": true,
- "prefix": ""
- },
- "iconLibrary": "lucide",
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils",
- "ui": "@/components/ui",
- "lib": "@/lib",
- "hooks": "@/hooks"
- },
- "registries": {}
-}
diff --git a/components/ask-ai/ask-ai-landing.tsx b/components/ask-ai/ask-ai-landing.tsx
deleted file mode 100644
index db19f5be4bfc3fc05e4083a18a41cca6169bf65a..0000000000000000000000000000000000000000
--- a/components/ask-ai/ask-ai-landing.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-"use client";
-import { ArrowUp } from "lucide-react";
-import { useState } from "react";
-import { useLocalStorage, useMount } from "react-use";
-import { useRouter } from "next/navigation";
-
-import { Button } from "@/components/ui/button";
-import { ProviderType } from "@/lib/type";
-import { Models } from "./models";
-import { DEFAULT_MODEL } from "@/lib/providers";
-import { cn } from "@/lib/utils";
-
-export function AskAiLanding({ className }: { className?: string }) {
- const [model = DEFAULT_MODEL, setModel] = useLocalStorage(
- "deepsite-model",
- DEFAULT_MODEL
- );
- const [provider, setProvider] = useLocalStorage(
- "deepsite-provider",
- "auto" as ProviderType
- );
- const router = useRouter();
- const [prompt, setPrompt] = useState("");
- const [mounted, setMounted] = useState(false);
-
- useMount(() => {
- setMounted(true);
- });
-
- return (
-
- );
-}
diff --git a/components/ask-ai/ask-ai.tsx b/components/ask-ai/ask-ai.tsx
deleted file mode 100644
index 66ce78f0d25644ecc7c222911f9455fca60de75c..0000000000000000000000000000000000000000
--- a/components/ask-ai/ask-ai.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-"use client";
-import { ArrowUp, Paintbrush, X } from "lucide-react";
-import { FaHand } from "react-icons/fa6";
-import { useRef, useState } from "react";
-import { HiStop } from "react-icons/hi2";
-import { useLocalStorage, useMount, useUpdateEffect } from "react-use";
-import { useRouter } from "next/navigation";
-import { useNextStep } from "nextstepjs";
-
-import { Button } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-import { useGeneration } from "./useGeneration";
-import { File, MobileTabType, ProviderType } from "@/lib/type";
-import { Models } from "./models";
-import { DEFAULT_MODEL, MODELS } from "@/lib/providers";
-import { Redesign } from "./redesign";
-import { Uploader } from "./uploader";
-import { InputMentions } from "./input-mentions";
-
-export function AskAI({
- initialPrompt,
- className,
- onToggleMobileTab,
- files,
- medias,
- tourHasBeenShown,
- isNew = false,
- isHistoryView,
- projectName = "new",
-}: {
- initialPrompt?: string;
- className?: string;
- files?: File[] | null;
- medias?: string[] | null;
- tourHasBeenShown?: boolean;
- onToggleMobileTab?: (tab: MobileTabType) => void;
- isNew?: boolean;
- isHistoryView?: boolean;
- projectName?: string;
-}) {
- const contentEditableRef = useRef(null);
- const [model = DEFAULT_MODEL, setModel] = useLocalStorage(
- "deepsite-model",
- DEFAULT_MODEL
- );
- const [provider, setProvider] = useLocalStorage(
- "deepsite-provider",
- "auto" as ProviderType
- );
-
- const [prompt, setPrompt] = useState(initialPrompt ?? "");
- const [redesignMd, setRedesignMd] = useState<{
- md: string;
- url: string;
- } | null>(null);
- const [selectedMedias, setSelectedMedias] = useState([]);
- const [imageLinks, setImageLinks] = useState([]);
- const [startTour, setStartTour] = useState(false);
-
- const router = useRouter();
- const { callAi, isLoading, stopGeneration, audio } =
- useGeneration(projectName);
- const { startNextStep } = useNextStep();
-
- const onComplete = () => {
- onToggleMobileTab?.("right-sidebar");
- };
-
- useMount(() => {
- if (initialPrompt && initialPrompt.trim() !== "" && isNew) {
- setTimeout(() => {
- if (isHistoryView) return;
- callAi(
- {
- prompt: initialPrompt,
- model,
- onComplete,
- provider,
- },
- setModel
- );
- router.replace("/new");
- }, 200);
- }
- });
-
- const onSubmit = () => {
- if (isHistoryView) return;
- if (contentEditableRef.current) {
- contentEditableRef.current.innerHTML = "";
- }
- callAi(
- {
- prompt,
- model,
- onComplete,
- provider,
- redesignMd,
- medias: [...(selectedMedias ?? []), ...(imageLinks ?? [])],
- },
- setModel
- );
- if (selectedMedias.length > 0) setSelectedMedias([]);
- if (imageLinks.length > 0) setImageLinks([]);
- if (redesignMd) setRedesignMd(null);
- };
-
- return (
-
-
-
-
-
- Your browser does not support the audio element.
-
-
- );
-}
diff --git a/components/ask-ai/context.tsx b/components/ask-ai/context.tsx
deleted file mode 100644
index c22f17fc81f95788b6d3e690cc70ac5d3b6f817a..0000000000000000000000000000000000000000
--- a/components/ask-ai/context.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import { AtSign, Braces, FileCode, FileText, X } from "lucide-react";
-import { useMemo, useState } from "react";
-import { useQueryClient } from "@tanstack/react-query";
-
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Button } from "@/components/ui/button";
-import { File } from "@/lib/type";
-import { cn } from "@/lib/utils";
-
-export const Context = ({
- files,
- setFiles,
-}: {
- files: File[];
- setFiles: (files: File[]) => void;
-}) => {
- const queryClient = useQueryClient();
- const [open, setOpen] = useState(false);
-
- const getFileIcon = (filePath: string, size = "size-3.5") => {
- if (filePath.endsWith(".css")) {
- return ;
- } else if (filePath.endsWith(".js")) {
- return ;
- } else if (filePath.endsWith(".json")) {
- return ;
- } else {
- return ;
- }
- };
-
- const getFiles = () => queryClient.getQueryData(["files"]) ?? [];
-
- return (
-
-
-
-
-
- Add Context...
-
-
-
-
-
- Select a file to send as context
-
-
- {getFiles().length === 0 ? (
-
- No files available
-
- ) : (
- <>
-
{
- setFiles([]);
- setOpen(false);
- }}
- className={`cursor-pointer w-full px-2 py-1.5 text-xs text-left rounded-md hover:bg-accent hover:text-accent-foreground transition-colors ${
- files.length === 0
- ? "bg-linear-to-r from-indigo-500/20 to-indigo-500/5 text-primary font-medium"
- : "text-muted-foreground"
- }`}
- >
- All files (default)
-
- {getFiles()?.map((page) => (
-
{
- if (files.some((f) => f.path === page.path))
- setFiles(files.filter((f) => f.path !== page.path));
- else setFiles(files ? [...files, page] : [page]);
- setOpen(false);
- }}
- className={`cursor-pointer w-full px-2 py-1.5 text-xs text-left rounded-md hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-1.5 ${
- files?.some((f) => f.path === page.path)
- ? "bg-linear-to-r from-indigo-500/20 to-indigo-500/5 text-primary font-medium"
- : "text-muted-foreground"
- }`}
- >
-
- {getFileIcon(page.path, "size-3")}
-
- {page.path}
-
- ))}
- >
- )}
-
-
-
-
- {files?.map((file) => (
-
- {getFileIcon(file.path, "size-3")}
- {file.path}
- {
- setFiles(files.filter((f) => f.path !== file.path));
- }}
- >
-
-
-
- ))}
-
- );
-};
diff --git a/components/ask-ai/input-mentions.tsx b/components/ask-ai/input-mentions.tsx
deleted file mode 100644
index 4ef11b0a0cd9aeaef85eddfd049a30272437b414..0000000000000000000000000000000000000000
--- a/components/ask-ai/input-mentions.tsx
+++ /dev/null
@@ -1,477 +0,0 @@
-import { useRef, useState, useEffect, RefObject } from "react";
-import { useClickAway } from "react-use";
-import { useQueryClient } from "@tanstack/react-query";
-
-import { searchFilesMentions } from "@/actions/mentions";
-import { File } from "@/lib/type";
-import { Braces, FileCode, FileText } from "lucide-react";
-
-export function InputMentions({
- ref,
- prompt,
- files,
- setPrompt,
- redesignMdUrl,
- onSubmit,
- imageLinks,
- setImageLinks,
-}: {
- ref: RefObject;
- prompt: string;
- files?: File[] | null;
- redesignMdUrl?: string;
- setPrompt: (prompt: string) => void;
- onSubmit: () => void;
- imageLinks?: string[];
- setImageLinks?: (links: string[]) => void;
-}) {
- const queryClient = useQueryClient();
- const [showMentionDropdown, setShowMentionDropdown] = useState(false);
- const [, setMentionSearch] = useState("");
- const dropdownRef = useRef(null);
- const [results, setResults] = useState([]);
-
- useClickAway(dropdownRef, () => {
- setShowMentionDropdown(false);
- });
-
- const isImageUrl = (url: string): boolean => {
- // Check if it's a valid HTTP/HTTPS URL with an image extension
- const imageUrlPattern = /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp|svg|bmp|ico)(\?.*)?$/i;
- return imageUrlPattern.test(url);
- };
-
- const getTextContent = (element: HTMLElement): string => {
- let text = "";
- const childNodes = element.childNodes;
-
- for (let i = 0; i < childNodes.length; i++) {
- const node = childNodes[i];
- if (node.nodeType === Node.TEXT_NODE) {
- text += node.textContent || "";
- } else if (node.nodeType === Node.ELEMENT_NODE) {
- const el = node as HTMLElement;
- if (el.classList.contains("mention-chip")) {
- text += el.getAttribute("data-mention-id") || "";
- } else if (el.classList.contains("image-chip")) {
- // Include image URL in text content for display purposes
- const imageUrl = el.getAttribute("data-image-url") || "";
- text += imageUrl ? ` ${imageUrl} ` : "";
- } else {
- text += el.textContent || "";
- }
- }
- }
- return text + "\u0020";
- };
-
- const extractPromptWithIds = (): string => {
- if (!ref.current) return "";
-
- let text = "";
- const childNodes = ref.current.childNodes;
-
- for (let i = 0; i < childNodes.length; i++) {
- const node = childNodes[i];
- if (node.nodeType === Node.TEXT_NODE) {
- text += node.textContent || "";
- } else if (node.nodeType === Node.ELEMENT_NODE) {
- const el = node as HTMLElement;
- if (el.classList.contains("mention-chip")) {
- text += el.getAttribute("data-mention-id") || "";
- } else if (el.classList.contains("image-chip")) {
- // Include image URL in prompt text
- const imageUrl = el.getAttribute("data-image-url") || "";
- text += imageUrl ? ` ${imageUrl} ` : "";
- } else {
- text += el.textContent || "";
- }
- }
- }
- return text;
- };
-
- const extractImageLinks = (): string[] => {
- if (!ref.current) return [];
-
- const links: string[] = [];
- const childNodes = ref.current.childNodes;
-
- for (let i = 0; i < childNodes.length; i++) {
- const node = childNodes[i];
- if (node.nodeType === Node.ELEMENT_NODE) {
- const el = node as HTMLElement;
- if (el.classList.contains("image-chip")) {
- const imageUrl = el.getAttribute("data-image-url");
- if (imageUrl) links.push(imageUrl);
- }
- }
- }
- return links;
- };
-
- const shouldDetectMention = (): {
- detect: boolean;
- textBeforeCursor: string;
- } => {
- const selection = window.getSelection();
- if (!selection || !ref.current) {
- return { detect: false, textBeforeCursor: "" };
- }
-
- const range = selection.getRangeAt(0);
- const node = range.startContainer;
-
- if (node.nodeType === Node.ELEMENT_NODE) {
- const element = node as HTMLElement;
- if (element.classList?.contains("mention-chip")) {
- return { detect: false, textBeforeCursor: "" };
- }
- }
-
- if (node.parentElement?.classList?.contains("mention-chip")) {
- return { detect: false, textBeforeCursor: "" };
- }
-
- if (node.nodeType === Node.TEXT_NODE) {
- const textContent = node.textContent || "";
- const cursorOffset = range.startOffset;
- const textBeforeCursor = textContent.substring(0, cursorOffset);
-
- const lastAtIndex = textBeforeCursor.lastIndexOf("@");
- if (lastAtIndex !== -1) {
- const textAfterAt = textBeforeCursor.substring(lastAtIndex + 1);
- if (!textAfterAt.includes(" ")) {
- return { detect: true, textBeforeCursor: textAfterAt };
- }
- }
- }
-
- return { detect: false, textBeforeCursor: "" };
- };
-
- const handleInput = async () => {
- if (!ref.current) return;
- const text = getTextContent(ref.current);
-
- // Only clear if there's no content at all (including chips)
- const hasImageChips = ref.current.querySelectorAll(".image-chip").length > 0;
- const hasMentionChips = ref.current.querySelectorAll(".mention-chip").length > 0;
-
- if (text.trim() === "" && !hasImageChips && !hasMentionChips) {
- ref.current.innerHTML = "";
- }
-
- setPrompt(text);
-
- // Update image links whenever input changes
- if (setImageLinks) {
- const links = extractImageLinks();
- setImageLinks(links);
- }
-
- const { detect, textBeforeCursor } = shouldDetectMention();
-
- if (detect && files && files?.length > 0) {
- setMentionSearch(textBeforeCursor);
- setShowMentionDropdown(true);
- const files = queryClient.getQueryData(["files"]) ?? [];
- const results = await searchFilesMentions(textBeforeCursor, files);
- setResults(results);
- } else {
- setShowMentionDropdown(false);
- }
- };
-
- const createMentionChipElement = (mentionId: string): HTMLSpanElement => {
- const mentionChip = document.createElement("span");
-
- mentionChip.className =
- "mention-chip inline-flex w-fit items-center justify-center gap-1 font-mono px-2 py-0.5 rounded-full text-xs font-medium bg-indigo-500/10 text-indigo-500 dark:bg-indigo-500/20 dark:text-indigo-400";
- mentionChip.contentEditable = "false";
- mentionChip.setAttribute("data-mention-id", `file:/${mentionId}`);
- mentionChip.textContent = `@${mentionId}`;
-
- return mentionChip;
- };
-
- const createImageChipElement = (imageUrl: string): HTMLSpanElement => {
- const imageChip = document.createElement("span");
-
- imageChip.className =
- "image-chip inline-flex w-fit items-center justify-center gap-1 font-mono px-2 py-0.5 rounded-full text-xs font-medium bg-emerald-500/10 text-emerald-600 dark:bg-emerald-500/20 dark:text-emerald-400";
- imageChip.contentEditable = "false";
- imageChip.setAttribute("data-image-url", imageUrl);
-
- // Create icon (using emoji for simplicity)
- const icon = document.createElement("span");
- icon.textContent = "🖼️";
- icon.className = "text-[10px]";
-
- // Truncate URL for display
- const displayUrl =
- imageUrl.length > 30 ? imageUrl.substring(0, 30) + "..." : imageUrl;
- const text = document.createTextNode(displayUrl);
-
- imageChip.appendChild(icon);
- imageChip.appendChild(text);
-
- return imageChip;
- };
-
- const insertMention = (mentionId: string) => {
- if (!ref.current) return;
-
- const selection = window.getSelection();
- if (!selection || selection.rangeCount === 0) return;
-
- const range = selection.getRangeAt(0);
- const textNode = range.startContainer;
-
- if (textNode.nodeType !== Node.TEXT_NODE) return;
-
- const textContent = textNode.textContent || "";
- const cursorOffset = range.startOffset;
-
- const textBeforeCursor = textContent.substring(0, cursorOffset);
- const lastAtIndex = textBeforeCursor.lastIndexOf("@");
-
- if (lastAtIndex !== -1) {
- const mentionChip = createMentionChipElement(mentionId);
-
- const beforeText = textContent.substring(0, lastAtIndex);
- const afterText = textContent.substring(cursorOffset);
- const parent = textNode.parentNode;
- if (!parent) return;
-
- const beforeNode = beforeText
- ? document.createTextNode(beforeText)
- : null;
- const spaceNode = document.createTextNode("\u0020");
- const afterNode = afterText ? document.createTextNode(afterText) : null;
-
- if (beforeNode) {
- parent.insertBefore(beforeNode, textNode);
- }
- parent.insertBefore(mentionChip, textNode);
- parent.insertBefore(spaceNode, textNode);
- if (afterNode) {
- parent.insertBefore(afterNode, textNode);
- }
-
- parent.removeChild(textNode);
-
- const newRange = document.createRange();
- if (afterNode) {
- newRange.setStart(afterNode, 0);
- } else {
- newRange.setStartAfter(spaceNode);
- }
- newRange.collapse(true);
- selection.removeAllRanges();
- selection.addRange(newRange);
-
- const newText = getTextContent(ref.current);
- setPrompt(newText);
- setShowMentionDropdown(false);
- setMentionSearch("");
- }
- };
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (!prompt || prompt.trim() === "") return;
-
- if (e.key === "Enter" && !e.shiftKey) {
- e.preventDefault();
- const promptWithIds = extractPromptWithIds();
- setPrompt(promptWithIds);
-
- if (setImageLinks) {
- const links = extractImageLinks();
- setImageLinks(links);
- }
-
- onSubmit();
-
- if (ref.current) {
- ref.current.innerHTML = "";
- }
- setPrompt("");
- setShowMentionDropdown(false);
- } else if (e.key === "Escape") {
- setShowMentionDropdown(false);
- }
- };
-
- useEffect(() => {
- if (ref.current && prompt === "" && ref.current.innerHTML !== "") {
- ref.current.innerHTML = "";
- }
- }, [prompt]);
-
- const handlePaste = (e: React.ClipboardEvent) => {
- e.preventDefault();
- const text = e.clipboardData.getData("text/plain");
-
- const selection = window.getSelection();
- if (!selection || selection.rangeCount === 0 || !ref.current) {
- document.execCommand("insertText", false, text);
- return;
- }
-
- const trimmedText = text.trim();
-
- // Check if pasted text is ONLY an image URL
- if (isImageUrl(trimmedText)) {
- const range = selection.getRangeAt(0);
- const imageChip = createImageChipElement(trimmedText);
- const spaceNode = document.createTextNode("\u0020");
-
- range.deleteContents();
- range.insertNode(imageChip);
-
- // Insert space after the chip
- const afterChipRange = document.createRange();
- afterChipRange.setStartAfter(imageChip);
- afterChipRange.insertNode(spaceNode);
-
- // Move cursor after the space
- const newRange = document.createRange();
- newRange.setStartAfter(spaceNode);
- newRange.collapse(true);
- selection.removeAllRanges();
- selection.addRange(newRange);
-
- // Update state
- const newText = getTextContent(ref.current);
- setPrompt(newText);
-
- if (setImageLinks) {
- const links = extractImageLinks();
- setImageLinks(links);
- }
- } else {
- // Check if text contains image URLs
- const imageUrlPattern = /https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|webp|svg|bmp|ico)(\?[^\s]*)?/gi;
- const containsImageUrl = imageUrlPattern.test(text);
-
- if (containsImageUrl) {
- // Split text and replace image URLs with chips
- const parts = text.split(/(https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|webp|svg|bmp|ico)(\?[^\s]*)?)/gi);
- const range = selection.getRangeAt(0);
- range.deleteContents();
-
- const fragment = document.createDocumentFragment();
-
- parts.forEach((part) => {
- if (isImageUrl(part)) {
- const imageChip = createImageChipElement(part);
- const spaceNode = document.createTextNode("\u0020");
- fragment.appendChild(imageChip);
- fragment.appendChild(spaceNode);
- } else if (part && !part.match(/^\?[^\s]*$/)) {
- // Skip query string matches, add other text
- const textNode = document.createTextNode(part);
- fragment.appendChild(textNode);
- }
- });
-
- range.insertNode(fragment);
-
- // Move cursor to end
- const newRange = document.createRange();
- newRange.selectNodeContents(ref.current);
- newRange.collapse(false);
- selection.removeAllRanges();
- selection.addRange(newRange);
-
- // Update state
- const newText = getTextContent(ref.current);
- setPrompt(newText);
-
- if (setImageLinks) {
- const links = extractImageLinks();
- setImageLinks(links);
- }
- } else {
- // Plain text, use default insertion
- document.execCommand("insertText", false, text);
- }
- }
- };
-
- return (
-
-
"']/g,
- ""
- )}, want to add something?`
- : files && files.length > 0
- ? "Ask me anything. Type @ to mention a file..."
- : "Ask me anything..."
- }
- onInput={handleInput}
- onKeyDown={handleKeyDown}
- onPaste={handlePaste}
- suppressContentEditableWarning
- >
- {showMentionDropdown && (
-
-
- {results?.length > 0 && (
-
- {results.map((file) => (
- insertMention(file.path)}
- />
- ))}
-
- )}
-
-
- )}
-
- );
-}
-
-export const getFileIcon = (filePath: string, size = "size-3.5") => {
- if (filePath.endsWith(".css")) {
- return ;
- } else if (filePath.endsWith(".js")) {
- return ;
- } else if (filePath.endsWith(".json")) {
- return ;
- } else {
- return ;
- }
-};
-
-function MentionResult({
- file,
- onSelect,
-}: {
- file: File;
- onSelect: () => void;
-}) {
- return (
-
- {getFileIcon(file.path, "size-3")}
- {file.path}
-
- );
-}
diff --git a/components/ask-ai/loading.tsx b/components/ask-ai/loading.tsx
deleted file mode 100644
index 060be494fb586788eab5c2139bb080fa51e209a2..0000000000000000000000000000000000000000
--- a/components/ask-ai/loading.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-import Loading from "@/components/loading";
-
-export const AiLoading = ({
- text = "AI Assistant is thinking...",
- showCircle = true,
- className,
-}: {
- text?: string;
- showCircle?: boolean;
- className?: string;
-}) => {
- return (
-
- {showCircle &&
}
-
-
- {text.split("").map((char, index) => (
-
- {char === " " ? "\u00A0" : char}
-
- ))}
-
-
-
- );
-};
diff --git a/components/ask-ai/models.tsx b/components/ask-ai/models.tsx
deleted file mode 100644
index 4e58acffcf87295047815246cf3b2dd14ef04d2f..0000000000000000000000000000000000000000
--- a/components/ask-ai/models.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-import {
- BrainIcon,
- ChevronDown,
- DollarSign,
- StarsIcon,
- Zap,
-} from "lucide-react";
-import { useMemo, useState } from "react";
-
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Button } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-import { ProviderType } from "@/lib/type";
-import { MODELS } from "@/lib/providers";
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-
-export function Models({
- model,
- setModel,
- provider,
- setProvider,
-}: {
- model: string;
- setModel: (model: string) => void;
- provider: ProviderType;
- setProvider: (provider: ProviderType) => void;
-}) {
- const [open, setOpen] = useState(false);
-
- const formattedModels = useMemo(() => {
- const lists: ((typeof MODELS)[0] | { isCategory: true; name: string })[] =
- [];
- const keys = new Set();
- MODELS.forEach((model) => {
- if (!keys.has(model.companyName)) {
- lists.push({
- isCategory: true,
- name: model.companyName,
- logo: model.logo,
- });
- keys.add(model.companyName);
- }
- lists.push(model);
- });
- return lists;
- }, []);
-
- return (
-
-
-
-
- {model.split("/").pop()?.toLowerCase()}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {formattedModels.map(
- (
- item:
- | (typeof MODELS)[0]
- | { isCategory: true; name: string }
- ) => {
- if ("isCategory" in item) {
- return (
-
- {item.name}
-
- );
- }
- const {
- value,
- label,
- isNew = false,
- isBestSeller = false,
- } = item;
- return (
-
- {value.split("/").pop() || label}
- {isNew && (
-
- New
-
- )}
- {isBestSeller && (
-
- )}
-
- );
- }
- )}
-
-
-
-
-
-
Provider mode:
-
- {(
- [
- { value: "cheapest", icon: DollarSign, color: "emerald" },
- {
- value: "auto",
- icon: BrainIcon,
- color: "indigo",
- name: "Smartest",
- },
- { value: "fastest", icon: Zap, color: "amber" },
- ] as const
- ).map(
- ({
- value,
- icon: Icon,
- color,
- name,
- }: {
- value: string;
- icon: React.ElementType;
- color: string;
- name?: string;
- }) => (
-
setProvider(value)}
- onKeyDown={(e) => {
- if (e.key === "Enter" || e.key === " ") {
- e.preventDefault();
- setProvider(value);
- }
- }}
- className={cn(
- "inline-flex items-center gap-1.5 h-7 px-2.5 text-xs font-medium border border-border rounded-md cursor-pointer transition-colors select-none",
- "hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
- provider === value && [
- "bg-transparent",
- color === "emerald" &&
- "[&_svg]:fill-emerald-500 [&_svg]:stroke-emerald-500 bg-emerald-500/10! border-emerald-500/10! text-emerald-500!",
- color === "indigo" &&
- "[&_svg]:fill-indigo-500 [&_svg]:stroke-indigo-500 bg-indigo-500/10! border-indigo-500/10! text-indigo-500!",
- color === "amber" &&
- "[&_svg]:fill-amber-500 [&_svg]:stroke-amber-500 bg-amber-500/10! border-amber-500/10! text-amber-500!",
- ]
- )}
- >
-
- {name ?? value.charAt(0).toUpperCase() + value.slice(1)}
-
- )
- )}
-
-
-
-
-
- );
-}
diff --git a/components/ask-ai/redesign.tsx b/components/ask-ai/redesign.tsx
deleted file mode 100644
index 3cddd074da060741f6d413c5fdc31c9dba86c460..0000000000000000000000000000000000000000
--- a/components/ask-ai/redesign.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import { useState } from "react";
-import { Paintbrush } from "lucide-react";
-import { toast } from "sonner";
-
-import { Button } from "@/components/ui/button";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Input } from "@/components/ui/input";
-import Loading from "@/components/loading";
-
-export function Redesign({
- onRedesign,
-}: {
- onRedesign: (md: string, url: string) => void;
-}) {
- const [url, setUrl] = useState("");
- const [open, setOpen] = useState(false);
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState(null);
-
- const checkIfUrlIsValid = (url: string) => {
- const urlPattern = new RegExp(
- /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/,
- "i"
- );
- return urlPattern.test(url);
- };
-
- const handleClick = async () => {
- setError(null);
- if (isLoading) return;
- if (!url) {
- toast.error("Please enter a URL.");
- return;
- }
- if (!checkIfUrlIsValid(url)) {
- toast.error("Please enter a valid URL.");
- return;
- }
- setIsLoading(true);
- const response = await fetch("/api/redesign", {
- method: "PUT",
- body: JSON.stringify({ url: url.trim() }),
- headers: {
- "Content-Type": "application/json",
- },
- })
- .then(async (response) => {
- if (response?.ok) {
- const data = await response.json();
- return data;
- }
- })
- .catch((error) => {
- setError(error.message);
- });
- if (response.ok) {
- onRedesign(response.markdown, url);
- }
- setIsLoading(false);
- };
-
- return (
-
-
-
- );
-}
diff --git a/components/ask-ai/uploader.tsx b/components/ask-ai/uploader.tsx
deleted file mode 100644
index c2f5f7482790b2209222c01a827ace953ddde4ca..0000000000000000000000000000000000000000
--- a/components/ask-ai/uploader.tsx
+++ /dev/null
@@ -1,233 +0,0 @@
-import {
- CheckCircle,
- FileVideo,
- ImageIcon,
- Music,
- Paperclip,
- Video,
-} from "lucide-react";
-import { useRef, useState } from "react";
-import Image from "next/image";
-import { useParams } from "next/navigation";
-
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Button } from "@/components/ui/button";
-import { getFileType, humanizeNumber } from "@/lib/utils";
-import { useQueryClient } from "@tanstack/react-query";
-import { ProjectWithCommits } from "@/actions/projects";
-import { toast } from "sonner";
-import Loading from "../loading";
-
-export const Uploader = ({
- medias,
- selected,
- setSelected,
-}: {
- medias?: string[] | null;
- selected: string[];
- setSelected: React.Dispatch>;
-}) => {
- const queryClient = useQueryClient();
- const { repoId } = useParams<{ repoId: string }>();
-
- const [open, setOpen] = useState(false);
- const [isUploading, setIsUploading] = useState(false);
- const [error, setError] = useState(null);
- const fileInputRef = useRef(null);
-
- const getFileIcon = (url: string) => {
- const fileType = getFileType(url);
- switch (fileType) {
- case "image":
- return ;
- case "video":
- return ;
- case "audio":
- return ;
- default:
- return ;
- }
- };
-
- const uploadFiles = async (files: FileList | null) => {
- setError(null);
- if (!files || files.length === 0) return;
-
- setIsUploading(true);
- const data = new FormData();
- Array.from(files).forEach((file) => {
- data.append("images", file);
- });
-
- const response = await fetch(`/api/projects/${repoId}/medias`, {
- method: "POST",
- body: data,
- })
- .then(async (response) => {
- if (response.ok) {
- const data = await response.json();
- return data;
- }
- throw new Error("Failed to save changes");
- })
- .catch((err) => {
- return { success: false, err };
- });
-
- if (response.success) {
- queryClient.setQueryData(
- ["project"],
- (oldProject: ProjectWithCommits) => ({
- ...oldProject,
- medias: [...response.medias, ...(oldProject?.medias ?? [])],
- })
- );
- toast.success("Media files uploaded successfully!");
- } else {
- setError(
- response.err ?? "Failed to upload media files, try again later."
- );
- }
-
- setIsUploading(false);
- };
-
- return (
-
-
-
- 0 ? "xs" : "icon-xs"}
- variant={
- selected?.length > 0 ? "indigo" : open ? "default" : "bordered"
- }
- className="rounded-full! relative"
- >
-
- {selected?.length > 0 && (
-
- {humanizeNumber(selected.length)}
-
- )}
-
-
-
-
-
- {error && {error}
}
- {medias && medias.length > 0 && (
-
-
- Uploaded files:
-
-
-
- {medias.map((media: string) => {
- const fileType = getFileType(media);
- return (
-
- setSelected(
- selected.includes(media)
- ? selected.filter((f) => f !== media)
- : [...selected, media]
- )
- }
- >
- {fileType === "image" ? (
-
- ) : fileType === "video" ? (
-
- ) : fileType === "audio" ? (
-
-
-
- ) : (
-
- {getFileIcon(media)}
-
- )}
- {selected.includes(media) && (
-
-
-
- )}
-
- );
- })}
-
-
-
- )}
-
- fileInputRef.current?.click()}
- className="relative w-full"
- disabled={isUploading}
- >
- {isUploading ? (
- <>
-
- Uploading media file(s)...
- >
- ) : (
- "Upload Media Files"
- )}
-
- uploadFiles(e.target.files)}
- />
-
-
-
-
-
- );
-};
diff --git a/components/ask-ai/useGeneration.ts b/components/ask-ai/useGeneration.ts
deleted file mode 100644
index e847b86b1ab2e61469fc568b0e35b47864242176..0000000000000000000000000000000000000000
--- a/components/ask-ai/useGeneration.ts
+++ /dev/null
@@ -1,437 +0,0 @@
-"use client";
-import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useRef } from "react";
-import { toast } from "sonner";
-import { useRouter } from "next/navigation";
-import { v4 as uuidv4 } from "uuid";
-import { useLocalStorage } from "react-use";
-import { useSession } from "next-auth/react";
-
-import { formatResponse } from "@/lib/format";
-import { File, Message, MessageActionType, ProviderType } from "@/lib/type";
-import { getContextFilesFromPrompt } from "@/lib/utils";
-
-const MESSAGES_QUERY_KEY = (projectName: string) =>
- ["messages", projectName] as const;
-
-export const useGeneration = (projectName: string) => {
- const router = useRouter();
- const audio = useRef(null);
- const queryClient = useQueryClient();
- const abortController = useRef(null);
- const [, setStoredMessages] = useLocalStorage(
- `messages-${projectName}`,
- []
- );
- const { data: session } = useSession();
-
- const { data: isLoading } = useQuery({
- queryKey: ["ai.generation.isLoading"],
- queryFn: () => false,
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- refetchOnMount: false,
- staleTime: Infinity,
- });
-
- const setIsLoading = (isLoading: boolean) => {
- queryClient.setQueryData(["ai.generation.isLoading"], isLoading);
- };
-
- const getFiles = () => queryClient.getQueryData(["files"]) ?? [];
- const setFiles = (newFiles: File[]) => {
- queryClient.setQueryData(["files"], (oldFiles: File[] = []) => {
- const currentFiles = oldFiles.filter(
- (file) => !newFiles.some((f) => f.path === file.path)
- );
- return [...currentFiles, ...newFiles];
- });
- };
-
- const getMessages = () =>
- queryClient.getQueryData(
- MESSAGES_QUERY_KEY(projectName ?? "new")
- ) ?? [];
- const addMessage = (message: Omit) => {
- const id = uuidv4();
- const key = MESSAGES_QUERY_KEY(projectName ?? "new");
- queryClient.setQueryData(key, (oldMessages = []) => {
- const newMessages = [...oldMessages, { ...message, id }];
- if (projectName !== "new") {
- localStorage.setItem(
- `messages-${projectName}`,
- JSON.stringify(newMessages)
- );
- }
- return newMessages;
- });
- return id;
- };
-
- const updateLastMessage = (content: string, files?: File[]) => {
- queryClient.setQueryData(
- MESSAGES_QUERY_KEY(projectName),
- (oldMessages = []) => {
- const newMessages = [
- ...oldMessages.slice(0, -1),
- {
- ...oldMessages[oldMessages.length - 1],
- content,
- isThinking: false,
- files: files?.map((file) => file.path),
- },
- ];
- if (projectName !== "new") {
- setStoredMessages(newMessages);
- }
- return newMessages;
- }
- );
- };
-
- const updateMessage = (messageId: string, message: Partial) => {
- const key = MESSAGES_QUERY_KEY(projectName ?? "new");
- const currentMessages = queryClient.getQueryData(key);
- if (!currentMessages) return;
- const index = currentMessages.findIndex((m) => m.id === messageId);
- if (index === -1) return;
- const newMessages = [
- ...currentMessages.slice(0, index),
- { ...currentMessages[index], ...message },
- ...currentMessages.slice(index + 1),
- ];
- if (projectName !== "new") {
- setStoredMessages(newMessages);
- }
- queryClient.setQueryData(key, newMessages);
- };
-
- const storeMessages = async (newProjectName: string) => {
- return new Promise((resolve) => {
- const currentMessages = queryClient.getQueryData(
- MESSAGES_QUERY_KEY("new")
- );
- localStorage.setItem(
- `messages-${newProjectName}`,
- JSON.stringify(currentMessages)
- );
- queryClient.setQueryData(
- MESSAGES_QUERY_KEY(newProjectName),
- currentMessages
- );
- setTimeout(() => resolve(true), 100);
- });
- };
-
- const createProject = async (
- files: File[],
- projectTitle: string,
- indexMessage: string,
- prompt: string
- ) => {
- updateMessage(indexMessage, {
- actions: [
- {
- label: "Publishing on Hugging Face...",
- variant: "default",
- loading: true,
- type: MessageActionType.PUBLISH_PROJECT,
- },
- ],
- });
- try {
- const response = await fetch("/api/projects", {
- method: "POST",
- body: JSON.stringify({
- projectTitle,
- files,
- prompt,
- }),
- headers: {
- "Content-Type": "application/json",
- },
- }).then(async (response) => {
- if (response.ok) {
- const data = await response.json();
- return data;
- }
- throw new Error("Failed to publish project");
- });
-
- if (response.repoUrl) {
- toast.success("Project has been published, build in progress...");
- updateMessage(indexMessage, {
- actions: [
- {
- label: "See Live preview",
- variant: "default",
- type: MessageActionType.SEE_LIVE_PREVIEW,
- },
- ],
- });
- storeMessages(response.repoUrl).then(() => {
- router.push(`/${response.repoUrl}`);
- });
- }
- } catch (error) {
- toast.error("Failed to publish project");
- updateMessage(indexMessage, {
- actions: [
- {
- label: "Publish on Hugging Face",
- variant: "default",
- type: MessageActionType.PUBLISH_PROJECT,
- projectTitle,
- prompt,
- },
- ],
- });
- }
- };
-
- const callAi = async (
- {
- prompt,
- model,
- onComplete,
- provider = "auto",
- redesignMd,
- medias,
- }: {
- prompt: string;
- model: string;
- redesignMd?: {
- url: string;
- md: string;
- } | null;
- medias?: string[] | null;
- onComplete: () => void;
- provider?: ProviderType;
- },
- setModel: (model: string) => void
- ) => {
- setIsLoading(true);
- const messages = getMessages();
- const files = getFiles();
- const filesToUse = await getContextFilesFromPrompt(prompt, files);
- const previousMessages = [...messages]?.filter(
- (message) => !message.isAutomated || !message.isAborted
- );
- addMessage({
- role: "user",
- content: `${
- redesignMd?.url ? `Redesign: ${redesignMd.url}\n` : ""
- }${prompt}`,
- createdAt: new Date(),
- });
- addMessage({
- role: "assistant",
- isThinking: true,
- createdAt: new Date(),
- model,
- });
-
- const isFollowUp = files?.length > 0;
- abortController.current = new AbortController();
-
- const request = await fetch("/api/ask", {
- method: "POST",
- body: JSON.stringify({
- prompt,
- model,
- files: filesToUse,
- previousMessages,
- provider,
- redesignMd,
- medias,
- }),
- headers: {
- "Content-Type": "application/json",
- },
- ...(abortController.current
- ? { signal: abortController.current.signal }
- : {}),
- });
-
- const currentMessages = getMessages();
-
- if (!request.ok) {
- const jsonResponse = await request.json()?.catch(() => null);
- const errorMessage =
- jsonResponse?.error || `Status code: ${request.status}`;
-
- const lastMessageId = currentMessages[currentMessages.length - 1].id;
- updateMessage(lastMessageId, {
- isThinking: false,
- isAborted: true,
- content: `Error: ${errorMessage}`,
- });
- setIsLoading(false);
- return;
- }
-
- if (request && request.body) {
- const reader = request.body.getReader();
- const decoder = new TextDecoder();
- let completeResponse = "";
- const read = async () => {
- const { done, value } = await reader.read();
- if (done) {
- audio.current?.play();
- const files = getFiles();
- const {
- messageContent,
- files: newFiles,
- projectTitle,
- } = formatResponse(completeResponse, files ?? []);
- updateLastMessage(messageContent, newFiles);
- if (newFiles && newFiles.length > 0) {
- setFiles(newFiles);
- onComplete();
- if (projectName === "new") {
- addMessage({
- role: "assistant",
- content:
- "I've finished the generation. Now you can decide to publish the project on Hugging Face to share it!",
- createdAt: new Date(),
- isAutomated: true,
- actions: [
- {
- label: "Publish on Hugging Face",
- variant: "default",
- type: MessageActionType.PUBLISH_PROJECT,
- prompt,
- projectTitle,
- },
- ],
- });
- } else {
- const response = await fetch(
- `/api/projects/${projectName.split("/")[1]}`,
- {
- method: "PUT",
- body: JSON.stringify({
- files: newFiles,
- prompt,
- }),
- headers: {
- "Content-Type": "application/json",
- },
- }
- ).then(async (response) => {
- if (response.ok) {
- const data = await response.json();
- return data;
- }
- });
- if (response.success) {
- toast.success("Project has been updated");
- } else {
- toast.error("Failed to update project");
- }
- }
- }
-
- setIsLoading(false);
- return;
- }
- const chunk = decoder.decode(value, { stream: true });
- completeResponse += chunk;
-
- if (completeResponse.includes("__ERROR__:")) {
- const errorMatch = completeResponse.match(/__ERROR__:(.+)/);
- if (errorMatch) {
- try {
- const errorData = JSON.parse(errorMatch[1]);
- if (errorData.isError) {
- const lastMessageId =
- currentMessages[currentMessages.length - 1].id;
- updateMessage(lastMessageId, {
- isThinking: false,
- isAborted: true,
- content: errorData?.showProMessage
- ? session?.user?.isPro ? "You have already reached your monthly included credits with Hugging Face Pro plan. Please consider adding more credits to your account." : "You have exceeded your monthly included credits with Hugging Face inference provider. Please consider upgrading to a pro plan."
- : `Error: ${errorData.messageError}`,
- actions: errorData?.showProMessage
- ? session?.user?.isPro ? [{
- label: "Add more credits",
- variant: "default",
- type: MessageActionType.ADD_CREDITS,
- }] : [
- {
- label: "Upgrade to Pro",
- variant: "pro",
- type: MessageActionType.UPGRADE_TO_PRO,
- },
- ]
- : [],
- });
- setIsLoading(false);
- return;
- }
- } catch (e) {
- console.error("Failed to parse error message:", e);
- }
- }
- }
- if (
- completeResponse.includes(
- "_Note: The selected model was not available. Switched to"
- )
- ) {
- const newModel = completeResponse
- .match(
- /The selected model was not available. Switched to (.+)/
- )?.[1]
- .replace(/`/g, "")
- .replace(" ", "")
- .replace(/\.|_$/g, "");
- if (newModel) {
- setModel(newModel);
- updateMessage(currentMessages[currentMessages.length - 1].id, {
- model: newModel,
- });
- }
- }
-
- const files = getFiles();
- const { messageContent, files: newFiles } = formatResponse(
- completeResponse,
- files ?? []
- );
- if (messageContent) updateLastMessage(messageContent);
- if (newFiles && newFiles.length > 0) {
- if (!isFollowUp) {
- setFiles(newFiles);
- }
- updateLastMessage(messageContent, newFiles);
- }
- read();
- };
- return await read();
- }
- };
-
- const stopGeneration = () => {
- if (abortController.current) {
- abortController.current.abort();
- abortController.current = null;
- setIsLoading(false);
- const currentMessages = getMessages();
- const lastMessageId = currentMessages[currentMessages.length - 1].id;
- updateMessage(lastMessageId, {
- isAborted: true,
- isThinking: false,
- });
- }
- };
-
- return {
- callAi,
- isLoading,
- stopGeneration,
- files: getFiles(),
- createProject,
- audio,
- };
-};
diff --git a/components/chat/index.tsx b/components/chat/index.tsx
deleted file mode 100644
index ececa27bb2dccd800b7796ebefe7cde50360e441..0000000000000000000000000000000000000000
--- a/components/chat/index.tsx
+++ /dev/null
@@ -1,377 +0,0 @@
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
-import { useSession } from "next-auth/react";
-import { cn } from "@/lib/utils";
-import { ChevronRight, ExternalLink } from "lucide-react";
-import { useEffect, useRef, useState } from "react";
-import Image from "next/image";
-import Markdown from "react-markdown";
-import { useQueryClient } from "@tanstack/react-query";
-import { formatDistanceToNow } from "date-fns";
-import { SpaceEntry } from "@huggingface/hub";
-import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
-import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism";
-
-import { useChat } from "./useChat";
-import { AiLoading } from "@/components/ask-ai/loading";
-import Loading from "@/components/loading";
-import { useGeneration } from "@/components/ask-ai/useGeneration";
-import { MessageAction, MessageActionType, File } from "@/lib/type";
-import { Button } from "@/components/ui/button";
-import { getFileIcon } from "@/components/ask-ai/input-mentions";
-import ProIcon from "@/assets/pro.svg";
-import { ProModal } from "../pro-modal";
-
-const ASSISTANT_AVATAR_URL = "https://i.imgur.com/Ho6v0or.jpeg";
-
-export function AppEditorChat({
- isNew,
- projectName,
- onSelectFile,
-}: {
- isNew?: boolean;
- projectName?: string;
- onSelectFile: (file: string) => void;
-}) {
- const chatContainerRef = useRef(null);
- const { data: session } = useSession();
- const queryClient = useQueryClient();
- const chatProjectName = isNew ? "new" : projectName ?? "new";
- const { messages } = useChat(chatProjectName);
- const { isLoading, createProject } = useGeneration(chatProjectName);
- const [openProModal, setOpenProModal] = useState(false);
-
- const project = queryClient.getQueryData(["project"]);
-
- const handleShowLastFile = (files?: string[]) => {
- if (files && files.length > 0) {
- const lastGeneratedFile = files[files.length - 1];
- if (lastGeneratedFile) {
- onSelectFile?.(lastGeneratedFile);
- }
- }
- };
- const handleActions = (action: MessageAction, messageId: string) => {
- if (!action) return;
- switch (action.type) {
- case MessageActionType.PUBLISH_PROJECT:
- const files = queryClient.getQueryData(["files"]) ?? [];
- return createProject(
- files ?? [],
- action.projectTitle ?? "",
- messageId,
- action.prompt ?? ""
- );
- case MessageActionType.SEE_LIVE_PREVIEW:
- return window.open(
- `https://huggingface.co/spaces/${project?.name}`,
- "_blank"
- );
- case MessageActionType.UPGRADE_TO_PRO:
- return setOpenProModal(true);
- case MessageActionType.ADD_CREDITS:
- return window.open(
- "https://huggingface.co/settings/billing?add-credits=true",
- "_blank"
- );
- }
- };
-
- useEffect(() => {
- if (chatContainerRef.current) {
- chatContainerRef.current.scrollTop =
- chatContainerRef.current.scrollHeight;
- }
- }, [messages]);
-
- return (
-
- {isNew ? (
-
- Start a new conversation with the AI
-
- ) : (
-
- Your last conversation has not been restored.
-
- )}
-
- {messages.map((message, id) => (
-
-
-
-
- {message.role === "user"
- ? session?.user?.name?.charAt(0) ?? ""
- : ""}
-
-
-
-
-
(
- {children}
- ),
- code: ({ className, children }) => {
- const isCodeBlock = className?.startsWith("language-");
- const language = className?.replace("language-", "");
- if (isCodeBlock) {
- return (
-
- {String(children).trim()}
-
- );
- }
-
- return (
-
- {children}
-
- );
- },
- ul: ({ children }) => (
-
- ),
- ol: ({ children }) => (
-
- {children}
-
- ),
- li: ({ children }) => (
- {children}
- ),
- a: ({ children, href }) => {
- const isValidUrl =
- href &&
- (href.startsWith("http://") ||
- href.startsWith("https://") ||
- href.startsWith("/") ||
- href.startsWith("#") ||
- href.startsWith("mailto:") ||
- href.startsWith("tel:"));
- const safeHref = isValidUrl ? href : "#";
-
- return (
-
-
- {children}
-
- );
- },
- pre: ({ children }) => <>{children}>,
- p: ({ children }) => {
- const content = String(children);
- if (
- typeof children === "string" &&
- (content.includes("file:/") ||
- content.match(/https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|webp|svg|bmp|ico)(?:\?[^\s]*)?/i))
- ) {
- const parts = content.split(/(file:\/\S+|https?:\/\/[^\s]+\.(?:jpg|jpeg|png|gif|webp|svg|bmp|ico)(?:\?[^\s]*)?)/gi);
- return (
-
- {parts.filter(Boolean).map((part, index) => {
- if (!part || part.trim() === "") return null;
-
- if (part.startsWith("file:/")) {
- return (
-
- {getFileIcon(part, "size-2.5")}
- {part.replace("file:/", "")}
-
- );
- } else if (
- part.match(
- /^https?:\/\/[^\s]+\.(?:jpg|jpeg|png|gif|webp|svg|bmp|ico)(?:\?[^\s]*)?$/i
- )
- ) {
- const displayUrl =
- part.length > 35
- ? part.substring(0, 35) + "..."
- : part;
- return (
-
- 🖼️
- {displayUrl}
-
- );
- }
- return part;
- })}
-
- );
- }
- return {children}
;
- },
- }}
- >
- {message.content}
-
- {message.isThinking && (
-
- )}
- {message.isAborted && (
-
- The request has been aborted due to an error OR the user has
- stopped the generation.
-
- )}
-
- {message.model && message.createdAt && (
-
-
- via{" "}
-
- {message.model}
-
-
- {!message.isThinking && (
-
- {formatDistanceToNow(message.createdAt, {
- addSuffix: true,
- })}
-
- )}
-
- )}
- {message.files && message.files.length > 0 && (
-
-
- {isLoading &&
- messages[messages.length - 1].id === message.id ? (
- <>
-
-
- {isNew ? "Creating" : "Editing"}{" "}
-
-
- >
- ) : (
- <>
-
- {isNew ? "Created" : "Edited"}{" "}
-
- {message.files.length > 1 && (
-
- (+ {message.files.length - 1} files)
-
- )}
-
-
handleShowLastFile(message.files)}
- >
-
-
- >
- )}
-
-
- )}
- {message.actions &&
- message.actions.length > 0 &&
- messages.length - 1 === id && (
-
- {message.actions.map((action, id) => (
- handleActions(action, message.id)}
- >
- {action.loading && (
-
- )}
- {action.type === MessageActionType.UPGRADE_TO_PRO && (
-
- )}
- {action.label}
-
- ))}
-
- )}
-
-
- ))}
-
-
-
- );
-}
-
-const FileCode = ({ file }: { file: string }) => (
-
- {file}
-
-);
diff --git a/components/chat/useChat.ts b/components/chat/useChat.ts
deleted file mode 100644
index 2f32019379f2fed9411cae356d5ad7b50e4ef7d5..0000000000000000000000000000000000000000
--- a/components/chat/useChat.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useLocalStorage } from "react-use";
-import { useEffect } from "react";
-import { v4 as uuidv4 } from "uuid";
-
-import { Message } from "@/lib/type";
-
-const MESSAGES_QUERY_KEY = (projectName: string) =>
- ["messages", projectName] as const;
-
-export function useChat(projectName: string) {
- const queryClient = useQueryClient();
- const [, setStoredMessages, clearStoredMessages] = useLocalStorage(
- `messages-${projectName}`,
- []
- );
-
- useEffect(() => {
- if (projectName !== "new") {
- queryClient.invalidateQueries({
- queryKey: MESSAGES_QUERY_KEY(projectName),
- });
- }
- }, [projectName, queryClient]);
-
- const { data: messages = [] } = useQuery({
- queryKey: MESSAGES_QUERY_KEY(projectName),
- queryFn: () => {
- if (projectName === "new") {
- return [];
- }
- const storedData = localStorage.getItem(`messages-${projectName}`);
- if (storedData) {
- try {
- const parsedMessages = JSON.parse(storedData);
- if (parsedMessages && parsedMessages.length > 0) {
- return parsedMessages;
- }
- } catch (error) {
- console.error("Failed to parse stored messages:", error);
- }
- }
- return [];
- },
- refetchOnMount: false,
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- refetchInterval: false,
- refetchIntervalInBackground: false,
- });
-
- const addMessage = (message: Omit) => {
- const id = uuidv4();
- queryClient.setQueryData(
- MESSAGES_QUERY_KEY(projectName),
- (oldMessages = []) => {
- const newMessages = [
- ...oldMessages,
- {
- ...message,
- id,
- },
- ];
- if (projectName !== "new") {
- setStoredMessages(newMessages);
- }
- return newMessages;
- }
- );
- return id;
- };
- const clearMessages = () => {
- queryClient.setQueryData(MESSAGES_QUERY_KEY(projectName), []);
- clearStoredMessages();
- };
-
- const storeMessages = async (newProjectName: string) => {
- return new Promise((resolve) => {
- const currentMessages = queryClient.getQueryData(
- MESSAGES_QUERY_KEY("new")
- );
- localStorage.setItem(
- `messages-${newProjectName}`,
- JSON.stringify(currentMessages)
- );
- queryClient.setQueryData(
- MESSAGES_QUERY_KEY(newProjectName),
- currentMessages
- );
- setTimeout(() => resolve(true), 100);
- });
- };
-
- return {
- messages,
- addMessage,
- clearMessages,
- storeMessages,
- };
-}
diff --git a/components/code/index.tsx b/components/code/index.tsx
deleted file mode 100644
index 6b6b687f828b7bf8a5bd66afb958552132281f2f..0000000000000000000000000000000000000000
--- a/components/code/index.tsx
+++ /dev/null
@@ -1,212 +0,0 @@
-import { useState } from "react";
-import {
- SandpackLayout,
- SandpackFileExplorer,
-} from "@codesandbox/sandpack-react";
-import { ChevronRight, ChevronLeft } from "lucide-react";
-import { useParams } from "next/navigation";
-import { useQueryClient } from "@tanstack/react-query";
-import { format } from "date-fns";
-import { useUpdateEffect } from "react-use";
-
-import { AppEditorMonacoEditor } from "./monaco-editor";
-import { Button } from "@/components/ui/button";
-import { cn } from "@/lib/utils";
-import Loading from "../loading";
-import { ProjectWithCommits } from "@/actions/projects";
-
-export function AppEditorCode() {
- const queryClient = useQueryClient();
- const { repoId } = useParams<{ repoId: string }>();
-
- const [isFileExplorerCollapsed, setIsFileExplorerCollapsed] = useState(true);
- const [isSavingChanges, setIsSavingChanges] = useState(false);
- const [isSavingChangesSuccess, setIsSavingChangesSuccess] = useState(false);
- const [isSavingChangesError, setIsSavingChangesError] = useState(false);
- const [isClosing, setIsClosing] = useState(false);
- const [showSaveChanges, setShowSaveChanges] = useState(false);
-
- const handleSaveChanges = async () => {
- if (repoId === "new") return;
- setIsSavingChanges(true);
- const manuallyUpdatedFiles =
- queryClient.getQueryData(["manuallyUpdatedFiles"]) ?? [];
- const response = await fetch(`/api/projects/${repoId}`, {
- method: "PUT",
- body: JSON.stringify({
- files: manuallyUpdatedFiles,
- prompt: `✍️ ${format(new Date(), "dd/MM")} - ${format(
- new Date(),
- "HH:mm"
- )} - Manual changes.`,
- isManualChanges: true,
- }),
- }).then(async (response) => {
- if (response.ok) {
- const data = await response.json();
- return data;
- }
- throw new Error("Failed to save changes");
- });
- if (response.success) {
- setIsSavingChangesSuccess(true);
- queryClient.invalidateQueries({ queryKey: ["manuallyUpdatedFiles"] });
- queryClient.setQueryData(
- ["project"],
- (oldProject: ProjectWithCommits) => ({
- ...oldProject,
- commits: [response.commit, ...(oldProject?.commits ?? [])],
- })
- );
- } else {
- setIsSavingChangesError(true);
- }
- setIsSavingChanges(false);
- setShowSaveChanges(false);
- };
-
- const undoChanges = () => {
- queryClient.setQueryData(["manuallyUpdatedFiles"], []);
- setShowSaveChanges(false);
- };
-
- useUpdateEffect(() => {
- if (isSavingChangesSuccess) {
- setTimeout(() => setIsSavingChangesSuccess(false), 3000);
- }
- }, [isSavingChangesSuccess]);
-
- useUpdateEffect(() => {
- if (isClosing) {
- setTimeout(() => {
- setIsSavingChangesSuccess(false);
- setIsSavingChangesError(false);
- setIsClosing(false);
- }, 300);
- }
- }, [isClosing]);
-
- return (
-
-
- setIsFileExplorerCollapsed(!isFileExplorerCollapsed)}
- title={
- isFileExplorerCollapsed
- ? "Expand file explorer"
- : "Collapse file explorer"
- }
- >
- {isFileExplorerCollapsed ? (
-
- ) : (
-
- )}
-
-
-
-
- {repoId && (
-
-
-
-
- {isSavingChangesSuccess
- ? "Changes saved"
- : isSavingChangesError
- ? "Failed to save changes"
- : "Save Changes"}
-
-
- {isSavingChangesSuccess
- ? "Changes saved successfully"
- : isSavingChangesError
- ? "Something went wrong, please try again."
- : "You have unsaved manual changes. Click the button to save your changes."}
-
-
-
- {!isSavingChangesSuccess && !isSavingChanges && (
-
- Undo
-
- )}
-
- {isSavingChangesSuccess || isSavingChangesError ? (
- setIsClosing(true)}
- >
- Close
-
- ) : (
-
- Save Changes
- {isSavingChanges && (
-
- )}
-
- )}
-
-
-
- )}
-
- );
-}
diff --git a/components/code/monaco-editor.tsx b/components/code/monaco-editor.tsx
deleted file mode 100644
index d222ce260193d76d9da71b784259a01653782381..0000000000000000000000000000000000000000
--- a/components/code/monaco-editor.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-import { useMemo, useRef, useCallback, useEffect } from "react";
-import { useQueryClient } from "@tanstack/react-query";
-import { Monaco } from "@monaco-editor/react";
-import {
- useActiveCode,
- SandpackStack,
- FileTabs,
- useSandpack,
-} from "@codesandbox/sandpack-react";
-import { useTheme } from "next-themes";
-
-import NightLight from "@/components/editor/night-light.json";
-import Night from "@/components/editor/night.json";
-import { File } from "@/lib/type";
-import dynamic from "next/dynamic";
-
-const Editor = dynamic(
- () => import("@monaco-editor/react").then((mod) => mod.Editor),
- { ssr: false }
-);
-
-const LANGUAGE_MAP = {
- js: "javascript",
- ts: "typescript",
- html: "html",
- css: "css",
- json: "json",
- txt: "text",
-};
-
-export function AppEditorMonacoEditor({
- setShowSaveChanges,
-}: {
- setShowSaveChanges: (show: boolean) => void;
-}) {
- const { theme } = useTheme();
- const { code, updateCode } = useActiveCode();
- const { sandpack } = useSandpack();
- const queryClient = useQueryClient();
- const updateTimeoutRef = useRef(null);
-
- const handleEditorDidMount = (monaco: Monaco) => {
- monaco.editor.defineTheme("NightLight", {
- base: "vs",
- inherit: true,
- ...NightLight,
- rules: [],
- });
- monaco.editor.defineTheme("Night", {
- base: "vs-dark",
- ...Night,
- rules: [],
- });
- };
-
- const language = useMemo(() => {
- const extension = sandpack.activeFile
- .split(".")
- .pop()
- ?.toLowerCase() as string;
- return LANGUAGE_MAP[extension as keyof typeof LANGUAGE_MAP] ?? "text";
- }, [sandpack.activeFile]);
-
- const updateFile = useCallback(
- (newValue: string, activeFile: string) => {
- if (updateTimeoutRef.current) {
- clearTimeout(updateTimeoutRef.current);
- }
-
- updateTimeoutRef.current = setTimeout(() => {
- const manuallyUpdatedFiles =
- queryClient.getQueryData(["manuallyUpdatedFiles"]) ?? [];
- const fileIndex = manuallyUpdatedFiles.findIndex(
- (file) => file.path === activeFile
- );
- if (fileIndex !== -1) {
- manuallyUpdatedFiles[fileIndex].content = newValue;
- } else {
- manuallyUpdatedFiles.push({
- path: activeFile,
- content: newValue,
- });
- }
- queryClient.setQueryData(
- ["manuallyUpdatedFiles"],
- manuallyUpdatedFiles
- );
- }, 100);
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [queryClient]
- );
-
- const handleEditorChange = (value: string | undefined) => {
- setShowSaveChanges(true);
- const newValue = value || "";
- updateCode(newValue);
- const activeFile = sandpack.activeFile?.replace(/^\//, "");
- updateFile(newValue, activeFile);
- };
-
- useEffect(() => {
- return () => {
- if (updateTimeoutRef.current) {
- clearTimeout(updateTimeoutRef.current);
- }
- };
- }, []);
-
- const themeEditor = useMemo(() => {
- const isSystemDark =
- window.matchMedia &&
- window.matchMedia("(prefers-color-scheme: dark)").matches;
- const effectiveTheme =
- theme === "system" ? (isSystemDark ? "dark" : "light") : theme;
- return effectiveTheme === "dark" ? "Night" : "NightLight";
- }, [theme]);
-
- return (
-
-
-
-
-
-
- );
-}
diff --git a/components/code/useEditor.ts b/components/code/useEditor.ts
deleted file mode 100644
index 06ed500729a17413283a42c8f9bf395f255b1518..0000000000000000000000000000000000000000
--- a/components/code/useEditor.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useProject } from "@/components/projects/useProject";
-import { useMemo } from "react";
-
-const LANGUAGE_MAP = {
- "py": "python",
- "js": "javascript",
- "ts": "typescript",
- "html": "html",
- "css": "css",
- "json": "json",
- "txt": "text",
-}
-
-export const useEditor = () => {
- const queryClient = useQueryClient();
- const { files } = useProject();
-
- const { data: isFileListOpen } = useQuery({
- queryKey: ["isFileListOpen"],
- queryFn: () => {
- return false;
- },
- initialData: false,
- refetchInterval: false,
- refetchOnWindowFocus: false,
- });
-
- const setIsFileListOpen = (isOpen: boolean) => {
- queryClient.setQueryData(["isFileListOpen"], () => isOpen);
- }
-
- const { data: editorFilePath } = useQuery({
- queryKey: ["editorFile"],
- queryFn: () => {
- return "app.py";
- },
- initialData: "app.py",
- refetchInterval: false,
- refetchOnWindowFocus: false,
- refetchOnMount: false,
- refetchOnReconnect: false,
- refetchIntervalInBackground: false,
- });
-
- const setEditorFilePath = (path: string) => {
- queryClient.setQueryData(["editorFile"], () => path);
- }
-
- const editorFileData = useMemo(() => {
- const finalFile = { path: "", content: "", language: "" };
- if (editorFilePath) {
- const file = files?.find((file) => file.path === editorFilePath);
- if (file) {
- finalFile.path = file.path;
- finalFile.content = file.content ?? "";
- finalFile.language = LANGUAGE_MAP[file.path.split(".").pop()?.toLowerCase() as keyof typeof LANGUAGE_MAP] ?? "text";
- }
- }
-
- return finalFile
- }, [editorFilePath, files])
-
- return {
- editorFilePath,
- editorFileData,
- setEditorFilePath,
- isFileListOpen,
- setIsFileListOpen,
- }
-}
\ No newline at end of file
diff --git a/components/editor/commits.tsx b/components/editor/commits.tsx
deleted file mode 100644
index 832b10e3649e33ff648bb4d408b49c33264bfe04..0000000000000000000000000000000000000000
--- a/components/editor/commits.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import { useRef, useState } from "react";
-import { HistoryIcon } from "lucide-react";
-import { useSearchParams } from "next/navigation";
-import { useUpdateEffect } from "react-use";
-
-import { Button } from "@/components/ui/button";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { Commit } from "@/lib/type";
-import { cn, humanizeNumber } from "@/lib/utils";
-
-export const Commits = function ({ commits }: { commits?: Commit[] }) {
- const searchParams = useSearchParams();
- const commitParam = searchParams.get("commit");
-
- const commitsContainer = useRef(null);
- const commitSelected = useRef(null);
- const [open, setOpen] = useState(false);
- const [selectedCommit, setSelectedCommit] = useState(
- commitParam
- ? commitParam
- : commits && commits.length > 0
- ? commits[0]?.oid
- : null
- );
-
- const handleViewCommit = (commitId: string) => {
- let url = window.location.pathname;
- if (commitId !== commits?.[0].oid) {
- url += `?commit=${encodeURIComponent(commitId)}`;
- }
- window.location.replace(url);
- };
-
- useUpdateEffect(() => {
- if (open) {
- setTimeout(() => {
- if (commitSelected.current && commitsContainer.current) {
- const container = commitsContainer.current;
- const selected = commitSelected.current;
- if (!container || !selected) return;
- const offsetTop = selected.offsetTop;
- const offsetHeight = selected.offsetHeight;
- const containerHeight = container.offsetHeight;
- const scrollTop = offsetTop - containerHeight / 2 + offsetHeight / 2;
- container.scrollTo({
- top: scrollTop,
- behavior: "smooth",
- });
- }
- }, 200);
- }
- }, [open]);
-
- useUpdateEffect(() => {
- if (selectedCommit !== commits?.[0].oid && !commitParam) {
- setSelectedCommit(commits ? commits[0]?.oid : null);
- }
- }, [commits]);
-
- return (
-
-
-
- );
-};
diff --git a/components/editor/header.tsx b/components/editor/header.tsx
deleted file mode 100644
index 18f7b07d20c2729add9ed439081a545f75ad2566..0000000000000000000000000000000000000000
--- a/components/editor/header.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import {
- Earth,
- RefreshCw,
- Monitor,
- Smartphone,
- Code,
- MessageCircle,
- ExternalLink,
-} from "lucide-react";
-import Link from "next/link";
-import { motion } from "framer-motion";
-import { useSandpackNavigation } from "@codesandbox/sandpack-react";
-
-import { Button } from "@/components/ui/button";
-import { ActivityType, DeviceType, MobileTabType } from "@/lib/type";
-import { UserMenu } from "@/components/user-menu";
-import { useProject } from "@/components/projects/useProject";
-import { cn } from "@/lib/utils";
-import { Commits } from "./commits";
-import { ProjectSettings } from "./project-settings";
-
-export function AppEditorHeader({
- currentActivity,
- onToggleActivity,
- onToggleDevice,
- device,
- mobileTab,
- isHistoryView,
-}: {
- isNew?: boolean;
- isHistoryView?: boolean;
- currentActivity: ActivityType;
- onToggleActivity: (activity: ActivityType) => void;
- onToggleDevice: () => void;
- device: DeviceType;
- mobileTab: MobileTabType;
- onToggleMobileTab: (tab: MobileTabType) => void;
-}) {
- const { project, files } = useProject();
- const { refresh } = useSandpackNavigation();
- const [isRefreshing, setIsRefreshing] = useState(false);
- const [isHovered, setIsHovered] = useState(false);
-
- const handleRefresh = () => {
- setIsRefreshing(true);
- setTimeout(() => {
- refresh();
- setIsRefreshing(false);
- }, 1000);
- };
-
- const handleToggleActivity = () => {
- onToggleActivity(currentActivity === "chat" ? "code" : "chat");
- };
-
- return (
-
- );
-}
diff --git a/components/editor/history-view.tsx b/components/editor/history-view.tsx
deleted file mode 100644
index a2c21ce0cc86654dbb5f34ac2ca385cd052ac379..0000000000000000000000000000000000000000
--- a/components/editor/history-view.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { ArrowLeft } from "lucide-react";
-import { useParams, useSearchParams } from "next/navigation";
-import { toast } from "sonner";
-
-export const HistoryView = function () {
- const { repoId } = useParams<{ repoId: string }>();
- const searchParams = useSearchParams();
- const commitId = searchParams.get("commit");
-
- const handleSetAsDefault = async () => {
- const confirmation = confirm(
- "This action will set this historical version as the default version of the project. Are you sure you want to proceed?"
- );
- if (!confirmation) return;
- const response = await fetch(`/api/projects/${repoId}/${commitId}`, {
- method: "POST",
- }).then(async (response) => {
- if (response.ok) {
- const data = await response.json();
- return data;
- }
- throw new Error("Failed to save changes");
- });
- if (response.success) {
- toast.success("Set as default version successfully!");
- setTimeout(() => {
- close();
- }, 500);
- } else {
- alert("Failed to set as default version, try again later.");
- }
- };
-
- const close = () => {
- window.location.replace(window.location.pathname);
- };
-
- return (
-
-
-
-
-
close()}
- >
-
- Go Back
-
-
- Set as Default Version
-
-
-
-
- );
-};
diff --git a/components/editor/index.tsx b/components/editor/index.tsx
deleted file mode 100644
index b2d203bd0ff65e3eb7fe0191155d94de0ba3db51..0000000000000000000000000000000000000000
--- a/components/editor/index.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-"use client";
-import { useState, useMemo } from "react";
-import dynamic from "next/dynamic";
-import { useBeforeUnload, useLocalStorage } from "react-use";
-import { ArrowRight } from "lucide-react";
-import { useTheme } from "next-themes";
-import { amethyst } from "@codesandbox/sandpack-themes";
-import { NextStep } from "nextstepjs";
-import confetti from "canvas-confetti";
-
-import { AppEditorHeader } from "./header";
-import { AppViewer } from "@/components/viewer";
-import { ActivityType, DeviceType, File, MobileTabType } from "@/lib/type";
-import { useProject } from "@/components/projects/useProject";
-import { AskAI } from "@/components/ask-ai/ask-ai";
-import { AppEditorChat } from "@/components/chat";
-import { cn, defaultHTML } from "@/lib/utils";
-import { Button } from "@/components/ui/button";
-import { AppEditorCode } from "@/components/code";
-import { ProjectWithCommits } from "@/actions/projects";
-import { HistoryView } from "./history-view";
-import { TourCustomCard } from "@/components/tour/card";
-import { steps } from "@/lib/onboarding";
-
-const SandpackProvider = dynamic(
- () =>
- import("@codesandbox/sandpack-react").then((mod) => mod.SandpackProvider),
- { ssr: false }
-);
-
-export function AppEditor({
- project: initialProject,
- files: initialFiles,
- initialPrompt,
- isNew = false,
- isHistoryView = false,
-}: {
- project?: ProjectWithCommits | null;
- files?: File[];
- initialPrompt?: string;
- isNew?: boolean;
- isHistoryView?: boolean;
-}) {
- const [currentActivity, setCurrentActivity] = useState("chat");
- const [device, setDevice] = useState("desktop");
- const [mobileTab, setMobileTab] = useState("left-sidebar");
- const { project, files } = useProject(initialProject, initialFiles, isNew);
- const { resolvedTheme } = useTheme();
- const projectName = isNew ? "new" : project?.name ?? "new";
-
- const toggleDevice = () => {
- setDevice((prev) => (prev === "desktop" ? "mobile" : "desktop"));
- };
- const [tourHasBeenShown, setTourHasBeenShown] = useLocalStorage(
- "tour-has-been-shown",
- false
- );
-
- useBeforeUnload(
- !project && (files?.length ?? 0) > 0,
- "You have unsaved changes, are you sure?"
- );
-
- const sandpackFiles = useMemo(() => {
- if (files && files.length > 0) {
- return files.reduce(
- (acc, file) => ({
- ...acc,
- [file.path]: { code: file.content ?? "" },
- }),
- {}
- );
- } else {
- return {
- "/index.html": {
- code: defaultHTML,
- },
- };
- }
- }, [files]);
-
- return (
- {
- setTourHasBeenShown(true);
- confetti({
- particleCount: 100,
- spread: 70,
- origin: { y: 1, x: 0.1 },
- });
- }}
- >
-
-
-
-
- {currentActivity === "chat" ? (
-
-
{
- setCurrentActivity("code");
- }}
- />
-
-
-
setMobileTab("right-sidebar")}
- >
- Go to Preview
-
-
-
-
-
- ) : (
-
- )}
- {isHistoryView &&
}
-
-
-
-
-
- );
-}
diff --git a/components/editor/night-light.json b/components/editor/night-light.json
deleted file mode 100644
index 77ed5495abb7d663d2d2002a5a750080ba6ed679..0000000000000000000000000000000000000000
--- a/components/editor/night-light.json
+++ /dev/null
@@ -1,1728 +0,0 @@
-{
- "name": "Night Owl Light",
- "type": "light",
- "semanticHighlighting": true,
- "colors": {
- "foreground": "#403f53",
- "focusBorder": "#93A1A1",
- "errorForeground": "#403f53",
- "selection.background": "#7a8181ad",
- "descriptionForeground": "#403f53",
- "widget.shadow": "#d9d9d9",
- "titleBar.activeBackground": "#F0F0F0",
- "notifications.background": "#F0F0F0",
- "notifications.foreground": "#403f53",
- "notificationLink.foreground": "#994cc3",
- "notifications.border": "#CCCCCC",
- "notificationCenter.border": "#CCCCCC",
- "notificationToast.border": "#CCCCCC",
- "notificationCenterHeader.foreground": "#403f53",
- "notificationCenterHeader.background": "#F0F0F0",
- "button.background": "#2AA298",
- "button.foreground": "#F0F0F0",
- "dropdown.background": "#F0F0F0",
- "dropdown.foreground": "#403f53",
- "dropdown.border": "#d9d9d9",
- "input.background": "#F0F0F0",
- "input.foreground": "#403f53",
- "input.border": "#d9d9d9",
- "input.placeholderForeground": "#93A1A1",
- "inputOption.activeBorder": "#2AA298",
- "inputValidation.infoBorder": "#D0D0D0",
- "inputValidation.infoBackground": "#F0F0F0",
- "inputValidation.warningBackground": "#daaa01",
- "inputValidation.warningBorder": "#E0AF02",
- "inputValidation.errorBackground": "#f76e6e",
- "inputValidation.errorBorder": "#de3d3b",
- "badge.background": "#2AA298",
- "badge.foreground": "#F0F0F0",
- "progressBar.background": "#2AA298",
- "list.activeSelectionBackground": "#d3e8f8",
- "list.activeSelectionForeground": "#403f53",
- "list.inactiveSelectionBackground": "#E0E7EA",
- "list.inactiveSelectionForeground": "#403f53",
- "list.focusBackground": "#d3e8f8",
- "list.hoverBackground": "#d3e8f8",
- "list.focusForeground": "#403f53",
- "list.hoverForeground": "#403f53",
- "list.highlightForeground": "#403f53",
- "list.errorForeground": "#E64D49",
- "list.warningForeground": "#daaa01",
- "activityBar.background": "#F0F0F0",
- "activityBar.foreground": "#403f53",
- "activityBar.dropBackground": "#D0D0D0",
- "activityBarBadge.background": "#403f53",
- "activityBarBadge.foreground": "#F0F0F0",
- "activityBar.border": "#F0F0F0",
- "sideBar.background": "#F0F0F0",
- "sideBar.foreground": "#403f53",
- "sideBarTitle.foreground": "#403f53",
- "sideBar.border": "#F0F0F0",
- "scrollbar.shadow": "#CCCCCC",
- "tab.border": "#F0F0F0",
- "tab.activeBackground": "#F6F6F6",
- "tab.activeForeground": "#403f53",
- "tab.inactiveForeground": "#403f53",
- "tab.inactiveBackground": "#F0F0F0",
- "editorGroup.border": "#F0F0F0",
- "editorGroup.background": "#F6F6F6",
- "editorGroupHeader.tabsBackground": "#F0F0F0",
- "editorGroupHeader.tabsBorder": "#F0F0F0",
- "editorGroupHeader.noTabsBackground": "#F0F0F0",
- "tab.activeModifiedBorder": "#2AA298",
- "tab.inactiveModifiedBorder": "#93A1A1",
- "tab.unfocusedActiveModifiedBorder": "#93A1A1",
- "tab.unfocusedInactiveModifiedBorder": "#93A1A1",
- "editor.background": "#FBFBFB",
- "editor.foreground": "#403f53",
- "editorCursor.foreground": "#90A7B2",
- "editorLineNumber.foreground": "#90A7B2",
- "editorLineNumber.activeForeground": "#403f53",
- "editor.selectionBackground": "#E0E0E0",
- "editor.selectionHighlightBackground": "#339cec33",
- "editor.wordHighlightBackground": "#339cec33",
- "editor.wordHighlightStrongBackground": "#007dd659",
- "editor.findMatchBackground": "#93A1A16c",
- "editor.findMatchHighlightBackground": "#93a1a16c",
- "editor.findRangeHighlightBackground": "#7497a633",
- "editor.hoverHighlightBackground": "#339cec33",
- "editor.lineHighlightBackground": "#F0F0F0",
- "editor.rangeHighlightBackground": "#000000",
- "editorWhitespace.foreground": "#d9d9d9",
- "editorIndentGuide.background": "#d9d9d9",
- "editorCodeLens.foreground": "#403f53",
- "editorError.foreground": "#E64D49",
- "editorError.border": "#FBFBFB",
- "editorWarning.foreground": "#daaa01",
- "editorWarning.border": "#daaa01",
- "editorGutter.addedBackground": "#49d0c5",
- "editorGutter.modifiedBackground": "#6fbef6",
- "editorGutter.deletedBackground": "#f76e6e",
- "editorRuler.foreground": "#d9d9d9",
- "editorOverviewRuler.errorForeground": "#E64D49",
- "editorOverviewRuler.warningForeground": "#daaa01",
- "editorInlayHint.background": "#F0F0F0",
- "editorInlayHint.foreground": "#403f53",
- "editorWidget.background": "#F0F0F0",
- "editorWidget.border": "#d9d9d9",
- "editorSuggestWidget.background": "#F0F0F0",
- "editorSuggestWidget.foreground": "#403f53",
- "editorSuggestWidget.highlightForeground": "#403f53",
- "editorSuggestWidget.selectedBackground": "#d3e8f8",
- "editorSuggestWidget.border": "#d9d9d9",
- "editorHoverWidget.background": "#F0F0F0",
- "editorHoverWidget.border": "#d9d9d9",
- "debugExceptionWidget.background": "#F0F0F0",
- "debugExceptionWidget.border": "#d9d9d9",
- "editorMarkerNavigation.background": "#D0D0D0",
- "editorMarkerNavigationError.background": "#f76e6e",
- "editorMarkerNavigationWarning.background": "#daaa01",
- "debugToolBar.background": "#F0F0F0",
- "pickerGroup.border": "#d9d9d9",
- "pickerGroup.foreground": "#403f53",
- "extensionButton.prominentBackground": "#2AA298",
- "extensionButton.prominentForeground": "#F0F0F0",
- "statusBar.background": "#F0F0F0",
- "statusBar.border": "#F0F0F0",
- "statusBar.debuggingBackground": "#F0F0F0",
- "statusBar.debuggingForeground": "#403f53",
- "statusBar.foreground": "#403f53",
- "statusBar.noFolderBackground": "#F0F0F0",
- "statusBar.noFolderForeground": "#403f53",
- "panel.background": "#F0F0F0",
- "panel.border": "#d9d9d9",
- "peekView.border": "#d9d9d9",
- "peekViewEditor.background": "#F6F6F6",
- "peekViewEditorGutter.background": "#F6F6F6",
- "peekViewEditor.matchHighlightBackground": "#49d0c5",
- "peekViewResult.background": "#F0F0F0",
- "peekViewResult.fileForeground": "#403f53",
- "peekViewResult.lineForeground": "#403f53",
- "peekViewResult.matchHighlightBackground": "#49d0c5",
- "peekViewResult.selectionBackground": "#E0E7EA",
- "peekViewResult.selectionForeground": "#403f53",
- "peekViewTitle.background": "#F0F0F0",
- "peekViewTitleLabel.foreground": "#403f53",
- "peekViewTitleDescription.foreground": "#403f53",
- "terminal.ansiBrightBlack": "#403f53",
- "terminal.ansiBlack": "#403f53",
- "terminal.ansiBrightBlue": "#288ed7",
- "terminal.ansiBlue": "#288ed7",
- "terminal.ansiBrightCyan": "#2AA298",
- "terminal.ansiCyan": "#2AA298",
- "terminal.ansiBrightGreen": "#08916a",
- "terminal.ansiGreen": "#08916a",
- "terminal.ansiBrightMagenta": "#d6438a",
- "terminal.ansiMagenta": "#d6438a",
- "terminal.ansiBrightRed": "#de3d3b",
- "terminal.ansiRed": "#de3d3b",
- "terminal.ansiBrightWhite": "#93A1A1",
- "terminal.ansiWhite": "#93A1A1",
- "terminal.ansiBrightYellow": "#daaa01",
- "terminal.ansiYellow": "#E0AF02",
- "terminal.background": "#F6F6F6",
- "terminal.foreground": "#403f53"
- },
- "tokenColors": [
- {
- "name": "Changed",
- "scope": [
- "markup.changed",
- "meta.diff.header.git",
- "meta.diff.header.from-file",
- "meta.diff.header.to-file"
- ],
- "settings": {
- "foreground": "#a2bffc",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Deleted",
- "scope": "markup.deleted.diff",
- "settings": {
- "foreground": "#EF535090",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Inserted",
- "scope": "markup.inserted.diff",
- "settings": {
- "foreground": "#4876d6ff",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Global settings",
- "settings": {
- "background": "#011627",
- "foreground": "#403f53"
- }
- },
- {
- "name": "Comment",
- "scope": ["comment", "punctuation.definition.comment"],
- "settings": {
- "foreground": "#989fb1",
- "fontStyle": "italic"
- }
- },
- {
- "name": "String",
- "scope": "string",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "String Quoted",
- "scope": ["string.quoted", "variable.other.readwrite.js"],
- "settings": {
- "foreground": "#c96765"
- }
- },
- {
- "name": "Support Constant Math",
- "scope": "support.constant.math",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Number",
- "scope": ["constant.numeric", "constant.character.numeric"],
- "settings": {
- "foreground": "#aa0982",
- "fontStyle": ""
- }
- },
- {
- "name": "Built-in constant",
- "scope": [
- "constant.language",
- "punctuation.definition.constant",
- "variable.other.constant"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "User-defined constant",
- "scope": ["constant.character", "constant.other"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Constant Character Escape",
- "scope": "constant.character.escape",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "RegExp String",
- "scope": ["string.regexp", "string.regexp keyword.other"],
- "settings": {
- "foreground": "#5ca7e4"
- }
- },
- {
- "name": "Comma in functions",
- "scope": "meta.function punctuation.separator.comma",
- "settings": {
- "foreground": "#5f7e97"
- }
- },
- {
- "name": "Variable",
- "scope": "variable",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Keyword",
- "scope": ["punctuation.accessor", "keyword"],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Storage",
- "scope": [
- "storage",
- "meta.var.expr",
- "meta.class meta.method.declaration meta.var.expr storage.type.js",
- "storage.type.property.js",
- "storage.type.property.ts",
- "storage.type.property.tsx"
- ],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Storage type",
- "scope": "storage.type",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Storage type",
- "scope": "storage.type.function.arrow.js",
- "settings": {
- "fontStyle": ""
- }
- },
- {
- "name": "Class name",
- "scope": ["entity.name.class", "meta.class entity.name.type.class"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Inherited class",
- "scope": "entity.other.inherited-class",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Function name",
- "scope": "entity.name.function",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Meta Tag",
- "scope": ["punctuation.definition.tag", "meta.tag"],
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "HTML Tag names",
- "scope": [
- "entity.name.tag",
- "meta.tag.other.html",
- "meta.tag.other.js",
- "meta.tag.other.tsx",
- "entity.name.tag.tsx",
- "entity.name.tag.js",
- "entity.name.tag",
- "meta.tag.js",
- "meta.tag.tsx",
- "meta.tag.html"
- ],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": ""
- }
- },
- {
- "name": "Tag attribute",
- "scope": "entity.other.attribute-name",
- "settings": {
- "fontStyle": "italic",
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Entity Name Tag Custom",
- "scope": "entity.name.tag.custom",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Library (function & constant)",
- "scope": ["support.function", "support.constant"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Support Constant Property Value meta",
- "scope": "support.constant.meta.property-value",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Library class/type",
- "scope": ["support.type", "support.class"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Support Variable DOM",
- "scope": "support.variable.dom",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Invalid",
- "scope": "invalid",
- "settings": {
- "foreground": "#ff2c83"
- }
- },
- {
- "name": "Invalid deprecated",
- "scope": "invalid.deprecated",
- "settings": {
- "foreground": "#d3423e"
- }
- },
- {
- "name": "Keyword Operator",
- "scope": "keyword.operator",
- "settings": {
- "foreground": "#0c969b",
- "fontStyle": ""
- }
- },
- {
- "name": "Keyword Operator Relational",
- "scope": "keyword.operator.relational",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Keyword Operator Assignment",
- "scope": "keyword.operator.assignment",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Keyword Operator Arithmetic",
- "scope": "keyword.operator.arithmetic",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Keyword Operator Bitwise",
- "scope": "keyword.operator.bitwise",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Keyword Operator Increment",
- "scope": "keyword.operator.increment",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Keyword Operator Ternary",
- "scope": "keyword.operator.ternary",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "Double-Slashed Comment",
- "scope": "comment.line.double-slash",
- "settings": {
- "foreground": "#939dbb"
- }
- },
- {
- "name": "Object",
- "scope": "object",
- "settings": {
- "foreground": "#cdebf7"
- }
- },
- {
- "name": "Null",
- "scope": "constant.language.null",
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "Meta Brace",
- "scope": "meta.brace",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Meta Delimiter Period",
- "scope": "meta.delimiter.period",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Punctuation Definition String",
- "scope": "punctuation.definition.string",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Punctuation Definition String Markdown",
- "scope": "punctuation.definition.string.begin.markdown",
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "Boolean",
- "scope": "constant.language.boolean",
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "Object Comma",
- "scope": "object.comma",
- "settings": {
- "foreground": "#ffffff"
- }
- },
- {
- "name": "Variable Parameter Function",
- "scope": "variable.parameter.function",
- "settings": {
- "foreground": "#0c969b",
- "fontStyle": ""
- }
- },
- {
- "name": "Support Type Property Name & entity name tags",
- "scope": [
- "support.type.vendor.property-name",
- "support.constant.vendor.property-value",
- "support.type.property-name",
- "meta.property-list entity.name.tag"
- ],
- "settings": {
- "foreground": "#0c969b",
- "fontStyle": ""
- }
- },
- {
- "name": "Entity Name tag reference in stylesheets",
- "scope": "meta.property-list entity.name.tag.reference",
- "settings": {
- "foreground": "#57eaf1"
- }
- },
- {
- "name": "Constant Other Color RGB Value Punctuation Definition Constant",
- "scope": "constant.other.color.rgb-value punctuation.definition.constant",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Constant Other Color",
- "scope": "constant.other.color",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Keyword Other Unit",
- "scope": "keyword.other.unit",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Meta Selector",
- "scope": "meta.selector",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Entity Other Attribute Name Id",
- "scope": "entity.other.attribute-name.id",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Meta Property Name",
- "scope": "meta.property-name",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Doctypes",
- "scope": ["entity.name.tag.doctype", "meta.tag.sgml.doctype"],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Punctuation Definition Parameters",
- "scope": "punctuation.definition.parameters",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Keyword Control Operator",
- "scope": "keyword.control.operator",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Keyword Operator Logical",
- "scope": "keyword.operator.logical",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": ""
- }
- },
- {
- "name": "Variable Instances",
- "scope": [
- "variable.instance",
- "variable.other.instance",
- "variable.readwrite.instance",
- "variable.other.readwrite.instance",
- "variable.other.property"
- ],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Variable Property Other object property",
- "scope": ["variable.other.object.property"],
- "settings": {
- "foreground": "#111111",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Variable Property Other object",
- "scope": ["variable.other.object.js"],
- "settings": {
- "fontStyle": ""
- }
- },
- {
- "name": "Entity Name Function",
- "scope": ["entity.name.function"],
- "settings": {
- "foreground": "#4876d6",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Keyword Operator Comparison, imports, returns and Keyword Operator Ruby",
- "scope": [
- "keyword.operator.comparison",
- "keyword.control.flow.js",
- "keyword.control.flow.ts",
- "keyword.control.flow.tsx",
- "keyword.control.ruby",
- "keyword.control.module.ruby",
- "keyword.control.class.ruby",
- "keyword.control.def.ruby",
- "keyword.control.loop.js",
- "keyword.control.loop.ts",
- "keyword.control.import.js",
- "keyword.control.import.ts",
- "keyword.control.import.tsx",
- "keyword.control.from.js",
- "keyword.control.from.ts",
- "keyword.control.from.tsx",
- "keyword.operator.instanceof.js",
- "keyword.operator.expression.instanceof.ts",
- "keyword.operator.expression.instanceof.tsx"
- ],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Keyword Control Conditional",
- "scope": [
- "keyword.control.conditional.js",
- "keyword.control.conditional.ts",
- "keyword.control.switch.js",
- "keyword.control.switch.ts"
- ],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": ""
- }
- },
- {
- "name": "Support Constant, `new` keyword, Special Method Keyword, `debugger`, other keywords",
- "scope": [
- "support.constant",
- "keyword.other.special-method",
- "keyword.other.new",
- "keyword.other.debugger",
- "keyword.control"
- ],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Support Function",
- "scope": "support.function",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Invalid Broken",
- "scope": "invalid.broken",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Invalid Unimplemented",
- "scope": "invalid.unimplemented",
- "settings": {
- "foreground": "#8BD649"
- }
- },
- {
- "name": "Invalid Illegal",
- "scope": "invalid.illegal",
- "settings": {
- "foreground": "#c96765"
- }
- },
- {
- "name": "Language Variable",
- "scope": "variable.language",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Support Variable Property",
- "scope": "support.variable.property",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Variable Function",
- "scope": "variable.function",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Variable Interpolation",
- "scope": "variable.interpolation",
- "settings": {
- "foreground": "#ec5f67"
- }
- },
- {
- "name": "Meta Function Call",
- "scope": "meta.function-call",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Punctuation Section Embedded",
- "scope": "punctuation.section.embedded",
- "settings": {
- "foreground": "#d3423e"
- }
- },
- {
- "name": "Punctuation Tweaks",
- "scope": [
- "punctuation.terminator.expression",
- "punctuation.definition.arguments",
- "punctuation.definition.array",
- "punctuation.section.array",
- "meta.array"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "More Punctuation Tweaks",
- "scope": [
- "punctuation.definition.list.begin",
- "punctuation.definition.list.end",
- "punctuation.separator.arguments",
- "punctuation.definition.list"
- ],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Template Strings",
- "scope": "string.template meta.template.expression",
- "settings": {
- "foreground": "#d3423e"
- }
- },
- {
- "name": "Backtics(``) in Template Strings",
- "scope": "string.template punctuation.definition.string",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Italics",
- "scope": "italic",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Bold",
- "scope": "bold",
- "settings": {
- "foreground": "#4876d6",
- "fontStyle": "bold"
- }
- },
- {
- "name": "Quote",
- "scope": "quote",
- "settings": {
- "foreground": "#697098",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Raw Code",
- "scope": "raw",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "CoffeScript Variable Assignment",
- "scope": "variable.assignment.coffee",
- "settings": {
- "foreground": "#31e1eb"
- }
- },
- {
- "name": "CoffeScript Parameter Function",
- "scope": "variable.parameter.function.coffee",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "CoffeeScript Assignments",
- "scope": "variable.assignment.coffee",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "C# Readwrite Variables",
- "scope": "variable.other.readwrite.cs",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "C# Classes & Storage types",
- "scope": ["entity.name.type.class.cs", "storage.type.cs"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "C# Namespaces",
- "scope": "entity.name.type.namespace.cs",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Tag names in Stylesheets",
- "scope": [
- "entity.name.tag.css",
- "entity.name.tag.less",
- "entity.name.tag.custom.css",
- "support.constant.property-value.css"
- ],
- "settings": {
- "foreground": "#c96765",
- "fontStyle": ""
- }
- },
- {
- "name": "Wildcard(*) selector in Stylesheets",
- "scope": [
- "entity.name.tag.wildcard.css",
- "entity.name.tag.wildcard.less",
- "entity.name.tag.wildcard.scss",
- "entity.name.tag.wildcard.sass"
- ],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "CSS Keyword Other Unit",
- "scope": "keyword.other.unit.css",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Attribute Name for CSS",
- "scope": [
- "meta.attribute-selector.css entity.other.attribute-name.attribute",
- "variable.other.readwrite.js"
- ],
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Elixir Classes",
- "scope": [
- "source.elixir support.type.elixir",
- "source.elixir meta.module.elixir entity.name.class.elixir"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Elixir Functions",
- "scope": "source.elixir entity.name.function",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Elixir Constants",
- "scope": [
- "source.elixir constant.other.symbol.elixir",
- "source.elixir constant.other.keywords.elixir"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Elixir String Punctuations",
- "scope": "source.elixir punctuation.definition.string",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Elixir",
- "scope": [
- "source.elixir variable.other.readwrite.module.elixir",
- "source.elixir variable.other.readwrite.module.elixir punctuation.definition.variable.elixir"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Elixir Binary Punctuations",
- "scope": "source.elixir .punctuation.binary.elixir",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Closure Constant Keyword",
- "scope": "constant.keyword.clojure",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Go Function Calls",
- "scope": "source.go meta.function-call.go",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Go Keywords",
- "scope": [
- "source.go keyword.package.go",
- "source.go keyword.import.go",
- "source.go keyword.function.go",
- "source.go keyword.type.go",
- "source.go keyword.struct.go",
- "source.go keyword.interface.go",
- "source.go keyword.const.go",
- "source.go keyword.var.go",
- "source.go keyword.map.go",
- "source.go keyword.channel.go",
- "source.go keyword.control.go"
- ],
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Go Constants e.g. nil, string format (%s, %d, etc.)",
- "scope": [
- "source.go constant.language.go",
- "source.go constant.other.placeholder.go"
- ],
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "C++ Functions",
- "scope": [
- "entity.name.function.preprocessor.cpp",
- "entity.scope.name.cpp"
- ],
- "settings": {
- "foreground": "#0c969bff"
- }
- },
- {
- "name": "C++ Meta Namespace",
- "scope": ["meta.namespace-block.cpp"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "C++ Language Primitive Storage",
- "scope": ["storage.type.language.primitive.cpp"],
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "C++ Preprocessor Macro",
- "scope": ["meta.preprocessor.macro.cpp"],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "C++ Variable Parameter",
- "scope": ["variable.parameter"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Powershell Variables",
- "scope": ["variable.other.readwrite.powershell"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Powershell Function",
- "scope": ["support.function.powershell"],
- "settings": {
- "foreground": "#0c969bff"
- }
- },
- {
- "name": "ID Attribute Name in HTML",
- "scope": "entity.other.attribute-name.id.html",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "HTML Punctuation Definition Tag",
- "scope": "punctuation.definition.tag.html",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "HTML Doctype",
- "scope": "meta.tag.sgml.doctype.html",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "JavaScript Classes",
- "scope": "meta.class entity.name.type.class.js",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "JavaScript Method Declaration e.g. `constructor`",
- "scope": "meta.method.declaration storage.type.js",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "JavaScript Terminator",
- "scope": "terminator.js",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "JavaScript Meta Punctuation Definition",
- "scope": "meta.js punctuation.definition.js",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Entity Names in Code Documentations",
- "scope": [
- "entity.name.type.instance.jsdoc",
- "entity.name.type.instance.phpdoc"
- ],
- "settings": {
- "foreground": "#5f7e97"
- }
- },
- {
- "name": "Other Variables in Code Documentations",
- "scope": ["variable.other.jsdoc", "variable.other.phpdoc"],
- "settings": {
- "foreground": "#78ccf0"
- }
- },
- {
- "name": "JavaScript module imports and exports",
- "scope": [
- "variable.other.meta.import.js",
- "meta.import.js variable.other",
- "variable.other.meta.export.js",
- "meta.export.js variable.other"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "JavaScript Variable Parameter Function",
- "scope": "variable.parameter.function.js",
- "settings": {
- "foreground": "#7986E7"
- }
- },
- {
- "name": "JavaScript[React] Variable Other Object",
- "scope": [
- "variable.other.object.js",
- "variable.other.object.jsx",
- "variable.object.property.js",
- "variable.object.property.jsx"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "JavaScript Variables",
- "scope": ["variable.js", "variable.other.js"],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "JavaScript Entity Name Type",
- "scope": ["entity.name.type.js", "entity.name.type.module.js"],
- "settings": {
- "foreground": "#111111",
- "fontStyle": ""
- }
- },
- {
- "name": "JavaScript Support Classes",
- "scope": "support.class.js",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "JSON Property Names",
- "scope": "support.type.property-name.json",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "JSON Support Constants",
- "scope": "support.constant.json",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "JSON Property values (string)",
- "scope": "meta.structure.dictionary.value.json string.quoted.double",
- "settings": {
- "foreground": "#c789d6"
- }
- },
- {
- "name": "Strings in JSON values",
- "scope": "string.quoted.double.json punctuation.definition.string.json",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Specific JSON Property values like null",
- "scope": "meta.structure.dictionary.json meta.structure.dictionary.value constant.language",
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "JavaScript Other Variable",
- "scope": "variable.other.object.js",
- "settings": {
- "foreground": "#0c969b",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Ruby Variables",
- "scope": ["variable.other.ruby"],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Ruby Class",
- "scope": ["entity.name.type.class.ruby"],
- "settings": {
- "foreground": "#c96765"
- }
- },
- {
- "name": "Ruby Hashkeys",
- "scope": "constant.language.symbol.hashkey.ruby",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Ruby Symbols",
- "scope": "constant.language.symbol.ruby",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "LESS Tag names",
- "scope": "entity.name.tag.less",
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "LESS Keyword Other Unit",
- "scope": "keyword.other.unit.css",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Attribute Name for LESS",
- "scope": "meta.attribute-selector.less entity.other.attribute-name.attribute",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Markdown Headings",
- "scope": [
- "markup.heading",
- "markup.heading.setext.1",
- "markup.heading.setext.2"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Markdown Italics",
- "scope": "markup.italic",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Markdown Bold",
- "scope": "markup.bold",
- "settings": {
- "foreground": "#4876d6",
- "fontStyle": "bold"
- }
- },
- {
- "name": "Markdown Quote + others",
- "scope": "markup.quote",
- "settings": {
- "foreground": "#697098",
- "fontStyle": "italic"
- }
- },
- {
- "name": "Markdown Raw Code + others",
- "scope": "markup.inline.raw",
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Markdown Links",
- "scope": [
- "markup.underline.link",
- "markup.underline.link.image"
- ],
- "settings": {
- "foreground": "#ff869a"
- }
- },
- {
- "name": "Markdown Link Title and Description",
- "scope": [
- "string.other.link.title.markdown",
- "string.other.link.description.markdown"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Markdown Punctuation",
- "scope": [
- "punctuation.definition.string.markdown",
- "punctuation.definition.string.begin.markdown",
- "punctuation.definition.string.end.markdown",
- "meta.link.inline.markdown punctuation.definition.string"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Markdown MetaData Punctuation",
- "scope": ["punctuation.definition.metadata.markdown"],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Markdown List Punctuation",
- "scope": ["beginning.punctuation.definition.list.markdown"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Markdown Inline Raw String",
- "scope": "markup.inline.raw.string.markdown",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "PHP Variables",
- "scope": ["variable.other.php", "variable.other.property.php"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Support Classes in PHP",
- "scope": "support.class.php",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Punctuations in PHP function calls",
- "scope": "meta.function-call.php punctuation",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "PHP Global Variables",
- "scope": "variable.other.global.php",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Declaration Punctuation in PHP Global Variables",
- "scope": "variable.other.global.php punctuation.definition.variable",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Language Constants in Python",
- "scope": "constant.language.python",
- "settings": {
- "foreground": "#bc5454"
- }
- },
- {
- "name": "Python Function Parameter and Arguments",
- "scope": [
- "variable.parameter.function.python",
- "meta.function-call.arguments.python"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Python Function Call",
- "scope": [
- "meta.function-call.python",
- "meta.function-call.generic.python"
- ],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "Punctuations in Python",
- "scope": "punctuation.python",
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Decorator Functions in Python",
- "scope": "entity.name.function.decorator.python",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Python Language Variable",
- "scope": "source.python variable.language.special",
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Python import control keyword",
- "scope": "keyword.control",
- "settings": {
- "foreground": "#994cc3",
- "fontStyle": "italic"
- }
- },
- {
- "name": "SCSS Variable",
- "scope": [
- "variable.scss",
- "variable.sass",
- "variable.parameter.url.scss",
- "variable.parameter.url.sass"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Variables in SASS At-Rules",
- "scope": [
- "source.css.scss meta.at-rule variable",
- "source.css.sass meta.at-rule variable"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "Variables in SASS At-Rules",
- "scope": [
- "source.css.scss meta.at-rule variable",
- "source.css.sass meta.at-rule variable"
- ],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "Attribute Name for SASS",
- "scope": [
- "meta.attribute-selector.scss entity.other.attribute-name.attribute",
- "meta.attribute-selector.sass entity.other.attribute-name.attribute"
- ],
- "settings": {
- "foreground": "#aa0982"
- }
- },
- {
- "name": "Tag names in SASS",
- "scope": ["entity.name.tag.scss", "entity.name.tag.sass"],
- "settings": {
- "foreground": "#0c969b"
- }
- },
- {
- "name": "SASS Keyword Other Unit",
- "scope": ["keyword.other.unit.scss", "keyword.other.unit.sass"],
- "settings": {
- "foreground": "#994cc3"
- }
- },
- {
- "name": "TypeScript[React] Variables and Object Properties",
- "scope": [
- "variable.other.readwrite.alias.ts",
- "variable.other.readwrite.alias.tsx",
- "variable.other.readwrite.ts",
- "variable.other.readwrite.tsx",
- "variable.other.object.ts",
- "variable.other.object.tsx",
- "variable.object.property.ts",
- "variable.object.property.tsx",
- "variable.other.ts",
- "variable.other.tsx",
- "variable.tsx",
- "variable.ts"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "TypeScript[React] Entity Name Types",
- "scope": ["entity.name.type.ts", "entity.name.type.tsx"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "TypeScript[React] Node Classes",
- "scope": ["support.class.node.ts", "support.class.node.tsx"],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "TypeScript[React] Entity Name Types as Parameters",
- "scope": [
- "meta.type.parameters.ts entity.name.type",
- "meta.type.parameters.tsx entity.name.type"
- ],
- "settings": {
- "foreground": "#5f7e97"
- }
- },
- {
- "name": "TypeScript[React] Import/Export Punctuations",
- "scope": [
- "meta.import.ts punctuation.definition.block",
- "meta.import.tsx punctuation.definition.block",
- "meta.export.ts punctuation.definition.block",
- "meta.export.tsx punctuation.definition.block"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "TypeScript[React] Punctuation Decorators",
- "scope": [
- "meta.decorator punctuation.decorator.ts",
- "meta.decorator punctuation.decorator.tsx"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "TypeScript[React] Punctuation Decorators",
- "scope": "meta.tag.js meta.jsx.children.tsx",
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "YAML Entity Name Tags",
- "scope": "entity.name.tag.yaml",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "JavaScript Variable Other ReadWrite",
- "scope": ["variable.other.readwrite.js", "variable.parameter"],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "Support Class Component",
- "scope": ["support.class.component.js", "support.class.component.tsx"],
- "settings": {
- "foreground": "#aa0982",
- "fontStyle": ""
- }
- },
- {
- "name": "Text nested in React tags",
- "scope": [
- "meta.jsx.children",
- "meta.jsx.children.js",
- "meta.jsx.children.tsx"
- ],
- "settings": {
- "foreground": "#403f53"
- }
- },
- {
- "name": "TypeScript Classes",
- "scope": "meta.class entity.name.type.class.tsx",
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "TypeScript Entity Name Type",
- "scope": ["entity.name.type.tsx", "entity.name.type.module.tsx"],
- "settings": {
- "foreground": "#111111"
- }
- },
- {
- "name": "TypeScript Class Variable Keyword",
- "scope": [
- "meta.class.ts meta.var.expr.ts storage.type.ts",
- "meta.class.tsx meta.var.expr.tsx storage.type.tsx"
- ],
- "settings": {
- "foreground": "#994CC3"
- }
- },
- {
- "name": "TypeScript Method Declaration e.g. `constructor`",
- "scope": [
- "meta.method.declaration storage.type.ts",
- "meta.method.declaration storage.type.tsx"
- ],
- "settings": {
- "foreground": "#4876d6"
- }
- },
- {
- "name": "normalize font style of certain components",
- "scope": [
- "meta.property-list.css meta.property-value.css variable.other.less",
- "meta.property-list.scss variable.scss",
- "meta.property-list.sass variable.sass",
- "meta.brace",
- "keyword.operator.operator",
- "keyword.operator.or.regexp",
- "keyword.operator.expression.in",
- "keyword.operator.relational",
- "keyword.operator.assignment",
- "keyword.operator.comparison",
- "keyword.operator.type",
- "keyword.operator",
- "keyword",
- "punctuation.definintion.string",
- "punctuation",
- "variable.other.readwrite.js",
- "storage.type",
- "source.css",
- "string.quoted"
- ],
- "settings": {
- "fontStyle": ""
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/components/editor/night.json b/components/editor/night.json
deleted file mode 100644
index 993c6496f2d1a9f783e395ed572fa4148796533f..0000000000000000000000000000000000000000
--- a/components/editor/night.json
+++ /dev/null
@@ -1,347 +0,0 @@
-{
- "inherit": true,
- "rules": [
- {
- "background": "24292e",
- "token": ""
- },
- {
- "foreground": "959da5",
- "token": "comment"
- },
- {
- "foreground": "959da5",
- "token": "punctuation.definition.comment"
- },
- {
- "foreground": "959da5",
- "token": "string.comment"
- },
- {
- "foreground": "c8e1ff",
- "token": "constant"
- },
- {
- "foreground": "c8e1ff",
- "token": "entity.name.constant"
- },
- {
- "foreground": "c8e1ff",
- "token": "variable.other.constant"
- },
- {
- "foreground": "c8e1ff",
- "token": "variable.language"
- },
- {
- "foreground": "b392f0",
- "token": "entity"
- },
- {
- "foreground": "b392f0",
- "token": "entity.name"
- },
- {
- "foreground": "f6f8fa",
- "token": "variable.parameter.function"
- },
- {
- "foreground": "7bcc72",
- "token": "entity.name.tag"
- },
- {
- "foreground": "ea4a5a",
- "token": "keyword"
- },
- {
- "foreground": "ea4a5a",
- "token": "storage"
- },
- {
- "foreground": "ea4a5a",
- "token": "storage.type"
- },
- {
- "foreground": "f6f8fa",
- "token": "storage.modifier.package"
- },
- {
- "foreground": "f6f8fa",
- "token": "storage.modifier.import"
- },
- {
- "foreground": "f6f8fa",
- "token": "storage.type.java"
- },
- {
- "foreground": "79b8ff",
- "token": "string"
- },
- {
- "foreground": "79b8ff",
- "token": "punctuation.definition.string"
- },
- {
- "foreground": "79b8ff",
- "token": "string punctuation.section.embedded source"
- },
- {
- "foreground": "c8e1ff",
- "token": "support"
- },
- {
- "foreground": "c8e1ff",
- "token": "meta.property-name"
- },
- {
- "foreground": "fb8532",
- "token": "variable"
- },
- {
- "foreground": "f6f8fa",
- "token": "variable.other"
- },
- {
- "foreground": "d73a49",
- "fontStyle": "bold italic underline",
- "token": "invalid.broken"
- },
- {
- "foreground": "d73a49",
- "fontStyle": "bold italic underline",
- "token": "invalid.deprecated"
- },
- {
- "foreground": "fafbfc",
- "background": "d73a49",
- "fontStyle": "italic underline",
- "token": "invalid.illegal"
- },
- {
- "foreground": "fafbfc",
- "background": "d73a49",
- "fontStyle": "italic underline",
- "token": "carriage-return"
- },
- {
- "foreground": "d73a49",
- "fontStyle": "bold italic underline",
- "token": "invalid.unimplemented"
- },
- {
- "foreground": "d73a49",
- "token": "message.error"
- },
- {
- "foreground": "f6f8fa",
- "token": "string source"
- },
- {
- "foreground": "c8e1ff",
- "token": "string variable"
- },
- {
- "foreground": "79b8ff",
- "token": "source.regexp"
- },
- {
- "foreground": "79b8ff",
- "token": "string.regexp"
- },
- {
- "foreground": "79b8ff",
- "token": "string.regexp.character-class"
- },
- {
- "foreground": "79b8ff",
- "token": "string.regexp constant.character.escape"
- },
- {
- "foreground": "79b8ff",
- "token": "string.regexp source.ruby.embedded"
- },
- {
- "foreground": "79b8ff",
- "token": "string.regexp string.regexp.arbitrary-repitition"
- },
- {
- "foreground": "7bcc72",
- "fontStyle": "bold",
- "token": "string.regexp constant.character.escape"
- },
- {
- "foreground": "c8e1ff",
- "token": "support.constant"
- },
- {
- "foreground": "c8e1ff",
- "token": "support.variable"
- },
- {
- "foreground": "c8e1ff",
- "token": "meta.module-reference"
- },
- {
- "foreground": "fb8532",
- "token": "markup.list"
- },
- {
- "foreground": "0366d6",
- "fontStyle": "bold",
- "token": "markup.heading"
- },
- {
- "foreground": "0366d6",
- "fontStyle": "bold",
- "token": "markup.heading entity.name"
- },
- {
- "foreground": "c8e1ff",
- "token": "markup.quote"
- },
- {
- "foreground": "f6f8fa",
- "fontStyle": "italic",
- "token": "markup.italic"
- },
- {
- "foreground": "f6f8fa",
- "fontStyle": "bold",
- "token": "markup.bold"
- },
- {
- "foreground": "c8e1ff",
- "token": "markup.raw"
- },
- {
- "foreground": "b31d28",
- "background": "ffeef0",
- "token": "markup.deleted"
- },
- {
- "foreground": "b31d28",
- "background": "ffeef0",
- "token": "meta.diff.header.from-file"
- },
- {
- "foreground": "b31d28",
- "background": "ffeef0",
- "token": "punctuation.definition.deleted"
- },
- {
- "foreground": "176f2c",
- "background": "f0fff4",
- "token": "markup.inserted"
- },
- {
- "foreground": "176f2c",
- "background": "f0fff4",
- "token": "meta.diff.header.to-file"
- },
- {
- "foreground": "176f2c",
- "background": "f0fff4",
- "token": "punctuation.definition.inserted"
- },
- {
- "foreground": "b08800",
- "background": "fffdef",
- "token": "markup.changed"
- },
- {
- "foreground": "b08800",
- "background": "fffdef",
- "token": "punctuation.definition.changed"
- },
- {
- "foreground": "2f363d",
- "background": "959da5",
- "token": "markup.ignored"
- },
- {
- "foreground": "2f363d",
- "background": "959da5",
- "token": "markup.untracked"
- },
- {
- "foreground": "b392f0",
- "fontStyle": "bold",
- "token": "meta.diff.range"
- },
- {
- "foreground": "c8e1ff",
- "token": "meta.diff.header"
- },
- {
- "foreground": "0366d6",
- "fontStyle": "bold",
- "token": "meta.separator"
- },
- {
- "foreground": "0366d6",
- "token": "meta.output"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.tag"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.curly"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.round"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.square"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.angle"
- },
- {
- "foreground": "ffeef0",
- "token": "brackethighlighter.quote"
- },
- {
- "foreground": "d73a49",
- "token": "brackethighlighter.unmatched"
- },
- {
- "foreground": "d73a49",
- "token": "sublimelinter.mark.error"
- },
- {
- "foreground": "fb8532",
- "token": "sublimelinter.mark.warning"
- },
- {
- "foreground": "6a737d",
- "token": "sublimelinter.gutter-mark"
- },
- {
- "foreground": "79b8ff",
- "fontStyle": "underline",
- "token": "constant.other.reference.link"
- },
- {
- "foreground": "79b8ff",
- "fontStyle": "underline",
- "token": "string.other.link"
- }
- ],
- "colors": {
- "editor.foreground": "#f6f8fa",
- "editor.background": "#24292e",
- "editor.selectionBackground": "#4c2889",
- "editor.inactiveSelectionBackground": "#444d56",
- "editor.lineHighlightBackground": "#444d56",
- "editorCursor.foreground": "#ffffff",
- "editorWhitespace.foreground": "#6a737d",
- "editorIndentGuide.background": "#6a737d",
- "editorIndentGuide.activeBackground": "#f6f8fa",
- "editor.selectionHighlightBorder": "#444d56"
- }
-}
\ No newline at end of file
diff --git a/components/editor/project-settings.tsx b/components/editor/project-settings.tsx
deleted file mode 100644
index 67c09ccea881f078a6db95a51d0671c471d9c08a..0000000000000000000000000000000000000000
--- a/components/editor/project-settings.tsx
+++ /dev/null
@@ -1,276 +0,0 @@
-import { useState } from "react";
-import Image from "next/image";
-import Link from "next/link";
-import { useTheme } from "next-themes";
-import { Settings, Check, Plus, Download } from "lucide-react";
-import { RiContrastFill } from "react-icons/ri";
-import { useSession } from "next-auth/react";
-import { useParams } from "next/navigation";
-import { ChevronDown, ChevronLeft, Edit } from "lucide-react";
-
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuPortal,
- DropdownMenuSeparator,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "@/components/ui/dialog";
-import { Button } from "@/components/ui/button";
-import { ProjectWithCommits } from "@/actions/projects";
-import ProIcon from "@/assets/pro.svg";
-import { Input } from "@/components/ui/input";
-import { useQueryClient } from "@tanstack/react-query";
-import Loading from "@/components/loading";
-import { toast } from "sonner";
-
-export const ProjectSettings = ({
- project,
-}: {
- project?: ProjectWithCommits | null;
-}) => {
- const queryClient = useQueryClient();
- const { repoId, owner } = useParams();
- const { theme, setTheme } = useTheme();
- const { data: session } = useSession();
-
- const [open, setOpen] = useState(false);
- const [projectName, setProjectName] = useState(
- project?.cardData?.title || "New DeepSite website",
- );
- const [isLoading, setIsLoading] = useState(false);
- const [error, setError] = useState(null);
-
- const handleRenameProject = async () => {
- setIsLoading(true);
- setError(null);
- try {
- const response = await fetch(`/api/projects/${repoId}/rename`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ newTitle: projectName }),
- }).then(async (response) => {
- if (response.ok) {
- return response.json();
- }
- });
- if (response?.success) {
- setOpen(false);
- queryClient.setQueryData(
- ["project"],
- (oldProject: ProjectWithCommits) => ({
- ...oldProject,
- cardData: {
- ...oldProject.cardData,
- title: projectName,
- },
- }),
- );
- } else {
- setError("Could not rename the project. Please try again.");
- }
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- } catch (err: any) {
- setError(err.message || "An error occurred");
- }
- setIsLoading(false);
- };
-
- const handleDownload = async () => {
- try {
- toast.info("Preparing download...");
- const response = await fetch(`/api/projects/${repoId}/download`, {
- headers: {
- Accept: "application/zip",
- },
- });
- if (!response.ok) {
- throw new Error("Failed to download project");
- }
- const blob = await response.blob();
- const url = window.URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.href = url;
- link.download = `${session?.user?.username}-${repoId}.zip`;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(url);
- toast.success("Download started!");
- } catch (error) {
- toast.error("Failed to download project");
- }
- };
-
- return (
- <>
-
-
-
- {project?.cardData?.emoji ? (
- {project?.cardData?.emoji}
- ) : (
-
- )}
-
-
- {project?.cardData?.title ?? "New DeepSite website"}{" "}
-
-
- Live preview of your app
-
-
-
-
-
-
-
-
-
- Go to Projects
-
-
-
- {!session?.user?.isPro && (
- <>
-
-
-
- Subscribe to Pro
-
-
-
- >
- )}
-
-
-
- New Project
-
-
- {project && (
- <>
- setOpen(true)}>
-
- Rename the project
-
-
-
-
- Project settings
-
-
- handleDownload()}>
-
- Download project
-
- >
- )}
-
-
-
- Appearance
-
-
-
- setTheme("light")}>
- Light
- {theme === "light" && }
-
- setTheme("dark")}>
- Dark
- {theme === "dark" && }
-
- setTheme("system")}>
- System
- {theme === "system" && }
-
-
-
-
-
-
-
-
-
- Rename your Project
-
- Update the name of your project below.
-
-
-
-
- {error &&
{error}
}
-
- The URL of your project will remain the same after changing the
- name, only the displayed name will be updated.
-
-
setProjectName(e.target.value)}
- />
-
-
-
-
- Cancel
-
- handleRenameProject()}
- >
- {isLoading ? (
- <>
-
- Saving project...
- >
- ) : (
- <>
-
- Update Project name
- >
- )}
-
-
-
-
- >
- );
-};
diff --git a/components/grid-pattern/index.tsx b/components/grid-pattern/index.tsx
deleted file mode 100644
index 1006f5253f4f5d749d15c07eb75c81b924c54aae..0000000000000000000000000000000000000000
--- a/components/grid-pattern/index.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-"use client";
-import { useId } from "react";
-import { cn } from "@/lib/utils";
-
-interface GridPatternProps extends React.SVGProps {
- width?: number;
- height?: number;
- x?: number;
- y?: number;
- squares?: Array<[x: number, y: number]>;
- strokeDasharray?: string;
- className?: string;
- [key: string]: unknown;
-}
-
-export function GridPattern({
- width = 40,
- height = 40,
- x = -1,
- y = -1,
- strokeDasharray = "0",
- squares,
- className,
- ...props
-}: GridPatternProps) {
- const id = useId();
-
- return (
-
-
-
-
-
-
-
- {squares && (
-
- {squares.map(([x, y]) => (
-
- ))}
-
- )}
-
- );
-}
diff --git a/components/login/login-buttons.tsx b/components/login/login-buttons.tsx
deleted file mode 100644
index 8354ba6530afc6cf87bdec6d25707a936d0fb5a6..0000000000000000000000000000000000000000
--- a/components/login/login-buttons.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-"use client";
-import { signIn } from "next-auth/react";
-import Image from "next/image";
-
-import { Button } from "@/components/ui/button";
-import HFLogo from "@/assets/hf-logo.svg";
-
-export const LoginButtons = ({ callbackUrl }: { callbackUrl: string }) => {
- return (
- signIn("huggingface", { callbackUrl })}>
-
- Sign in with Hugging Face
-
- );
-};
diff --git a/components/not-authorized.tsx b/components/not-authorized.tsx
deleted file mode 100644
index 574e252b5e74f88bd99afaf131160e035b739097..0000000000000000000000000000000000000000
--- a/components/not-authorized.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-"use client";
-import { useState } from "react";
-import Image from "next/image";
-import { Button } from "./ui/button";
-import Link from "next/link";
-import { useMount } from "react-use";
-
-import { Dialog, DialogContent, DialogTitle } from "./ui/dialog";
-
-export const NotAuthorizedDomain = () => {
- const [open, setOpen] = useState(false);
-
- useMount(() => {
- const isAllowedDomain = (hostname: string) => {
- const host = hostname.toLowerCase();
- return (
- host.endsWith(".huggingface.co") ||
- host.endsWith(".hf.co") ||
- host === "huggingface.co" ||
- host === "hf.co" ||
- host === "enzostvs-deepsite.hf.space" ||
- host === "deepsite.hf.co"
- );
- };
-
- const isInIframe = () => {
- try {
- return window.self !== window.top;
- } catch {
- return true;
- }
- };
-
- const isEmbedded = () => {
- try {
- return window.location !== window.parent.location;
- } catch {
- return true;
- }
- };
-
- const addCanonicalUrl = () => {
- const existingCanonical = document.querySelector('link[rel="canonical"]');
- if (existingCanonical) {
- existingCanonical.remove();
- }
-
- const canonical = document.createElement("link");
- canonical.rel = "canonical";
- canonical.href = window.location.href;
- document.head.appendChild(canonical);
-
- if (isInIframe() || isEmbedded()) {
- try {
- const parentHostname = document.referrer
- ? new URL(document.referrer).hostname
- : null;
- if (parentHostname && !isAllowedDomain(parentHostname)) {
- const noIndexMeta = document.createElement("meta");
- noIndexMeta.name = "robots";
- noIndexMeta.content = "noindex, nofollow";
- document.head.appendChild(noIndexMeta);
- }
- } catch (error) {
- console.debug(
- "SEO: Could not determine parent domain for iframe indexing rules"
- );
- }
- }
- };
-
- const shouldShowWarning = () => {
- if (!isInIframe() && !isEmbedded()) {
- return false; // Not in an iframe
- }
-
- try {
- const parentHostname = window.parent.location.hostname;
- return !isAllowedDomain(parentHostname);
- } catch {
- try {
- if (document.referrer) {
- const referrerUrl = new URL(document.referrer);
- return !isAllowedDomain(referrerUrl.hostname);
- }
- } catch {}
- return true;
- }
- };
-
- addCanonicalUrl();
-
- if (shouldShowWarning()) {
- setOpen(true);
- }
- });
-
- return (
-
-
-
-
-
-
Access Denied
-
- Unfortunately, you don't have access to DeepSite from this
- domain:{" "}
- {open && (
-
- {window?.location?.hostname ?? "unknown domain"}
-
- )}
- .
-
-
-
-
- Go to DeepSite
-
-
-
-
-
- );
-};
diff --git a/components/not-found/buttons.tsx b/components/not-found/buttons.tsx
deleted file mode 100644
index a30f436d48121858411d727b457abff9c3bb56e2..0000000000000000000000000000000000000000
--- a/components/not-found/buttons.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-"use client";
-import Link from "next/link";
-import { Button } from "@/components/ui/button";
-
-export function NotFoundButtons() {
- return (
-
-
- Return Home
-
- window.history.back()}>
- Back to previous page
-
-
- );
-}
diff --git a/components/pro-modal/index.tsx b/components/pro-modal/index.tsx
deleted file mode 100644
index 777cd9a5ce7dc1110ffc810d525e0c4a31adc1f9..0000000000000000000000000000000000000000
--- a/components/pro-modal/index.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Button } from "@/components/ui/button";
-import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
-import { CheckCheck } from "lucide-react";
-
-export const ProModal = ({
- open,
- onClose,
-}: {
- open: boolean;
- onClose: React.Dispatch>;
-}) => {
- const handleProClick = () => {
- window.open("https://huggingface.co/subscribe/pro?from=DeepSite", "_blank");
- onClose(false);
- };
- return (
-
-
-
-
-
-
-
- Upgrade to a Account, and unlock your
- DeepSite high quota access ⚡
-
-
-
- You'll also unlock some Hugging Face PRO features, like:
-
-
-
- Get acces to thousands of AI app (ZeroGPU) with high quota
-
-
-
- Get exclusive early access to new features and updates
-
-
-
- Get free credits across all Inference Providers
-
-
- ... and lots more!
-
-
-
- Subscribe to PRO ($9/month)
-
-
-
-
- );
-};
-
-export const ProTag = ({
- className,
- ...props
-}: {
- className?: string;
- onClick?: () => void;
-}) => (
-
- PRO
-
-);
diff --git a/components/projects/big-project-card.tsx b/components/projects/big-project-card.tsx
deleted file mode 100644
index 609a49e187e06bdbe968c9b00b3b96962c9fde23..0000000000000000000000000000000000000000
--- a/components/projects/big-project-card.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-import Link from "next/link";
-import { SpaceEntry } from "@huggingface/hub";
-import { format } from "date-fns";
-import {
- CogIcon,
- Download,
- EllipsisVertical,
- ExternalLink,
- TrashIcon,
-} from "lucide-react";
-
-import { Button } from "@/components/ui/button";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { toast } from "sonner";
-
-// from-red-500 to-red-500
-// from-yellow-500 to-yellow-500
-// from-green-500 to-green-500
-// from-purple-500 to-purple-500
-// from-blue-500 to-blue-500
-// from-pink-500 to-pink-500
-// from-gray-500 to-gray-500
-// from-indigo-500 to-indigo-500
-
-export function BigProjectCard({
- project,
- onOpenDeleteDialog,
-}: {
- project: SpaceEntry;
- onOpenDeleteDialog: (id: string) => void;
-}) {
- const handleDownload = async () => {
- try {
- toast.info("Preparing download...");
- const response = await fetch(
- `/api/projects/${project.name.split("/")[1]}/download`,
- {
- headers: {
- Accept: "application/zip",
- },
- },
- );
- if (!response.ok) {
- throw new Error("Failed to download project");
- }
- const blob = await response.blob();
- const url = window.URL.createObjectURL(blob);
- const link = document.createElement("a");
- link.href = url;
- link.download = `${project.name.replaceAll("/", "-")}.zip`;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- window.URL.revokeObjectURL(url);
- toast.success("Download started!");
- } catch (error) {
- toast.error("Failed to download project");
- }
- };
-
- return (
-
-
- {project.private ? (
-
-
{project.cardData?.emoji}
-
- {project.cardData?.title}
-
-
- ) : (
-
-
-
- )}
-
-
-
- Open project
-
-
-
- {project.cardData?.title}
-
-
-
-
- Last update: {format(project.updatedAt, "MMM d, yyyy")}
-
-
-
-
-
-
-
-
- {
- e.preventDefault();
- e.stopPropagation();
- window.open(
- `https://huggingface.co/spaces/${project.name}/settings`,
- "_blank",
- );
- }}
- >
-
- Settings
-
- {
- handleDownload();
- e.preventDefault();
- e.stopPropagation();
- }}
- >
-
- Download
-
- {
- e.preventDefault();
- e.stopPropagation();
- onOpenDeleteDialog(project.name);
- }}
- >
-
- Delete this project
-
-
-
-
-
- );
-}
diff --git a/components/projects/project-card.tsx b/components/projects/project-card.tsx
deleted file mode 100644
index 2112aa66c0c857b3180fd9f7baa65f3586456d76..0000000000000000000000000000000000000000
--- a/components/projects/project-card.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { SpaceEntry } from "@huggingface/hub";
-
-// from-red-500 to-red-500
-// from-yellow-500 to-yellow-500
-// from-green-500 to-green-500
-// from-purple-500 to-purple-500
-// from-blue-500 to-blue-500
-// from-pink-500 to-pink-500
-// from-gray-500 to-gray-500
-// from-indigo-500 to-indigo-500
-
-export function ProjectCard({ project }: { project: SpaceEntry }) {
- return (
-
-
-
- {project?.cardData?.emoji || "🚀"}
-
-
-
- {project.cardData?.title}
-
-
- {project.name}
-
-
-
-
- );
-}
diff --git a/components/projects/useManualUpdates.ts b/components/projects/useManualUpdates.ts
deleted file mode 100644
index 6d3cdeee36528c027635a85d1183f470c887aa7f..0000000000000000000000000000000000000000
--- a/components/projects/useManualUpdates.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { File } from "@/lib/type";
-import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useMemo } from "react";
-
-export const useManualUpdates = () => {
- const queryClient = useQueryClient();
-
- const { data: manuallyUpdatedFiles } = useQuery({
- queryKey: ["manuallyUpdatedFiles"],
- queryFn: () => {
- return [];
- },
- refetchOnWindowFocus: false,
- refetchOnMount: false,
- refetchOnReconnect: false,
- refetchInterval: false,
- refetchIntervalInBackground: false,
- });
- const files = useMemo(() => {
- return queryClient.getQueryData(["files"]) ?? [];
- }, [queryClient]);
-
- const isManuallyUpdatedSameAsFiles = useMemo(() => {
- return manuallyUpdatedFiles?.every((file) =>
- files?.some((f) => f.path === file.path && f.content === file.content)
- );
- }, [manuallyUpdatedFiles, files]);
-
- return { manuallyUpdatedFiles, isManuallyUpdatedSameAsFiles };
-};
diff --git a/components/projects/useProject.ts b/components/projects/useProject.ts
deleted file mode 100644
index 5a8b4f4bcd21d45bfc1eb1bc8ccf184404bbb066..0000000000000000000000000000000000000000
--- a/components/projects/useProject.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { useEffect } from "react";
-
-import { getProject, ProjectWithCommits } from "@/actions/projects";
-import { File } from "@/lib/type";
-
-export const useProject = (
- initialProject?: ProjectWithCommits | null,
- initialFiles?: File[] | null,
- isNew?: boolean
-) => {
- const queryClient = useQueryClient();
-
- useEffect(() => {
- if (isNew) {
- queryClient.setQueryData(["project"], null);
- queryClient.setQueryData(["files"], []);
- } else if (initialProject) {
- queryClient.setQueryData(["project"], initialProject);
- if (initialFiles) {
- queryClient.setQueryData(["files"], initialFiles);
- }
- }
- }, [initialProject, initialFiles, isNew, queryClient]);
-
- const {
- data: project,
- isLoading,
- error,
- } = useQuery({
- queryKey: ["project"],
- initialData: initialProject,
- queryFn: async () => {
- if (isNew) return null;
- const datas = await getProject(initialProject?.name as string);
- if (datas?.files) {
- setFiles(datas.files);
- }
- return datas?.project ?? null;
- },
- refetchOnWindowFocus: false,
- refetchOnMount: false,
- refetchOnReconnect: false,
- refetchInterval: false,
- refetchIntervalInBackground: false,
- });
-
- const { data: files } = useQuery({
- queryKey: ["files"],
- initialData: initialFiles,
- queryFn: () => {
- return [];
- },
- refetchOnWindowFocus: false,
- refetchOnMount: false,
- refetchOnReconnect: false,
- refetchInterval: false,
- refetchIntervalInBackground: false,
- });
-
- const setFiles = (newFiles: File[]) => {
- queryClient.setQueryData(["files"], (oldFiles: File[] = []) => {
- const currentFiles = oldFiles.filter(
- (file) => !newFiles.some((f) => f.path === file.path)
- );
- return [...currentFiles, ...newFiles];
- });
- };
-
- return {
- project,
- isLoading,
- error,
- files,
- };
-};
diff --git a/components/projects/useProjects.ts b/components/projects/useProjects.ts
deleted file mode 100644
index 4b67775c489e76b3b8ffef0fa5929f06f42df896..0000000000000000000000000000000000000000
--- a/components/projects/useProjects.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { getProjects } from "@/actions/projects";
-import { useQuery } from "@tanstack/react-query";
-
-export const useProjects = () => {
- const { data: projects, isLoading, error, refetch } = useQuery({
- queryKey: ["projects"],
- queryFn: () => getProjects(),
- });
- return { projects, isLoading, error, refetch };
-}
\ No newline at end of file
diff --git a/components/projects/user-projects.tsx b/components/projects/user-projects.tsx
deleted file mode 100644
index 1b447dd91a4cbccd6bc00aaa048919b7bf3b57e2..0000000000000000000000000000000000000000
--- a/components/projects/user-projects.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-"use client";
-
-import Link from "next/link";
-import { useState } from "react";
-import { Plus, TrashIcon } from "lucide-react";
-import { useSession } from "next-auth/react";
-
-import { useProjects } from "./useProjects";
-import { Button } from "@/components/ui/button";
-import { BigProjectCard } from "./big-project-card";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "@/components/ui/dialog";
-import { Input } from "@/components/ui/input";
-import { toast } from "sonner";
-
-export function UserProjects() {
- const { data: session } = useSession();
- const { projects, isLoading, error, refetch } = useProjects();
- const [open, setOpen] = useState(false);
- const [deleteProjectId, setDeleteProjectId] = useState(null);
- const [input, setInput] = useState("");
- if (isLoading || error || !session?.user?.name) return null;
-
- const handleDeleteProject = async () => {
- const response = await fetch(
- `/api/projects/${deleteProjectId?.split("/")[1]}`,
- {
- method: "DELETE",
- headers: {
- "Content-Type": "application/json",
- },
- }
- ).then(async (response) => {
- if (response.ok) {
- return response.json();
- }
- });
- if (response?.success) {
- toast.success("Project has been deleted");
- refetch();
- setOpen(false);
- setDeleteProjectId(null);
- setInput("");
- } else {
- toast.error("Failed to delete project");
- }
- };
-
- return (
-
-
-
-
- {projects?.map((project) => (
-
{
- setOpen(true);
- setDeleteProjectId(id);
- }}
- />
- ))}
- {projects?.length === 0 && (
-
-
-
- )}
-
-
-
-
-
- Delete project
-
- This action is irreversible. Are you sure you want to delete this
- project?
-
-
-
-
-
- Type{" "}
-
- {deleteProjectId}
- {" "}
- to confirm deletion
-
-
setInput(e.target.value)}
- />
-
-
-
-
- Cancel
-
- handleDeleteProject()}
- >
-
- Delete project
-
-
-
-
-
- );
-}
diff --git a/components/providers/react-query.tsx b/components/providers/react-query.tsx
deleted file mode 100644
index 70f1710fbfb56a37bfe8f6fd888a8df1690acefc..0000000000000000000000000000000000000000
--- a/components/providers/react-query.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-"use client";
-
-import { useState } from "react";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental";
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
-
-export function ReactQueryProvider({
- children,
-}: {
- children: React.ReactNode;
-}) {
- const [queryClient] = useState(() => new QueryClient());
- return (
-
- {children}
-
-
- );
-}
diff --git a/components/providers/session.tsx b/components/providers/session.tsx
deleted file mode 100644
index dcc0b7efdac893d0c929fb85b0dd0941ca79fde4..0000000000000000000000000000000000000000
--- a/components/providers/session.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-"use client";
-
-import { SessionProvider } from "next-auth/react";
-
-export function AuthProvider({ children }: { children: React.ReactNode }) {
- return (
- {children}
- );
-}
diff --git a/components/providers/theme.tsx b/components/providers/theme.tsx
deleted file mode 100644
index 189a2b1a12231255b53038166eb92d2d03bd0e9d..0000000000000000000000000000000000000000
--- a/components/providers/theme.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { ThemeProvider as NextThemesProvider } from "next-themes";
-
-export function ThemeProvider({
- children,
- ...props
-}: React.ComponentProps) {
- return {children} ;
-}
diff --git a/components/public/animated-dots-background.tsx b/components/public/animated-dots-background.tsx
deleted file mode 100644
index ed91f8c570942139278117fe7bb82a087b26b1e2..0000000000000000000000000000000000000000
--- a/components/public/animated-dots-background.tsx
+++ /dev/null
@@ -1,180 +0,0 @@
-"use client";
-
-import { useEffect, useRef } from "react";
-
-export function AnimatedDotsBackground() {
- const canvasRef = useRef(null);
- const dotsRef = useRef<
- Array<{ x: number; y: number; targetColor: number; currentColor: number }>
- >([]);
- const mouseRef = useRef({ x: -1000, y: -1000 });
- const animationFrameRef = useRef(null);
-
- useEffect(() => {
- const canvas = canvasRef.current;
- if (!canvas) return undefined;
-
- const ctx = canvas.getContext("2d");
- if (!ctx) return;
-
- // Helper function to convert OKLCH to RGB
- const oklchToRgb = (
- oklchString: string
- ): { r: number; g: number; b: number } => {
- const temp = document.createElement("div");
- temp.style.color = oklchString;
- document.body.appendChild(temp);
- const computed = window.getComputedStyle(temp).color;
- document.body.removeChild(temp);
-
- // Parse RGB values from the computed color string
- const match = computed.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
- if (match) {
- return {
- r: parseInt(match[1]),
- g: parseInt(match[2]),
- b: parseInt(match[3]),
- };
- }
- return { r: 200, g: 200, b: 200 }; // Fallback
- };
-
- // Get theme colors from CSS variables
- const getThemeColors = () => {
- const style = getComputedStyle(document.documentElement);
- const mutedFg = style.getPropertyValue("--muted-foreground").trim();
- const primary = style.getPropertyValue("--primary").trim();
-
- return {
- base: oklchToRgb(`oklch(${mutedFg})`),
- accent: oklchToRgb(`oklch(${primary})`),
- };
- };
-
- const colors = getThemeColors();
-
- // Set canvas size
- const updateCanvasSize = () => {
- const rect = canvas.getBoundingClientRect();
- canvas.width = rect.width * window.devicePixelRatio;
- canvas.height = rect.height * window.devicePixelRatio;
- ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
-
- // Reinitialize dots when canvas size changes
- initializeDots();
- };
-
- const initializeDots = () => {
- const rect = canvas.getBoundingClientRect();
- const spacing = 30; // Space between dots
- const cols = Math.ceil(rect.width / spacing);
- const rows = Math.ceil(rect.height / spacing);
-
- dotsRef.current = [];
- for (let i = 0; i < cols; i++) {
- for (let j = 0; j < rows; j++) {
- dotsRef.current.push({
- x: i * spacing + spacing / 2,
- y: j * spacing + spacing / 2,
- targetColor: 0, // 0 = gray, 1 = orange
- currentColor: 0,
- });
- }
- }
- };
-
- updateCanvasSize();
-
- // Handle mouse move
- const handleMouseMove = (e: MouseEvent) => {
- const rect = canvas.getBoundingClientRect();
- mouseRef.current = {
- x: e.clientX - rect.left,
- y: e.clientY - rect.top,
- };
- };
-
- // Handle mouse leave
- const handleMouseLeave = () => {
- mouseRef.current = { x: -1000, y: -1000 };
- };
-
- canvas.addEventListener("mousemove", handleMouseMove);
- canvas.addEventListener("mouseleave", handleMouseLeave);
- window.addEventListener("resize", updateCanvasSize);
-
- // Animation loop
- const animate = () => {
- const rect = canvas.getBoundingClientRect();
- ctx.clearRect(0, 0, rect.width, rect.height);
-
- const hoverRadius = 80; // Distance for hover effect
- const { x: mouseX, y: mouseY } = mouseRef.current;
-
- dotsRef.current.forEach((dot) => {
- // Calculate distance to mouse
- const dx = dot.x - mouseX;
- const dy = dot.y - mouseY;
- const distance = Math.sqrt(dx * dx + dy * dy);
-
- // Update target color based on proximity to mouse
- if (distance < hoverRadius) {
- const intensity = 1 - distance / hoverRadius;
- dot.targetColor = intensity;
- } else {
- dot.targetColor = 0;
- }
-
- // Smooth transition with ease (lerp with easing factor)
- const easeSpeed = 0.1;
- dot.currentColor += (dot.targetColor - dot.currentColor) * easeSpeed;
-
- // Interpolate between base and accent colors from theme
- const r = Math.round(
- colors.base.r + (colors.accent.r - colors.base.r) * dot.currentColor
- );
- const g = Math.round(
- colors.base.g + (colors.accent.g - colors.base.g) * dot.currentColor
- );
- const b = Math.round(
- colors.base.b + (colors.accent.b - colors.base.b) * dot.currentColor
- );
-
- ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
-
- // Draw dot with size based on hover intensity
- const baseSize = 2;
- const hoverSize = 4;
- const size = baseSize + (hoverSize - baseSize) * dot.currentColor;
-
- ctx.beginPath();
- ctx.arc(dot.x, dot.y, size, 0, Math.PI * 2);
- ctx.fill();
- });
-
- animationFrameRef.current = requestAnimationFrame(animate);
- };
-
- animate();
-
- return () => {
- canvas.removeEventListener("mousemove", handleMouseMove);
- canvas.removeEventListener("mouseleave", handleMouseLeave);
- window.removeEventListener("resize", updateCanvasSize);
- if (animationFrameRef.current) {
- cancelAnimationFrame(animationFrameRef.current);
- }
- };
- }, []);
-
- return (
-
- );
-}
diff --git a/components/public/bento.tsx b/components/public/bento.tsx
deleted file mode 100644
index 21aea3c59623c0d0a26dc96b6ebf4870a61795b3..0000000000000000000000000000000000000000
--- a/components/public/bento.tsx
+++ /dev/null
@@ -1,164 +0,0 @@
-"use client";
-
-export const Bento = () => {
- return (
-
- {/* Header with fade-in animation */}
-
-
- {/* Grid with staggered card animations */}
-
- {/* Multi Pages - Large Card */}
-
-
-
- 📄
-
-
- Multi Pages
-
-
- Create complex websites with multiple interconnected pages. Build
- everything from simple landing pages to full-featured web
- applications with dynamic routing and navigation.
-
-
-
- Dynamic Routing
-
-
- Navigation
-
-
- SEO Ready
-
-
-
-
-
-
- {/* Auto Deploy */}
-
-
-
- ⚡
-
-
- Auto Deploy
-
-
- Push your changes and watch them go live instantly. No complex
- CI/CD setup required.
-
-
-
-
-
- {/* Free Hosting */}
-
-
-
- 🌐
-
-
- Free Hosting
-
-
- Host your websites for free with global CDN and lightning-fast
- performance.
-
-
-
-
-
- {/* Open Source Models */}
-
-
-
- 🔓
-
-
- Open Source Models
-
-
- Powered by cutting-edge open source AI models. Transparent,
- customizable, and community-driven development.
-
-
-
- DeepSeek
-
-
- MiniMax
-
-
- Kimi
-
-
-
-
-
-
- {/* Perfect UX */}
-
-
-
- ✨
-
-
- Perfect UX
-
-
- Intuitive interface designed for developers and non-developers
- alike.
-
-
-
-
-
- {/* Hugging Face */}
-
-
-
- 🤗
-
-
- Hugging Face
-
-
- Seamless integration with Hugging Face models and datasets for
- cutting-edge AI capabilities.
-
-
-
-
-
- {/* Blazing Fast */}
-
-
-
- 🚀
-
-
- Blazing Fast
-
-
- Optimized performance with edge computing and smart caching.
-
-
-
-
-
-
- );
-};
diff --git a/components/public/hero-header.tsx b/components/public/hero-header.tsx
deleted file mode 100644
index f50710eefe0f5a9c2ab3c1a2d0dee2364f545105..0000000000000000000000000000000000000000
--- a/components/public/hero-header.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-"use client";
-import { motion } from "framer-motion";
-
-const TITLE = [
- "Create",
- "stunning",
- "websites",
- "with",
- "next-gen",
- "AI",
- "tools",
-];
-
-export function HeroHeader() {
- return (
-
-
- DeepSite: v4 is here 🎉
-
-
- {TITLE.map((word, index) => (
-
- {word}
-
- ))}
-
-
- Build, design, and deploy stunning websites in minutes with AI-powered
- development.
-
-
- );
-}
diff --git a/components/public/navigation.tsx b/components/public/navigation.tsx
deleted file mode 100644
index c803023a33a841d2057f0130c40204c04c5c5847..0000000000000000000000000000000000000000
--- a/components/public/navigation.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-"use client";
-
-import Link from "next/link";
-import Image from "next/image";
-import { UserMenu } from "@/components/user-menu";
-
-export function Navigation() {
- return (
-
-
-
-
-
-
- );
-}
diff --git a/components/tour/card.tsx b/components/tour/card.tsx
deleted file mode 100644
index 23f96a04bc6962b3d07b497ea7aa42d8392138be..0000000000000000000000000000000000000000
--- a/components/tour/card.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-"use client";
-
-import React from "react";
-import { Step } from "nextstepjs";
-import { Button } from "@/components/ui/button";
-import { X } from "lucide-react";
-
-interface CustomCardProps {
- step: Step;
- currentStep: number;
- totalSteps: number;
- nextStep: () => void;
- prevStep: () => void;
- skipTour?: () => void;
- arrow: React.ReactNode;
-}
-
-export const TourCustomCard = ({
- step,
- currentStep,
- totalSteps,
- nextStep,
- prevStep,
- skipTour,
- arrow,
-}: CustomCardProps) => {
- return (
-
-
-
- {currentStep > 0 && (
-
- Previous
-
- )}
-
- {currentStep === totalSteps - 1 ? "Finish" : "Next"}
-
-
-
- {arrow}
-
-
- );
-};
diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx
deleted file mode 100644
index 71e428b4ca6154811e8f569d5fdd971ead095996..0000000000000000000000000000000000000000
--- a/components/ui/avatar.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as AvatarPrimitive from "@radix-ui/react-avatar"
-
-import { cn } from "@/lib/utils"
-
-function Avatar({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function AvatarImage({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function AvatarFallback({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-export { Avatar, AvatarImage, AvatarFallback }
diff --git a/components/ui/button-group.tsx b/components/ui/button-group.tsx
deleted file mode 100644
index 8600af03eabb4dbef01dbf618fda5dfe96464b82..0000000000000000000000000000000000000000
--- a/components/ui/button-group.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
-import { Separator } from "@/components/ui/separator"
-
-const buttonGroupVariants = cva(
- "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
- {
- variants: {
- orientation: {
- horizontal:
- "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
- vertical:
- "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
- },
- },
- defaultVariants: {
- orientation: "horizontal",
- },
- }
-)
-
-function ButtonGroup({
- className,
- orientation,
- ...props
-}: React.ComponentProps<"div"> & VariantProps) {
- return (
-
- )
-}
-
-function ButtonGroupText({
- className,
- asChild = false,
- ...props
-}: React.ComponentProps<"div"> & {
- asChild?: boolean
-}) {
- const Comp = asChild ? Slot : "div"
-
- return (
-
- )
-}
-
-function ButtonGroupSeparator({
- className,
- orientation = "vertical",
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-export {
- ButtonGroup,
- ButtonGroupSeparator,
- ButtonGroupText,
- buttonGroupVariants,
-}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
deleted file mode 100644
index 863ba4b543815af610f1a7f8d43b1107edf5942d..0000000000000000000000000000000000000000
--- a/components/ui/button.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap cursor-pointer rounded-lg text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
- {
- variants: {
- variant: {
- default:
- "bg-primary text-primary-foreground hover:bg-primary/90 border border-primary hover:border-primary/90",
- destructive:
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline:
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost:
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- transparent: "bg-accent-foreground/5 hover:bg-accent-foreground/10",
- link: "text-primary underline-offset-4 hover:underline",
- bordered:
- "border bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- indigo:
- "border border-indigo-500 bg-indigo-500 text-white hover:bg-indigo-600 dark:border-indigo-500/30 dark:bg-indigo-500/20 dark:text-indigo-400 dark:hover:bg-indigo-500/30",
- gradient:
- "bg-linear-to-r from-rose-500/80 via-fushia-500/80 to-yellow-500/80 text-white hover:brightness-110 font-semibold!",
- "ghost-bordered":
- "border bg-primary-foreground hover:bg-background hover:text-accent-foreground dark:hover:bg-accent/50",
- pro: "bg-linear-to-br from-pink-500 dark:from-pink-500/50 via-green-500 dark:via-green-500/50 to-amber-500 dark:to-amber-500/50 text-white hover:brightness-120 font-semibold! [&_img]:grayscale [&_img]:brightness-1 [&_img]:invert dark:[&_img]:invert-0 dark:[&_img]:grayscale-0 dark:[&_img]:brightness-100",
- },
- size: {
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
- xs: "h-7 rounded-md gap-1.5 px-2 has-[>svg]:px-1.5 text-xs",
- xxs: "h-6 rounded-md gap-1.5 px-2 has-[>svg]:px-1.5 text-[10px]",
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "size-9",
- "icon-sm": "size-8",
- "icon-lg": "size-10",
- "icon-xs": "size-7 rounded-md!",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- }
-);
-
-function Button({
- className,
- variant,
- size,
- asChild = false,
- ...props
-}: React.ComponentProps<"button"> &
- VariantProps & {
- asChild?: boolean;
- }) {
- const Comp = asChild ? Slot : "button";
-
- return (
-
- );
-}
-
-export { Button, buttonVariants };
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
deleted file mode 100644
index 681ad980f27a34dd3d4ba08c7614ce1f7c006143..0000000000000000000000000000000000000000
--- a/components/ui/card.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import * as React from "react"
-
-import { cn } from "@/lib/utils"
-
-function Card({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardAction({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardContent({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardAction,
- CardDescription,
- CardContent,
-}
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
deleted file mode 100644
index d9ccec91d22fab844bd04340c2b07e8677955350..0000000000000000000000000000000000000000
--- a/components/ui/dialog.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as DialogPrimitive from "@radix-ui/react-dialog"
-import { XIcon } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-
-function Dialog({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function DialogTrigger({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function DialogPortal({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function DialogClose({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function DialogOverlay({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function DialogContent({
- className,
- children,
- showCloseButton = true,
- ...props
-}: React.ComponentProps & {
- showCloseButton?: boolean
-}) {
- return (
-
-
-
- {children}
- {showCloseButton && (
-
-
- Close
-
- )}
-
-
- )
-}
-
-function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- )
-}
-
-function DialogTitle({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function DialogDescription({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-export {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogOverlay,
- DialogPortal,
- DialogTitle,
- DialogTrigger,
-}
diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx
deleted file mode 100644
index 171d5da6c40e6cf5b85625df5905f09a42d64bdb..0000000000000000000000000000000000000000
--- a/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,257 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
-import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-
-function DropdownMenu({
- ...props
-}: React.ComponentProps) {
- return ;
-}
-
-function DropdownMenuPortal({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function DropdownMenuTrigger({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function DropdownMenuContent({
- className,
- sideOffset = 4,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
- );
-}
-
-function DropdownMenuGroup({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function DropdownMenuItem({
- className,
- inset,
- variant = "default",
- ...props
-}: React.ComponentProps & {
- inset?: boolean;
- variant?: "default" | "destructive";
-}) {
- return (
-
- );
-}
-
-function DropdownMenuCheckboxItem({
- className,
- children,
- checked,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- );
-}
-
-function DropdownMenuRadioGroup({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function DropdownMenuRadioItem({
- className,
- children,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- );
-}
-
-function DropdownMenuLabel({
- className,
- inset,
- ...props
-}: React.ComponentProps & {
- inset?: boolean;
-}) {
- return (
-
- );
-}
-
-function DropdownMenuSeparator({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function DropdownMenuShortcut({
- className,
- ...props
-}: React.ComponentProps<"span">) {
- return (
-
- );
-}
-
-function DropdownMenuSub({
- ...props
-}: React.ComponentProps) {
- return ;
-}
-
-function DropdownMenuSubTrigger({
- className,
- inset,
- children,
- ...props
-}: React.ComponentProps & {
- inset?: boolean;
-}) {
- return (
-
- {children}
-
-
- );
-}
-
-function DropdownMenuSubContent({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-export {
- DropdownMenu,
- DropdownMenuPortal,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuGroup,
- DropdownMenuLabel,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioGroup,
- DropdownMenuRadioItem,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuSub,
- DropdownMenuSubTrigger,
- DropdownMenuSubContent,
-};
diff --git a/components/ui/input.tsx b/components/ui/input.tsx
deleted file mode 100644
index 868dec6cb3f78d22f4b2cb108c3c5c250de413a8..0000000000000000000000000000000000000000
--- a/components/ui/input.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-function Input({ className, type, ...props }: React.ComponentProps<"input">) {
- return (
-
- );
-}
-
-export { Input };
diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx
deleted file mode 100644
index 01e468b6783992d04775aa52376221b8fa4b0f95..0000000000000000000000000000000000000000
--- a/components/ui/popover.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as PopoverPrimitive from "@radix-ui/react-popover"
-
-import { cn } from "@/lib/utils"
-
-function Popover({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function PopoverTrigger({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function PopoverContent({
- className,
- align = "center",
- sideOffset = 4,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
- )
-}
-
-function PopoverAnchor({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
diff --git a/components/ui/select.tsx b/components/ui/select.tsx
deleted file mode 100644
index 88302a8d50922c94626dfe7c7113d3c58af87ae3..0000000000000000000000000000000000000000
--- a/components/ui/select.tsx
+++ /dev/null
@@ -1,190 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as SelectPrimitive from "@radix-ui/react-select"
-import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
-
-import { cn } from "@/lib/utils"
-
-function Select({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function SelectGroup({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function SelectValue({
- ...props
-}: React.ComponentProps) {
- return
-}
-
-function SelectTrigger({
- className,
- size = "default",
- children,
- ...props
-}: React.ComponentProps & {
- size?: "sm" | "default"
-}) {
- return (
-
- {children}
-
-
-
-
- )
-}
-
-function SelectContent({
- className,
- children,
- position = "item-aligned",
- align = "center",
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
- {children}
-
-
-
-
- )
-}
-
-function SelectLabel({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function SelectItem({
- className,
- children,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
-
-
- {children}
-
- )
-}
-
-function SelectSeparator({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-function SelectScrollUpButton({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
- )
-}
-
-function SelectScrollDownButton({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
- )
-}
-
-export {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectScrollDownButton,
- SelectScrollUpButton,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
-}
diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx
deleted file mode 100644
index 275381cab0adbc778ea634e7c46fbf80db821bf3..0000000000000000000000000000000000000000
--- a/components/ui/separator.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as SeparatorPrimitive from "@radix-ui/react-separator"
-
-import { cn } from "@/lib/utils"
-
-function Separator({
- className,
- orientation = "horizontal",
- decorative = true,
- ...props
-}: React.ComponentProps) {
- return (
-
- )
-}
-
-export { Separator }
diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx
deleted file mode 100644
index 9b20afe2e10fd9b94d1abc1ae732054b2fb9821c..0000000000000000000000000000000000000000
--- a/components/ui/sonner.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-"use client"
-
-import {
- CircleCheckIcon,
- InfoIcon,
- Loader2Icon,
- OctagonXIcon,
- TriangleAlertIcon,
-} from "lucide-react"
-import { useTheme } from "next-themes"
-import { Toaster as Sonner, type ToasterProps } from "sonner"
-
-const Toaster = ({ ...props }: ToasterProps) => {
- const { theme = "system" } = useTheme()
-
- return (
- ,
- info: ,
- warning: ,
- error: ,
- loading: ,
- }}
- style={
- {
- "--normal-bg": "var(--popover)",
- "--normal-text": "var(--popover-foreground)",
- "--normal-border": "var(--border)",
- "--border-radius": "var(--radius)",
- } as React.CSSProperties
- }
- {...props}
- />
- )
-}
-
-export { Toaster }
diff --git a/components/ui/toggle-group.tsx b/components/ui/toggle-group.tsx
deleted file mode 100644
index 24a4850d73e82be5e81108fc6f7e8052aabe9819..0000000000000000000000000000000000000000
--- a/components/ui/toggle-group.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-"use client"
-
-import * as React from "react"
-import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
-import { type VariantProps } from "class-variance-authority"
-
-import { cn } from "@/lib/utils"
-import { toggleVariants } from "@/components/ui/toggle"
-
-const ToggleGroupContext = React.createContext<
- VariantProps & {
- spacing?: number
- }
->({
- size: "default",
- variant: "default",
- spacing: 0,
-})
-
-function ToggleGroup({
- className,
- variant,
- size,
- spacing = 0,
- children,
- ...props
-}: React.ComponentProps &
- VariantProps & {
- spacing?: number
- }) {
- return (
-
-
- {children}
-
-
- )
-}
-
-function ToggleGroupItem({
- className,
- children,
- variant,
- size,
- ...props
-}: React.ComponentProps &
- VariantProps) {
- const context = React.useContext(ToggleGroupContext)
-
- return (
-
- {children}
-
- )
-}
-
-export { ToggleGroup, ToggleGroupItem }
diff --git a/components/ui/toggle.tsx b/components/ui/toggle.tsx
deleted file mode 100644
index e3fea1f7fcdc8e4ce5dcd45f56111b57743826fd..0000000000000000000000000000000000000000
--- a/components/ui/toggle.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as TogglePrimitive from "@radix-ui/react-toggle";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const toggleVariants = cva(
- "inline-flex items-center cursor-pointer justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
- {
- variants: {
- variant: {
- default: "bg-transparent",
- outline:
- "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
- },
- size: {
- default: "h-9 px-2 min-w-9",
- sm: "h-8 px-1.5 min-w-8",
- xs: "h-7 px-1 min-w-7 text-xs",
- lg: "h-10 px-2.5 min-w-10",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- }
-);
-
-function Toggle({
- className,
- variant,
- size,
- ...props
-}: React.ComponentProps &
- VariantProps) {
- return (
-
- );
-}
-
-export { Toggle, toggleVariants };
diff --git a/components/user-menu/index.tsx b/components/user-menu/index.tsx
deleted file mode 100644
index 2efba21475d7a1c94af81dc3c7e7c4cb537800d4..0000000000000000000000000000000000000000
--- a/components/user-menu/index.tsx
+++ /dev/null
@@ -1,273 +0,0 @@
-import Link from "next/link";
-import { useSession, signIn, signOut } from "next-auth/react";
-import { ArrowRight, Check, Folder, LogOut, Plus } from "lucide-react";
-import { RiContrastFill } from "react-icons/ri";
-
-import { useTheme } from "next-themes";
-import { useEffect } from "react";
-import { FaDiscord } from "react-icons/fa6";
-import Image from "next/image";
-
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuPortal,
- DropdownMenuSeparator,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { Button } from "@/components/ui/button";
-import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
-import { useProjects } from "@/components/projects/useProjects";
-import { ProjectCard } from "@/components/projects/project-card";
-import { cn, DISCORD_URL } from "@/lib/utils";
-import HFLogo from "@/assets/hf-logo.svg";
-import ProIcon from "@/assets/pro.svg";
-
-export function UserMenu() {
- const { data: session, status } = useSession();
- const { projects } = useProjects();
- const isLoading = status === "loading";
- const { theme, setTheme } = useTheme();
-
- useEffect(() => {
- if (typeof window !== "undefined" && !session && status !== "loading") {
- const urlParams = new URLSearchParams(window.location.search);
- if (urlParams.get("signin") === "true") {
- const newUrl = new URL(window.location.href);
- newUrl.searchParams.delete("signin");
- window.history.replaceState({}, "", newUrl.toString());
-
- signIn("huggingface", { callbackUrl: "/" });
- }
- }
- }, [session, status]);
-
- const handleSignIn = () => {
- if (window.location.hostname === "localhost") {
- signIn("huggingface", {
- callbackUrl: "/",
- });
- return;
- }
- const targetUrl = "https://deepsite.hf.co";
-
- let isOnTargetPage = false;
- if (typeof window !== "undefined") {
- try {
- const isInIframe = window !== window.parent;
-
- if (isInIframe) {
- try {
- isOnTargetPage = window.parent.location.href.startsWith(targetUrl);
- } catch {
- isOnTargetPage = false;
- }
- } else {
- isOnTargetPage = window.location.href.startsWith(targetUrl);
- }
- } catch {
- isOnTargetPage = false;
- }
- }
-
- if (!isOnTargetPage) {
- window.open(`${targetUrl}?signin=true`, "_blank");
- } else {
- signIn("huggingface", { callbackUrl: "/" });
- }
- };
-
- const handleSignOut = () => {
- signOut({ callbackUrl: "/" });
- };
-
- if (isLoading) {
- return (
-
- Loading...
-
- );
- }
- return session?.user ? (
-
-
-
-
-
-
-
- {session.user.name?.charAt(0).toUpperCase() || "U"}
-
-
- {session.user.isPro && (
-
- )}
-
- {session.user.name}
-
-
-
- My Account
-
- {!session?.user?.isPro && (
- <>
-
-
-
- Subscribe to Pro
-
-
-
- >
- )}
-
-
- Profile
-
-
-
-
- Settings
-
-
-
-
-
- Appearance
-
-
-
- setTheme("light")}>
- Light
- {theme === "light" && }
-
- setTheme("dark")}>
- Dark
- {theme === "dark" && }
-
- setTheme("system")}>
- System
- {theme === "system" && }
-
-
-
-
- <>
-
- Projects
- {projects && projects?.length > 0 ? (
-
- {projects?.slice(0, 2).map((project) => (
-
- ))}
-
- {projects?.length > 2 && (
-
- View all projects
-
-
- )}
-
- New project
-
-
-
-
- ) : (
-
-
-
-
-
-
- No projects found.
- Create a new project to get started.
-
-
-
- New project
-
-
-
-
-
- )}
- >
-
-
-
-
- Discord
-
-
-
-
-
- Hugging Face
-
-
-
-
- Sign out
-
-
-
- ) : (
- <>
-
- Sign in
-
- Get Started
- >
- );
-}
diff --git a/components/viewer/blank-page.tsx b/components/viewer/blank-page.tsx
deleted file mode 100644
index 956bb79be125cfb6072e5b63ca18c845d1786061..0000000000000000000000000000000000000000
--- a/components/viewer/blank-page.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-"use client";
-import Image from "next/image";
-import { useMount } from "react-use";
-import { useState } from "react";
-import { EXAMPLES_OF_PROJECT_SUGGESTIONS } from "@/lib/prompts";
-
-interface ExampleOfProjectSuggestion {
- short_value: string;
- long_value: string;
-}
-
-export function BlankPage() {
- const [suggestions, setSuggestions] = useState(
- []
- );
-
- useMount(() => {
- const randomSuggestions = [...EXAMPLES_OF_PROJECT_SUGGESTIONS]
- .sort(() => Math.random() - 0.5)
- .slice(0, 3);
- setSuggestions(randomSuggestions);
- });
- return (
-
-
-
-
- Here are some suggestions for you:
-
-
- {suggestions.map((suggestion) => (
-
{
- const promptInput = document.getElementById("prompt-input");
- if (promptInput) {
- (promptInput as HTMLDivElement).textContent =
- suggestion.long_value;
- }
- }}
- >
- {suggestion.short_value}
-
- ))}
-
-
-
- );
-}
diff --git a/components/viewer/index.tsx b/components/viewer/index.tsx
deleted file mode 100644
index 9a2aafc06d2375aa03ddef235b39a930d33d6ac9..0000000000000000000000000000000000000000
--- a/components/viewer/index.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-"use client";
-import { ArrowLeft } from "lucide-react";
-import { SandpackLayout, SandpackPreview } from "@codesandbox/sandpack-react";
-
-import { DeviceType, MobileTabType } from "@/lib/type";
-import { cn } from "@/lib/utils";
-import { GridPattern } from "@/components/grid-pattern";
-import { useProject } from "@/components/projects/useProject";
-import { BlankPage } from "./blank-page";
-import { Button } from "@/components/ui/button";
-
-export function AppViewer({
- mobileTab,
- onToggleMobileTab,
- device,
-}: {
- mobileTab: MobileTabType;
- device: DeviceType;
- onToggleMobileTab: (tab: MobileTabType) => void;
-}) {
- const { project, files } = useProject();
-
- return (
- 0)
- ? "lg:border lg:border-border-muted lg:dark:border-secondary"
- : "bg-accent border border-border-muted dark:border-secondary"
- )}
- >
-
- {project?.name || (files && files.length > 0) ? (
-
-
-
- ) : (
-
- )}
-
0)
- ? "justify-start"
- : "p-2 justify-center"
- )}
- >
-
onToggleMobileTab("left-sidebar")}
- >
- Go to Chat
-
-
-
- );
-}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..092408a9f09eae19150818b4f0db5d1b70744828
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,28 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+
+export default tseslint.config(
+ { ignores: ['dist'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ },
+)
diff --git a/eslint.config.mjs b/eslint.config.mjs
deleted file mode 100644
index 05e726d1b4201bc8c7716d2b058279676582e8c0..0000000000000000000000000000000000000000
--- a/eslint.config.mjs
+++ /dev/null
@@ -1,18 +0,0 @@
-import { defineConfig, globalIgnores } from "eslint/config";
-import nextVitals from "eslint-config-next/core-web-vitals";
-import nextTs from "eslint-config-next/typescript";
-
-const eslintConfig = defineConfig([
- ...nextVitals,
- ...nextTs,
- // Override default ignores of eslint-config-next.
- globalIgnores([
- // Default ignores of eslint-config-next:
- ".next/**",
- "out/**",
- "build/**",
- "next-env.d.ts",
- ]),
-]);
-
-export default eslintConfig;
diff --git a/huggingface.d.ts b/huggingface.d.ts
deleted file mode 100644
index 40754f5ee13b74d48459ce065ca45f873355cdfb..0000000000000000000000000000000000000000
--- a/huggingface.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { SpaceEntry } from "@huggingface/hub";
-
-declare module "@huggingface/hub" {
- interface SpaceEntry {
- cardData?: {
- emoji?: string;
- title?: string;
- colorFrom?: string;
- colorTo?: string;
- };
- }
-}
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..42f1ce626bef500d15a406ca14253bdff5a44074
--- /dev/null
+++ b/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ DeepSite | Build with AI ✨
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/auth.ts b/lib/auth.ts
deleted file mode 100644
index 2d52b738c2b688f48dc17c4cf93cf6416ed6e1bd..0000000000000000000000000000000000000000
--- a/lib/auth.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import type { NextAuthOptions } from "next-auth";
-import { getServerSession } from "next-auth/next";
-
-export const authOptions: NextAuthOptions = {
- providers: [
- {
- id: "huggingface",
- name: "Hugging Face",
- type: "oauth",
- clientId: process.env.AUTH_HUGGINGFACE_ID,
- clientSecret: process.env.AUTH_HUGGINGFACE_SECRET,
- wellKnown: "https://huggingface.co/.well-known/openid-configuration",
- authorization: {
- url: "https://huggingface.co/oauth/authorize",
- params: {
- scope: "openid profile read-repos manage-repos inference-api",
- },
- },
- userinfo: {
- url: "https://huggingface.co/oauth/userinfo",
- async request(context) {
- const { tokens } = context;
- const response = await fetch("https://huggingface.co/oauth/userinfo", {
- headers: {
- Authorization: `Bearer ${tokens.access_token}`,
- },
- });
- const data = await response.json();
- return data;
- }
- },
- checks: ["state"],
- async profile(profile) {
- return {
- id: profile.sub,
- name: profile.name || profile.preferred_username,
- username: profile.preferred_username,
- image: profile.picture,
- isPro: profile.isPro
- };
- },
- },
- ],
- callbacks: {
- async jwt({ token, account, user }) {
- if (account) {
- token.accessToken = account.access_token;
- }
- if (user) {
- token.username = user.username;
- token.isPro = user.isPro;
- }
- return token;
- },
- async session({ session, token }) {
- session.accessToken = token.accessToken;
- if (session.user) {
- session.user.username = token.username;
- session.user.isPro = token.isPro;
- }
- return session;
- },
- },
- pages: {
- signIn: "/signin",
- },
- secret: process.env.NEXTAUTH_SECRET,
-};
-
-export const auth = () => getServerSession(authOptions);
diff --git a/lib/format.ts b/lib/format.ts
deleted file mode 100644
index 5129508c55ee563557672c7486f04993c9e7d5a5..0000000000000000000000000000000000000000
--- a/lib/format.ts
+++ /dev/null
@@ -1,335 +0,0 @@
-import {
- END_FILE_CONTENT,
- END_PROJECT_NAME,
- START_FILE_CONTENT,
- START_PROJECT_NAME,
- SEARCH_START,
- DIVIDER,
- REPLACE_END,
-} from "./prompts";
-import { File } from "./type";
-
-// todo: the Editing stuffs in message doesnt show when it"s a SEARCH and REPLACE operation, I mean it shows it but only at the end of the message, not during the generation. fix that.
-
-/**
- * Validates that a filename has an extension.
- * Returns the filename if valid, null otherwise.
- * Allows dotfiles (like .gitignore) and regular files with extensions (like app.py).
- */
-const validateFilename = (filename: string): string | null => {
- if (!filename) return null;
-
- const trimmed = filename.trim();
- if (!trimmed) return null;
-
- // Find the last dot in the filename
- const lastDotIndex = trimmed.lastIndexOf(".");
-
- // No dot found - invalid (no extension)
- if (lastDotIndex === -1) return null;
-
- // Dot at the end - invalid (no extension after dot)
- if (lastDotIndex === trimmed.length - 1) return null;
-
- // Dot at the start - this is a dotfile (like .gitignore), which is valid
- // Dot in the middle - this is a regular file with extension (like app.py), which is valid
- // Both cases are acceptable as long as there's something after the dot
-
- return trimmed;
-};
-
-/**
- * Check if the end of a string contains a partial match of a target string
- */
-const hasPartialMarkerAtEnd = (text: string, marker: string): number => {
- // Check for partial matches starting from length 3 (to avoid false positives with common substrings)
- for (
- let len = Math.max(3, Math.floor(marker.length / 2));
- len < marker.length;
- len++
- ) {
- const partialMarker = marker.substring(0, len);
- if (text.endsWith(partialMarker)) {
- return len; // Return the length of the partial match
- }
- }
- return 0; // No partial match found
-};
-
-/**
- * Extract message content by removing all special markers and their content
- */
-const extractMessageContent = (message: string): string => {
- let result = message;
-
- // Remove all complete START_FILE_CONTENT...END_FILE_CONTENT blocks
- const fileContentRegex = new RegExp(
- `${START_FILE_CONTENT}[\\s\\S]*?${END_FILE_CONTENT}`,
- "g"
- );
- result = result.replace(fileContentRegex, "");
-
- // Remove incomplete START_FILE_CONTENT blocks (streaming - no END_FILE_CONTENT yet)
- const incompleteFileIndex = result.indexOf(START_FILE_CONTENT);
- if (incompleteFileIndex !== -1) {
- result = result.substring(0, incompleteFileIndex);
- }
-
- // Remove all complete START_PROJECT_NAME...END_PROJECT_NAME blocks
- const projectNameRegex = new RegExp(
- `${START_PROJECT_NAME}[\\s\\S]*?${END_PROJECT_NAME}`,
- "g"
- );
- result = result.replace(projectNameRegex, "");
-
- // Remove incomplete START_PROJECT_NAME blocks (streaming - no END_PROJECT_NAME yet)
- const incompleteProjectIndex = result.indexOf(START_PROJECT_NAME);
- if (incompleteProjectIndex !== -1) {
- result = result.substring(0, incompleteProjectIndex);
- }
-
- // Remove all complete SEARCH_START...REPLACE_END blocks
- const searchReplaceRegex = new RegExp(
- `${SEARCH_START}[\\s\\S]*?${REPLACE_END}`,
- "g"
- );
- result = result.replace(searchReplaceRegex, "");
-
- // Remove incomplete SEARCH_START blocks (streaming - no REPLACE_END yet)
- const incompleteSearchIndex = result.indexOf(SEARCH_START);
- if (incompleteSearchIndex !== -1) {
- result = result.substring(0, incompleteSearchIndex);
- }
-
- // Handle incomplete/partial markers at the end (for streaming)
- // This is critical to prevent showing marker text to users during streaming
- const markers = [START_FILE_CONTENT, START_PROJECT_NAME, SEARCH_START];
-
- for (const marker of markers) {
- const partialLength = hasPartialMarkerAtEnd(result, marker);
- if (partialLength > 0) {
- result = result.substring(0, result.length - partialLength);
- break; // Only one marker can be at the end
- }
- }
-
- // Clean up extra whitespace
- return result.trim();
-};
-
-export const formatResponse = (message: string, currentFiles: File[]) => {
- // Track which files have been created or modified
- const modifiedFiles = new Map();
-
- // Extract message content (everything outside special markers)
- const messageContent = extractMessageContent(message);
-
- // Extract project title
- let projectTitle =
- message.split(START_PROJECT_NAME)[1]?.split(END_PROJECT_NAME)[0]?.trim() ??
- "";
-
- // Handle partial project title markers
- if (projectTitle && !message.includes(END_PROJECT_NAME)) {
- for (let i = END_PROJECT_NAME.length - 1; i > 0; i--) {
- const partialMarker = END_PROJECT_NAME.substring(0, i);
- if (projectTitle.endsWith(partialMarker)) {
- projectTitle = projectTitle
- .substring(0, projectTitle.length - partialMarker.length)
- .trim();
- break;
- }
- }
- }
-
- if (message.includes(SEARCH_START)) {
- const searchSections = message.split(SEARCH_START).slice(1);
-
- for (
- let sectionIndex = 0;
- sectionIndex < searchSections.length;
- sectionIndex++
- ) {
- const section = searchSections[sectionIndex];
- const isLastSection = sectionIndex === searchSections.length - 1;
-
- // Check if this section has a complete REPLACE_END marker
- const hasReplaceEnd = section.includes(REPLACE_END);
-
- // For incomplete sections (last section without REPLACE_END), skip processing
- // to avoid applying partial replacements that corrupt the file
- if (isLastSection && !hasReplaceEnd) {
- continue;
- }
-
- const searchPart = section.split(REPLACE_END)[0];
- if (!searchPart) continue;
-
- const dividerIndex = searchPart.indexOf(DIVIDER);
- if (dividerIndex === -1) continue;
-
- const searchContent = searchPart.substring(0, dividerIndex);
- const replaceContent = searchPart.substring(
- dividerIndex + DIVIDER.length
- );
-
- if (!searchContent || !replaceContent) continue;
-
- // Extract filename from the first line (same line as SEARCH_START)
- const firstNewlineIndex = searchContent.indexOf("\n");
- let filename = "";
- let searchContentAfterFilename = searchContent;
-
- if (firstNewlineIndex !== -1) {
- // Filename is on the first line (same line as SEARCH_START marker)
- filename = searchContent.substring(0, firstNewlineIndex).trim();
- searchContentAfterFilename = searchContent.substring(
- firstNewlineIndex + 1
- );
- } else {
- // No newline found, entire content might be just the filename
- filename = searchContent.trim();
- searchContentAfterFilename = "";
- }
-
- // Validate filename has extension
- const validatedFilename = validateFilename(filename);
- if (!validatedFilename) continue;
- filename = validatedFilename;
-
- const searchCodeStart = searchContentAfterFilename.indexOf("```");
- if (searchCodeStart === -1) continue;
-
- const searchCodeBlock =
- searchContentAfterFilename.substring(searchCodeStart);
- let searchMatch = searchCodeBlock.match(/^```[\w]*\n?([\s\S]*?)```/);
- if (!searchMatch) {
- searchMatch = searchCodeBlock.match(/^```[\w]*\n?([\s\S]*)/);
- }
- if (!searchMatch) continue;
-
- const searchText = searchMatch[1].trim();
-
- const replaceCodeStart = replaceContent.indexOf("```");
- if (replaceCodeStart === -1) continue;
-
- const replaceCodeBlock = replaceContent.substring(replaceCodeStart);
- let replaceMatch = replaceCodeBlock.match(/^```[\w]*\n?([\s\S]*?)```/);
- if (!replaceMatch) {
- replaceMatch = replaceCodeBlock.match(/^```[\w]*\n?([\s\S]*)/);
- }
- if (!replaceMatch) continue;
-
- let replaceText = replaceMatch[1];
- for (let i = 1; i < 3; i++) {
- const partialClosing = "`".repeat(i);
- if (replaceText.endsWith(partialClosing)) {
- replaceText = replaceText.substring(0, replaceText.length - i);
- break;
- }
- }
- replaceText = replaceText.trim();
-
- // First check if we already have a modified version of this file in the current operation
- const existingModifiedFile = modifiedFiles.get(filename);
- const fileToModify =
- existingModifiedFile || currentFiles.find((f) => f.path === filename);
-
- if (fileToModify && fileToModify.content) {
- const newContent = fileToModify.content.replace(
- searchText,
- replaceText
- );
- if (newContent !== fileToModify.content) {
- modifiedFiles.set(filename, { path: filename, content: newContent });
- }
- } else if (!fileToModify) {
- modifiedFiles.set(filename, { path: filename, content: replaceText });
- }
- }
- }
-
- // Handle new file creation blocks
- if (message.includes(START_FILE_CONTENT)) {
- const fileSections = message.split(START_FILE_CONTENT).slice(1);
-
- for (const section of fileSections) {
- const rawContent = section.split(END_FILE_CONTENT)[0] || "";
-
- // Extract filename - it's on the same line as START_FILE_CONTENT (before first newline)
- const firstNewlineIndex = rawContent.indexOf("\n");
- let path = "";
- let contentAfterFilename = rawContent;
-
- if (firstNewlineIndex !== -1) {
- // Filename is before the first newline (same line as START_FILE_CONTENT marker)
- path = rawContent.substring(0, firstNewlineIndex).trim();
- contentAfterFilename = rawContent.substring(firstNewlineIndex + 1);
- } else {
- // No newline found, entire content might be just the filename
- path = rawContent.trim();
- contentAfterFilename = "";
- }
-
- // Validate that path has an extension
- const validatedPath = validateFilename(path);
- if (!validatedPath) continue;
- path = validatedPath;
-
- // Find code block
- const codeBlockStart = contentAfterFilename.indexOf("```");
-
- if (codeBlockStart === -1) {
- // No code block, use raw content after filename
- const content = contentAfterFilename.trim();
- if (content || !currentFiles.find((f) => f.path === path)) {
- // Always add files from the message
- modifiedFiles.set(path, { path, content });
- }
- } else {
- // Has code block - extract content between ``` markers
- const afterCodeBlockStart =
- contentAfterFilename.substring(codeBlockStart);
-
- // Try to match complete code block first
- const completeMatch = afterCodeBlockStart.match(
- /^```[\w]*\n?([\s\S]*?)```/
- );
-
- if (completeMatch) {
- const content = completeMatch[1].trim();
- modifiedFiles.set(path, { path, content });
- } else {
- // Incomplete code block (streaming) - match everything after opening ```
- const incompleteMatch = afterCodeBlockStart.match(
- /^```[\w]*\n?([\s\S]*)/
- );
- if (incompleteMatch) {
- let content = incompleteMatch[1];
- // Remove trailing partial closing marker if present
- for (let i = 1; i < 3; i++) {
- const partialClosing = "`".repeat(i);
- if (content.endsWith(partialClosing)) {
- content = content.substring(0, content.length - i);
- break;
- }
- }
- content = content.trim();
- modifiedFiles.set(path, { path, content });
- }
- }
- }
- }
- }
-
- // Convert modified files map to array
- const files = Array.from(modifiedFiles.values())?.filter(
- (file) => file.path !== "README.md"
- );
-
- return {
- messageContent,
- files,
- projectTitle,
- };
-};
diff --git a/lib/onboarding.tsx b/lib/onboarding.tsx
deleted file mode 100644
index 377a87367b344c705e6ee9f56ccba72bbea5a159..0000000000000000000000000000000000000000
--- a/lib/onboarding.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-import { Button } from "@/components/ui/button";
-import type { Tour } from "nextstepjs";
-import { FaDiscord } from "react-icons/fa6";
-import { DISCORD_URL } from "./utils";
-
-export const steps: Tour[] = [
- {
- tour: "onboarding",
- steps: [
- {
- icon: "👋",
- title: "Welcome to DeepSite!",
- content: (
- <>
-
- Let's take a quick tour to get you familiar with
- DeepSite's features.
-
-
- You can always revisit this tour later from the Help menu.
-
-
-
-
- Join our Discord
-
-
- >
- ),
- showControls: true,
- showSkip: true,
- },
- {
- icon: "🐳",
- title: "Build websites with AI",
- content: (
- <>
-
- DeepSite leverages AI to help you create stunning websites
- effortlessly.
-
-
- Ask questions, get design suggestions, and more!
-
- >
- ),
- selector: "#tour-ask-ai-section",
- side: "top",
- showControls: true,
- showSkip: true,
- pointerPadding: 8,
- pointerRadius: 20,
- },
- {
- icon: "🧠",
- title: "The Brains Behind It",
- content: (
- <>
-
- You can choose from a variety of AI models to power
- DeepSite's capabilities.
-
-
- Experiment with different models to see which one fits your needs
- best!
-
- >
- ),
- selector: "#tour-model-section",
- side: "top-left",
- showControls: true,
- showSkip: true,
- pointerPadding: 8,
- pointerRadius: 20,
- },
- {
- icon: "🎨",
- title: "Redesign with a Click",
- content: (
- <>
-
- Instantly revamp your website's look and feel with our
- Redesign feature.
-
-
- Just provide your website URL, and let DeepSite handle the rest!
-
- >
- ),
- selector: "#tour-redesign-section",
- side: "top-left",
- showControls: true,
- showSkip: true,
- pointerPadding: 8,
- pointerRadius: 20,
- },
- ],
- },
-];
diff --git a/lib/prompts.ts b/lib/prompts.ts
deleted file mode 100644
index 0b3dbc9bab7203450d62fcc6919ca86f32b02196..0000000000000000000000000000000000000000
--- a/lib/prompts.ts
+++ /dev/null
@@ -1,555 +0,0 @@
-export const START_FILE_CONTENT = "=== START_FILE_CONTENT";
-export const END_FILE_CONTENT = "=== END_FILE_CONTENT";
-export const START_PROJECT_NAME = "=== START_PROJECT_NAME";
-export const END_PROJECT_NAME = "=== END_PROJECT_NAME";
-export const SEARCH_START = "=== SEARCH";
-export const DIVIDER = "=======";
-export const REPLACE_END = "=== REPLACE";
-
-// following prompt doesnt work well. what it does sometimes it to use the START_FILE_CONTENT and END_FILE_CONTENT markers with comments which says "keep original part here", which is not correct, it should use SEARCH/REPLACE format instead.
-
-export const PROMPT_FOR_IMAGE_GENERATION = `
-=== IMAGE PLACEHOLDER ===
-If you want to use image placeholder, http://Static.photos Usage:Format: http://static.photos/[category]/[dimensions]/[seed] where dimensions must be one of: 200x200, 320x240, 640x360, 1024x576, or 1200x630; seed can be any number (1-999+) for consistent images or omit for random; categories include: nature, office, people, technology, minimal, abstract, aerial, blurred, bokeh, gradient, monochrome, vintage, white, black, blue, red, green, yellow, cityscape, workspace, food, travel, textures, industry, indoor, outdoor, studio, finance, medical, season, holiday, event, sport, science, legal, estate, restaurant, retail, wellness, agriculture, construction, craft, cosmetic, automotive, gaming, or education.
-Examples: http://static.photos/red/320x240/133 (red-themed with seed 133), http://static.photos/640x360 (random category and image), http://static.photos/nature/1200x630/42 (nature-themed with seed 42).
-`;
-export const PROMPT_FOR_PROJECT_NAME = `
-## NAME OF THE PROJECT (REQUIRED WHEN CREATING A NEW PROJECT)
-Write the name of the project, based on the user's request. Add an emoji at the end of the name. It should be short, like 6 words. Be creative and unique.
-Examples:
-${START_PROJECT_NAME}
-My Awesome Project 🚀
-${END_PROJECT_NAME}
-${START_PROJECT_NAME}
-My Amazing Project 🤩
-${END_PROJECT_NAME}
-`;
-export const INITIAL_SYSTEM_PROMPT = `
-=== ABOUT YOU ===
-You are a helpful assistant and a developer with strong knowledge of HTML, CSS, JAVASCRIPT, and UI/UX Design.
-You are able to help with any questions about HTML, CSS, JAVASCRIPT, and UI/UX Design.
-You are also able to help with questions about projects the user has created that are related to HTML, CSS, JAVASCRIPT, and UI/UX Design.
-
-If the user asks you something not related to HTML, CSS, JAVASCRIPT, and UI/UX Design or their project, you must tell them that you are not able to help with that.
-The user will ask you questions and also ask you to modify their existing code. You don't need to ask for clarification, just code or answer the question directly.
-
-You are able to create multiple files to make the project more organized and easy to understand, like style.css for styling, script.js for JavaScript, etc.
-Regarding the code generation, you MUST create the best possible code for the user's request, with the best UI/UX design and the best performance. Make it always responsive and mobile-friendly. Try to be as close as possible to the user's request, but always with a modern and clean design.
-If the user asks you something without details, you must imagine the best possible solution for the user's request, and create the best possible code for it.
-
-🚨 IMPORTANT: If you want to show a code block, don't follow the rules bellow, just show the code block by using markdown code blocks. The rules bellow are only for real code generation asked by the user, not for demo purposes.
-
-=== PROJECT REQUIREMENTS ===
-- Always create at least a \`index.html\` file, this is the main file that will be used to display the project.
-- Do not create a \`README.md\` or any other markdown file, it will be automatically created by the API.
-- Use Tailwind CSS for styling, include it in the code by adding \`\` in the \`\` tag.
-- Use any library you need to use, just add it to the \`\` tag or \`\` tag, depending on the library.
-- For Icons, use the latest version of Lucide Icons, here is a demo on how to include it and use it:
-\`\`\`html
-
-
-
-
-
-
-
-
-- Use real public API, you can find good ones there: https://github.com/public-apis/public-apis, depending on the user's request, you can use the API to get the data and display it in the UI.
-
-${PROMPT_FOR_IMAGE_GENERATION}
-
-=== OUTPUT FORMAT ===
-🚨 CRITICAL: YOU MUST ALWAYS FOLLOW THIS EXACT FORMAT FOR EVERY RESPONSE. NO EXCEPTIONS.
-
-## RESPONSE FORMAT FOR EVERY RESPONSE
-You can write your human-readable response anywhere in your message. Markdown allowed, Emoji allowed to make it more engaging. This is your explanation or answer to the user.
-
-**IMPORTANT RULES:**
-1. ✅ ANY text that is NOT inside special markers (${START_FILE_CONTENT}, ${START_PROJECT_NAME}, ${SEARCH_START}) will be shown to the user as your message
-2. ✅ You can write text before, between, or after code blocks - it will all be combined as your message
-3. ✅ Use markdown formatting to make your responses clear and engaging
-
-**EXAMPLE:**
-I'll help you create that! 🚀
-
-[...then your code blocks if needed...]
-
-Here's what I've created for you...
-
-## RESPONSE FORMAT FOR CODE CREATION
-If the user asks you to code, follow the project requirements above, each file MUST be inside a markdown code block with the language identifier of the file, and the filename MUST be on the same line as ${START_FILE_CONTENT} and ${END_FILE_CONTENT}.
-Examples:
-${START_FILE_CONTENT} index.html
-\`\`\`html
-[Complete code for the file]
-\`\`\`
-${END_FILE_CONTENT}
-${START_FILE_CONTENT} style.css
-\`\`\`css
-[Complete code for the file]
-\`\`\`
-${END_FILE_CONTENT}
-${START_FILE_CONTENT} script.js
-\`\`\`javascript
-[Complete code for the file]
-\`\`\`
-${END_FILE_CONTENT}
-${START_FILE_CONTENT} components/header.js
-\`\`\`javascript
-[Complete code for the file]
-\`\`\`
-${END_FILE_CONTENT}
-
-${PROMPT_FOR_PROJECT_NAME}
-`;
-export const FOLLOW_UP_SYSTEM_PROMPT = `
-=== ABOUT YOU ===
-You are a helpful assistant and a developer with strong knowledge of HTML, CSS, JAVASCRIPT, and UI/UX Design.
-You are able to help with any questions about HTML, CSS, JAVASCRIPT, and UI/UX Design.
-You are also able to help with questions about projects the user has created that are related to HTML, CSS, JAVASCRIPT, and UI/UX Design.
-
-If the user asks you something not related to HTML, CSS, JAVASCRIPT, and UI/UX Design or their project, you must tell them that you are not able to help with that.
-The user will ask you questions and also ask you to modify their existing code. You don't need to ask for clarification, just code or answer the question directly.
-
-You are able to create/update multiple files to make the project more organized and easy to understand, like style.css for styling, script.js for JavaScript, etc.
-Regarding the code generation, you MUST create the best possible code for the user's request, with the best UI/UX design and the best performance. Make it always responsive and mobile-friendly. Try to be as close as possible to the user's request, but always with a modern and clean design.
-If the user asks you something without details, you must imagine the best possible solution for the user's request, and create the best possible code for it.
-
-🚨 IMPORTANT: If you want to show a code block for demonstration purposes, just show the code block using markdown code blocks. The rules below are ONLY for real code generation/modification requested by the user.
-
-🚨🚨🚨 CRITICAL UPDATE RULE: When the user asks for an update, you MUST:
-- ONLY MODIFY what the user specifically asked for
-- DO NOT rewrite entire files - use the SEARCH/REPLACE format to change only the necessary lines
-- Keep the original code structure and functionality intact
-- Only make additional changes if they are directly related and necessary for the requested change to work
-
-=== PROJECT REQUIREMENTS ===
-- Do not create a \`README.md\` or any other markdown file, it will be automatically created by the API.
-- Use Tailwind CSS for styling, include it in the code by adding \`\` in the \`\` tag.
-- Use any library you need to use, just add it to the \`\` tag or \`\` tag, depending on the library.
-- For Icons, use the latest version of Lucide Icons, here is a demo on how to include it and use it:
-\`\`\`html
-
-
-
-
-
-
-
-
-\`\`\`
-- Use real public API, you can find good ones there: https://github.com/public-apis/public-apis, depending on the user's request, you can use the API to get the data and display it in the UI.
-- When creating new pages or adding shared functionality, make sure to update navigation links in existing HTML files so users can navigate to the new pages.
-
-${PROMPT_FOR_IMAGE_GENERATION}
-
-=== OUTPUT FORMAT ===
-🚨 CRITICAL: YOU MUST ALWAYS FOLLOW THIS EXACT FORMAT FOR EVERY RESPONSE. NO EXCEPTIONS.
-
-## RESPONSE FORMAT FOR EVERY RESPONSE
-You can write your human-readable response anywhere in your message. Markdown allowed, Emoji allowed to make it more engaging. This is your explanation or answer to the user.
-
-**IMPORTANT RULES:**
-1. ✅ ANY text that is NOT inside special markers (${START_FILE_CONTENT}, ${SEARCH_START}) will be shown to the user as your message
-2. ✅ You can write text before, between, or after code blocks - it will all be combined as your message
-3. ✅ Use markdown formatting to make your responses clear and engaging
-
-**EXAMPLE:**
-I'll update that for you! 🚀
-
-[...then your SEARCH/REPLACE blocks or new file blocks if needed...]
-
-Done! Your changes are ready.
-
-## RESPONSE FORMAT FOR CODE UPDATES (MODIFYING EXISTING FILES)
-🚨 THIS IS THE PRIMARY FORMAT YOU SHOULD USE WHEN UPDATING EXISTING CODE.
-
-When the user asks you to update existing code, you MUST use the SEARCH/REPLACE format. DO NOT output entire files.
-You can do multiple updates in the same response, just follow the steps below for each update:
-
-**SEARCH/REPLACE FORMAT:**
-1. Start with ${SEARCH_START}
-2. Add a space and provide the filename of the file to update (e.g., index.html, style.css, script.js, about.html, etc.)
-3. Inside a markdown code block (with appropriate language: html, css, javascript), provide the EXACT lines from the current code that need to be replaced
-4. Use ${DIVIDER} to separate the search block from the replacement
-5. Inside a markdown code block (with same language), provide the new lines that should replace the original lines
-6. End with ${REPLACE_END}
-
-**IMPORTANT SEARCH/REPLACE RULES:**
-- You can use multiple SEARCH/REPLACE blocks if changes are needed in different parts of the same file or in different files
-- The SEARCH block must *exactly* match the current code, including indentation, whitespace, and line breaks
-- Choose enough context in your SEARCH block to make it unique within the file
-- To insert code at the beginning, use an empty SEARCH block (only ${SEARCH_START} filename, empty code block, ${DIVIDER}, then your code, ${REPLACE_END})
-- To insert code in the middle, include the line *before* the insertion point in SEARCH, then include that same line plus the new lines in REPLACE
-- To delete code, provide the lines to delete in SEARCH and leave REPLACE empty (empty code block between ${DIVIDER} and ${REPLACE_END})
-
-**EXAMPLES:**
-
-Example 1 - Updating HTML content:
-${SEARCH_START} index.html
-\`\`\`html
- Old Title
- Old description
-\`\`\`
-${DIVIDER}
-\`\`\`html
- New Title
- New amazing description with more details
-\`\`\`
-${REPLACE_END}
-
-Example 2 - Updating CSS:
-${SEARCH_START} style.css
-\`\`\`css
-body {
- background: white;
- color: black;
-}
-\`\`\`
-${DIVIDER}
-\`\`\`css
-body {
- background: linear-gradient(to right, #667eea, #764ba2);
- color: white;
-}
-\`\`\`
-${REPLACE_END}
-
-Example 3 - Inserting code (adding script tag before closing body):
-${SEARCH_START} index.html
-\`\`\`html
-
-
-\`\`\`
-${DIVIDER}
-\`\`\`html
-
-
-
-\`\`\`
-${REPLACE_END}
-
-Example 4 - Deleting code:
-${SEARCH_START} index.html
-\`\`\`html
- This paragraph will be deleted.
-\`\`\`
-${DIVIDER}
-\`\`\`html
-\`\`\`
-${REPLACE_END}
-
-Example 5 - Multiple changes in different files:
-${SEARCH_START} index.html
-\`\`\`html
- Welcome
-\`\`\`
-${DIVIDER}
-\`\`\`html
- Welcome to our site!
-\`\`\`
-${REPLACE_END}
-
-${SEARCH_START} style.css
-\`\`\`css
-h1 {
- font-size: 24px;
-}
-\`\`\`
-${DIVIDER}
-\`\`\`css
-h1 {
- font-size: 36px;
- font-weight: bold;
-}
-\`\`\`
-${REPLACE_END}
-
-## RESPONSE FORMAT FOR CREATING NEW FILES
-When the user asks you to create a NEW file (new HTML page, new CSS file, new JS file, etc.), use the following format:
-
-**NEW FILE FORMAT:**
-1. Start with ${START_FILE_CONTENT}
-2. Add a space and provide the filename (e.g., about.html, animation.js, components/header.js)
-3. Inside a markdown code block with appropriate language identifier (html, css, javascript), provide the COMPLETE code for the new file
-4. End with ${END_FILE_CONTENT}
-5. If creating a new page, make sure to ALSO UPDATE existing HTML files with navigation links using the SEARCH/REPLACE format
-
-**EXAMPLES:**
-
-Example 1 - Creating a new HTML page:
-${START_FILE_CONTENT} about.html
-\`\`\`html
-
-
-
-
-
- About Us
-
-
-
-
-
- Home
- About
-
-
- About Us
- This is the about page.
-
-
-
-
-\`\`\`
-${END_FILE_CONTENT}
-
-Then update existing pages to link to it:
-${SEARCH_START} index.html
-\`\`\`html
-
- Home
-
-\`\`\`
-${DIVIDER}
-\`\`\`html
-
- Home
- About
-
-\`\`\`
-${REPLACE_END}
-
-Example 2 - Creating a new JavaScript file:
-${START_FILE_CONTENT} animation.js
-\`\`\`javascript
-// Smooth scroll animation
-document.querySelectorAll('a[href^="#"]').forEach(anchor => {
- anchor.addEventListener('click', function (e) {
- e.preventDefault();
- const target = document.querySelector(this.getAttribute('href'));
- if (target) {
- target.scrollIntoView({ behavior: 'smooth' });
- }
- });
-});
-\`\`\`
-${END_FILE_CONTENT}
-
-Then update HTML files to include it:
-${SEARCH_START} index.html
-\`\`\`html
-
-
-\`\`\`
-${DIVIDER}
-\`\`\`html
-
-
-
-\`\`\`
-${REPLACE_END}
-
-## SUMMARY OF WHEN TO USE EACH FORMAT
-- **SEARCH/REPLACE format** (${SEARCH_START}...${DIVIDER}...${REPLACE_END}): Use when UPDATING/MODIFYING existing files. This is your primary format for changes.
-- **NEW FILE format** (${START_FILE_CONTENT}...${END_FILE_CONTENT}): Use when CREATING brand new files that don't exist yet.
-- When creating new pages, ALWAYS update navigation in existing pages using SEARCH/REPLACE format.
-- When creating new CSS/JS files, ALWAYS update HTML files to include them using SEARCH/REPLACE format.
-
-Remember: DO NOT rewrite entire files when updating. Use SEARCH/REPLACE to change only what's necessary!
-`;
-
-export const EXAMPLES_OF_PROJECT_SUGGESTIONS = [
- // Business & SaaS
- {
- short_value: "Modern SaaS Landing Page",
- long_value:
- "Create a modern SaaS landing page with a hero section featuring a product demo video or animation, benefits section with icons and descriptions, pricing plans comparison table with highlighted recommended tier, customer testimonials with company logos and photos, FAQ accordion section, integration showcase, and a prominent call-to-action footer with email signup.",
- },
- {
- short_value: "Startup Landing Page",
- long_value:
- "Build a startup landing page with a bold hero section featuring the value proposition, problem-solution sections with visual illustrations, product features showcase with interactive elements, team section with photos and roles, investor logos, press mentions, early access signup form, and social proof metrics counter.",
- },
- {
- short_value: "Business Consulting Website",
- long_value:
- "Design a professional business consulting website with a trust-building hero section, services overview with detailed descriptions, case studies with results and metrics, consultant team profiles with expertise areas, client testimonials carousel, consultation booking form, and a resources/blog section.",
- },
- // E-commerce & Retail
- {
- short_value: "E-commerce Product Page",
- long_value:
- "Create a detailed e-commerce product page with image gallery and zoom functionality, product specifications table, size/color selector with visual swatches, add-to-cart button with quantity selector, customer reviews and ratings section, related products carousel, shipping information, and return policy accordion.",
- },
- {
- short_value: "Online Store Homepage",
- long_value:
- "Build an online store homepage with a hero banner featuring seasonal promotions, category navigation cards with images, featured products grid with quick view, bestsellers section, new arrivals carousel, customer testimonials, newsletter signup with discount offer, and trust badges for secure payment and shipping.",
- },
- {
- short_value: "Fashion Brand Website",
- long_value:
- "Design a fashion brand website with full-screen hero slider showcasing latest collections, lookbook section with lifestyle photography, shop by category grid, featured collections with hover effects, Instagram feed integration, brand story section with video, styling tips blog section, and size guide with measurements.",
- },
- // Food & Restaurant
- {
- short_value: "Restaurant Website",
- long_value:
- "Create a restaurant website with appetizing hero section featuring signature dishes, online menu with categories and dish photos, reservation booking system with date/time picker, location map with directions, chef's special section, customer reviews with food ratings, gallery of restaurant ambiance, and contact form for events.",
- },
- {
- short_value: "Coffee Shop Website",
- long_value:
- "Build a cozy coffee shop website with warm hero section showcasing coffee art, menu with drinks and pastries categorized by type, about section highlighting coffee sourcing and roasting process, location finder with multiple branches, loyalty program details, online ordering integration, barista spotlights, and events calendar.",
- },
- {
- short_value: "Food Delivery Landing Page",
- long_value:
- "Design a food delivery landing page with eye-catching hero showing popular dishes, restaurant categories with cuisine filters, how it works section with step-by-step illustrations, delivery area checker with postal code input, app download buttons with QR code, partner restaurants showcase, promotional offers banner, and customer testimonials with ratings.",
- },
- // Real Estate & Property
- {
- short_value: "Real Estate Agency Website",
- long_value:
- "Create a real estate agency website with property search bar featuring filters for location/price/bedrooms, featured properties grid with key details and photos, virtual tour integration, neighborhood guides with amenities, agent profiles with contact information, mortgage calculator tool, market insights blog, and property valuation request form.",
- },
- {
- short_value: "Luxury Property Showcase",
- long_value:
- "Build a luxury property showcase with full-screen hero image slider, exclusive property listings with high-quality photography, detailed property descriptions with amenities list, 360-degree virtual tours, location highlights with lifestyle benefits, appointment scheduling for private viewings, property comparison tool, and concierge services section.",
- },
- // Creative & Portfolio
- {
- short_value: "Photography Portfolio",
- long_value:
- "Design a photography portfolio with masonry grid layout showcasing best work, categorized galleries by photography type (wedding/portrait/landscape), lightbox image viewer with navigation, about the photographer section with bio and equipment, client testimonials, package pricing for different photo services, contact form for booking inquiries, and Instagram feed integration.",
- },
- {
- short_value: "Creative Agency Portfolio",
- long_value:
- "Create a creative agency portfolio with bold hero section featuring latest campaign, services overview with icons and descriptions, case studies showcase with before/after comparisons and results metrics, client logos wall, team section with creative roles, design process timeline, awards and recognition section, and project inquiry form.",
- },
- {
- short_value: "UX/UI Designer Portfolio",
- long_value:
- "Build a UX/UI designer portfolio with clean hero section introducing yourself, featured projects with case study breakdowns including problem/solution/results, interactive prototypes embedded, design process explanation with wireframes and mockups, skills and tools section with proficiency levels, client testimonials, Dribbble/Behance integration, and contact form.",
- },
- // Personal & Blog
- {
- short_value: "Personal Brand Website",
- long_value:
- "Design a personal brand website with impactful hero section with professional photo and tagline, about me section with story and values, services or expertise areas, portfolio or work samples, speaking engagements or media appearances, blog or articles section, newsletter signup, social media links, and contact form with calendar booking integration.",
- },
- {
- short_value: "Modern Blog Website",
- long_value:
- "Create a modern blog website with featured post hero section, blog posts grid with thumbnails and excerpts, category filters and tags, author bio section with photo, related posts suggestions, comment section integration, email subscription form with popup, search functionality, popular posts sidebar, and social sharing buttons.",
- },
- {
- short_value: "Travel Blog",
- long_value:
- "Build a travel blog with inspiring hero banner featuring destination photos, travel posts organized by country/region, interactive map showing visited places, travel guides with itineraries and tips, photo galleries from trips, travel essentials and gear recommendations, destination comparison tool, email newsletter for travel updates, and booking affiliate links.",
- },
- // Health & Fitness
- {
- short_value: "Fitness Gym Website",
- long_value:
- "Create a fitness gym website with motivating hero video background, class schedule calendar with booking system, membership plans comparison with benefits, trainer profiles with specializations, virtual gym tour with facility photos, success stories with before/after transformations, fitness blog with workout tips, free trial signup form, and location/hours information.",
- },
- {
- short_value: "Yoga Studio Website",
- long_value:
- "Design a yoga studio website with calming hero section showcasing studio space, class types with descriptions and difficulty levels, live class schedule with online/in-person options, instructor bios with teaching philosophies, beginner's guide to yoga, wellness blog, workshop and retreat information, membership packages, and online booking system.",
- },
- {
- short_value: "Health & Wellness Page",
- long_value:
- "Build a health and wellness page with hero section promoting holistic health, services offered including nutrition/fitness/mental health, practitioner profiles with credentials, appointment booking calendar, wellness resources and blog articles, patient testimonials with health journey stories, FAQ section, insurance information, and contact form with health assessment questionnaire.",
- },
- // Education & Learning
- {
- short_value: "Online Course Landing Page",
- long_value:
- "Create an online course landing page with compelling hero showcasing course outcomes, curriculum breakdown with module details, instructor credentials and biography, student testimonials with success stories, course preview video, pricing options with payment plans, enrollment countdown timer, skills you'll learn section with icons, and money-back guarantee badge.",
- },
- {
- short_value: "University/School Website",
- long_value:
- "Design a university/school website with prominent hero featuring campus life, academic programs overview with departments, admissions information with requirements and deadlines, virtual campus tour with interactive map, faculty profiles with research areas, student resources and services, events calendar, news and announcements section, and application portal access.",
- },
- {
- short_value: "Tutoring Service Website",
- long_value:
- "Build a tutoring service website with hero section highlighting student success rates, subjects offered with grade levels, tutor matching system with profiles and qualifications, flexible scheduling with online/in-person options, pricing packages and hourly rates, parent testimonials, free assessment offer, learning resources, and contact form with subject selector.",
- },
- // Events & Entertainment
- {
- short_value: "Event Conference Website",
- long_value:
- "Create an event conference website with countdown timer to event date, speaker lineup with bios and session topics, event schedule with multiple tracks, venue information with map and travel details, ticket types and pricing with early bird discounts, sponsor showcase with logo display, past event highlights with photos/videos, and registration form.",
- },
- {
- short_value: "Music Festival Landing Page",
- long_value:
- "Design a music festival landing page with vibrant hero featuring headliner artists, full lineup with artist photos and genres, stage schedule with set times, festival map with venue layout, ticket tiers with VIP options, camping and accommodation info, festival rules and what to bring, photo gallery from previous years, and ticket purchase integration.",
- },
- {
- short_value: "Wedding Website",
- long_value:
- "Build a wedding website with romantic hero featuring couple's photo, love story timeline, wedding day schedule with ceremony and reception details, venue information with directions, RSVP form with meal preferences, accommodation suggestions for guests, gift registry links, photo gallery from engagement, wedding party introductions, and FAQs section.",
- },
- // Professional Services
- {
- short_value: "Law Firm Website",
- long_value:
- "Create a law firm website with professional hero establishing trust and authority, practice areas with detailed service descriptions, attorney profiles with education and case experience, case results and settlements, legal resources and blog, client testimonials, consultation booking form, frequently asked legal questions, contact information with multiple office locations.",
- },
- {
- short_value: "Dental Clinic Website",
- long_value:
- "Design a dental clinic website with welcoming hero showing modern facilities, dental services offered with procedure explanations, dentist and staff profiles with credentials, patient testimonials with smile transformations, new patient forms downloadable, insurance and payment options, dental health blog and tips, online appointment booking system, and emergency contact information.",
- },
- {
- short_value: "Architecture Firm Website",
- long_value:
- "Build an architecture firm website with striking hero showcasing signature project, portfolio gallery organized by project type (residential/commercial/institutional), project case studies with design concepts and final photos, services from planning to construction, architect team profiles, design philosophy and approach, awards and publications, and project inquiry form.",
- },
- // Technology & Apps
- {
- short_value: "App Landing Page",
- long_value:
- "Create an app landing page with phone mockups showing app interface, key features section with animated icons, app store download buttons (iOS/Android), how it works explanation with screenshots, user testimonials and ratings, app demo video, subscription pricing if applicable, press mentions, FAQ section, and waitlist signup for beta access.",
- },
- {
- short_value: "Software Product Page",
- long_value:
- "Design a software product page with hero demonstrating product in action, features breakdown with detailed descriptions and benefits, technical specifications and integrations, pricing tiers comparison table, customer logos and case studies, live product demo or free trial signup, API documentation link, security and compliance badges, and customer support information.",
- },
- // Non-profit & Community
- {
- short_value: "Non-profit Organization Website",
- long_value:
- "Create a non-profit website with mission-driven hero showing impact, about section explaining cause and history, programs and initiatives with detailed descriptions, impact metrics and success stories, donation form with different giving options, volunteer opportunities with signup, upcoming events and campaigns, transparency section with annual reports, and newsletter subscription.",
- },
- {
- short_value: "Community Organization Website",
- long_value:
- "Build a community organization website with welcoming hero showcasing community members, about the organization with mission and values, programs and services offered, membership information and benefits, events calendar with community gatherings, volunteer opportunities, resources and support section, testimonials from community members, and contact form with location map.",
- },
- // Misc & Utility
- {
- short_value: "Interactive Weather Dashboard",
- long_value:
- "Design an interactive weather dashboard with current weather display showing temperature/conditions/humidity, location search with autocomplete, 7-day forecast with daily highs/lows, hourly forecast chart, weather map with radar overlay, severe weather alerts section, sunrise/sunset times, air quality index, UV index, and weather statistics using real weather API data.",
- },
- {
- short_value: "Modern Calculator",
- long_value:
- "Create a modern calculator with clean interface featuring number pad and operation buttons, basic arithmetic operations (+,-,*,/), advanced functions (percentage, square root, power), calculation history log that can be cleared, keyboard input support, light/dark theme toggle, responsive design for mobile and desktop, and smooth animations for button presses.",
- },
-];
diff --git a/lib/providers.ts b/lib/providers.ts
deleted file mode 100644
index 32c517dac89aa431d77d7bb6d895f9080a39c153..0000000000000000000000000000000000000000
--- a/lib/providers.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import DeepSeekLogo from "@/assets/deepseek.svg";
-import QwenLogo from "@/assets/qwen.svg";
-import KimiLogo from "@/assets/kimi.svg";
-import ZaiLogo from "@/assets/zai.svg";
-import MiniMaxLogo from "@/assets/minimax.svg";
-
-export const MODELS = [
-
- {
- value: "moonshotai/Kimi-K2.5",
- label: "Kimi K2 Instruct",
- providers: ["together", "novita", "groq"],
- logo: KimiLogo,
- isNew: true,
- isBestSeller: true,
- companyName: "Kimi",
- },
- {
- value: "deepseek-ai/DeepSeek-V3-0324",
- label: "DeepSeek V3 O324",
- providers: ["fireworks-ai", "nebius", "sambanova", "novita", "hyperbolic"],
- logo: DeepSeekLogo,
- companyName: "DeepSeek",
- isBestSeller: true,
- },
- {
- value: "deepseek-ai/DeepSeek-V3.2",
- label: "DeepSeek V3.2",
- logo: DeepSeekLogo,
- companyName: "DeepSeek",
- },
- {
- value: "Qwen/Qwen3-Coder-Next",
- label: "Qwen3 Coder Next",
- providers: ["novita", "hyperbolic"],
- logo: QwenLogo,
- companyName: "Qwen",
- isNew: true,
- },
- {
- value: "zai-org/GLM-4.7",
- label: "GLM-4.7",
- logo: ZaiLogo,
- companyName: "Z.ai",
- },
- {
- value: "MiniMaxAI/MiniMax-M2.1",
- label: "MiniMax M2.1",
- logo: MiniMaxLogo,
- companyName: "MiniMax",
- top_k: 40,
- temperature: 1.0,
- top_p: 0.95,
- },
-];
-
-export const DEFAULT_MODEL = "moonshotai/Kimi-K2.5";
-
-export const getProviders = async (model: string) => {
- const response = await fetch(
- `https://router.huggingface.co/v1/models/${model}`
- );
- const { data } = await response.json();
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- return data.providers.map((provider: any) => provider.provider);
-};
diff --git a/lib/seo.ts b/lib/seo.ts
deleted file mode 100644
index 3dba9263033045e407016ef14735c6b0b9322cfa..0000000000000000000000000000000000000000
--- a/lib/seo.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import { Metadata } from "next";
-
-interface SEOParams {
- title?: string;
- description?: string;
- path?: string;
- image?: string;
- noIndex?: boolean;
- canonical?: string;
-}
-
-export function generateSEO({
- 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.",
- path = "",
- image = "/banner.png",
- noIndex = false,
- canonical,
-}: SEOParams = {}): Metadata {
- const baseUrl = "https://deepsite.hf.co";
- const fullUrl = `${baseUrl}${path}`;
- const canonicalUrl = canonical || fullUrl;
-
- return {
- title,
- description,
- alternates: {
- canonical: canonicalUrl,
- },
- robots: noIndex
- ? {
- index: false,
- follow: false,
- googleBot: {
- index: false,
- follow: false,
- "max-video-preview": -1,
- "max-image-preview": "large",
- "max-snippet": -1,
- },
- }
- : {
- index: true,
- follow: true,
- googleBot: {
- index: true,
- follow: true,
- "max-video-preview": -1,
- "max-image-preview": "large",
- "max-snippet": -1,
- },
- },
- openGraph: {
- title,
- description,
- url: canonicalUrl,
- siteName: "DeepSite",
- images: [
- {
- url: `${baseUrl}${image}`,
- width: 1200,
- height: 630,
- alt: `${title} - DeepSite`,
- },
- ],
- locale: "en_US",
- type: "website",
- },
- twitter: {
- card: "summary_large_image",
- title,
- description,
- images: [`${baseUrl}${image}`],
- creator: "@deepsite",
- },
- other: {
- // Control how the page appears when shared
- "og:image:secure_url": `${baseUrl}${image}`,
- // Help search engines understand the primary URL
- rel: "canonical",
- },
- };
-}
-
-export function generateStructuredData(
- type: "WebApplication" | "Organization" | "Article",
- data: any
-) {
- const baseStructuredData = {
- "@context": "https://schema.org",
- "@type": type,
- };
-
- switch (type) {
- case "WebApplication":
- return {
- ...baseStructuredData,
- name: "DeepSite",
- description: "Build websites with AI, no code required",
- url: "https://deepsite.hf.co",
- applicationCategory: "DeveloperApplication",
- operatingSystem: "Web",
- offers: {
- "@type": "Offer",
- price: "0",
- priceCurrency: "USD",
- },
- creator: {
- "@type": "Organization",
- name: "DeepSite",
- url: "https://deepsite.hf.co",
- },
- ...data,
- };
-
- case "Organization":
- return {
- ...baseStructuredData,
- name: "DeepSite",
- url: "https://deepsite.hf.co",
- logo: "https://deepsite.hf.co/logo.svg",
- description: "AI-powered web development platform",
- sameAs: [
- // Add social media links here if available
- ],
- ...data,
- };
-
- default:
- return { ...baseStructuredData, ...data };
- }
-}
diff --git a/lib/type.ts b/lib/type.ts
deleted file mode 100644
index c57b306d3a6d918e55dff8c23ad86b9f8bea8ea4..0000000000000000000000000000000000000000
--- a/lib/type.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { buttonVariants } from "@/components/ui/button";
-import { VariantProps } from "class-variance-authority";
-import { LucideIcon } from "lucide-react";
-
-export type DeviceType = "desktop" | "mobile";
-export type ActivityType = "chat" | "code";
-export type MobileTabType = "left-sidebar" | "right-sidebar";
-export type ProviderType = "auto" | "cheapest" | "fastest" | string;
-export enum MessageActionType {
- PUBLISH_PROJECT = "PUBLISH_PROJECT",
- SEE_LIVE_PREVIEW = "SEE_LIVE_PREVIEW",
- UPGRADE_TO_PRO = "UPGRADE_TO_PRO",
- ADD_CREDITS = "ADD_CREDITS",
-}
-export type MessageAction = {
- label: string;
- variant?: VariantProps["variant"];
- icon?: LucideIcon;
- disabled?: boolean;
- loading?: boolean;
- type?: MessageActionType;
- projectTitle?: string;
- prompt?: string;
- messageReferrer?: number;
-};
-export type Message = {
- id: string;
- role: MessageRole;
- model?: string;
- content?: string;
- createdAt: Date;
- isThinking?: boolean;
- files?: string[];
- isAborted?: boolean;
- isAutomated?: boolean;
- actions?: MessageAction[];
-};
-export type File = {
- path: string;
- content?: string;
-};
-export interface Commit {
- title: string;
- oid: string;
- date: Date;
-}
-export type MessageRole = "user" | "assistant";
diff --git a/lib/utils.ts b/lib/utils.ts
deleted file mode 100644
index f84a3d4ade20f9347e9205db5c02ed87d0eb88dd..0000000000000000000000000000000000000000
--- a/lib/utils.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-import { clsx, type ClassValue } from "clsx";
-import { twMerge } from "tailwind-merge";
-import { File } from "./type";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
-
-export const ALLOWED_DOMAINS = [
- "huggingface.co",
- "deepsite.hf.co",
- "localhost",
- "enzostvs-deepsite-v4-demo.hf.space",
-];
-
-export const COLORS = [
- "red",
- "blue",
- "green",
- "yellow",
- "purple",
- "pink",
- "gray",
-];
-export const EMOJIS_FOR_SPACE = [
- "🚀",
- "🔥",
- "✨",
- "💡",
- "🤖",
- "🌟",
- "🎉",
- "💎",
- "⚡",
- "🎨",
- "🧠",
- "📦",
- "🛠️",
- "🚧",
- "🌈",
- "📚",
- "🧩",
- "🔧",
- "🖥️",
- "📱",
-];
-
-export const getMentionsFromPrompt = async (prompt: string) => {
- const mentions = prompt.match(/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+/g);
- const validMentions = await Promise.all(
- mentions?.map(async (mention) => {
- let type = "model";
- let response = await fetch(
- `https://huggingface.co/api/models/${mention}`
- );
- if (!response.ok) {
- type = "dataset";
- response = await fetch(
- `https://huggingface.co/api/datasets/${mention}`
- );
- if (!response.ok) {
- return null;
- }
- }
- const readme = await fetch(
- `https://huggingface.co/${
- type === "model" ? "" : "datasets/"
- }${mention}/raw/main/README.md`
- );
- const readmeContent = await readme.text();
-
- const data = await response.json();
- return {
- library_name: data?.library_name ?? data?.cardData?.library_name,
- pipeline_tag: data?.pipeline_tag ?? data?.cardData?.pipeline_tag,
- model_id: data.id,
- readme: readmeContent ?? "",
- };
- }) ?? []
- );
- return validMentions?.filter((mention) => mention !== null) ?? [];
-};
-export const getContextFilesFromPrompt = async (
- prompt: string,
- currentFiles: File[]
-) => {
- const mentions = prompt.match(/file:\/[a-zA-Z0-9-_.\/]+/g);
- const filesToUseAsContext: File[] = [];
- if (currentFiles.length === 0) return currentFiles;
- if (!mentions || mentions.length === 0) return currentFiles;
- mentions?.forEach((mention) => {
- const filePath = mention.replace("file:/", "");
- const matchedFile = currentFiles.find((file) => file.path === filePath);
- if (matchedFile) {
- filesToUseAsContext.push(matchedFile);
- }
- });
- return filesToUseAsContext;
-};
-
-export const defaultHTML = `
-
-
- My app
-
-
-
-
-
-
-
- 🔥 DeepSite v4: New version dropped!
-
-
-
- I'm ready to develop,
-
- Ask me anything.
-
-
-
-
-
-
-`;
-
-export function injectDeepSiteBadge(html: string): string {
- const badgeScript =
- '';
-
- // Remove any existing badge script to avoid duplicates
- const cleanedHtml = html.replace(
- / in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`,
+ },
+ ...(previousPrompt
+ ? [
+ {
+ role: "user",
+ content: previousPrompt,
+ },
+ ]
+ : []),
+ ...(html
+ ? [
+ {
+ role: "assistant",
+ content: `The current code is: ${html}.`,
+ },
+ ]
+ : []),
+ {
+ role: "user",
+ content: prompt,
+ },
+ ],
+ ...(selectedProvider.id !== "sambanova"
+ ? {
+ max_tokens: selectedProvider.max_tokens,
+ }
+ : {}),
+ });
+
+ while (true) {
+ const { done, value } = await chatCompletion.next();
+ if (done) {
+ break;
+ }
+ const chunk = value.choices[0]?.delta?.content;
+ if (chunk) {
+ if (provider !== "sambanova") {
+ res.write(chunk);
+ completeResponse += chunk;
+
+ if (completeResponse.includes("")) {
+ break;
+ }
+ } else {
+ let newChunk = chunk;
+ if (chunk.includes("")) {
+ // Replace everything after the last