Spaces:
Running
Running
Delete app
#525
by
escambalkon - opened
This view is limited to 50 files because it contains too many changes.
See the raw diff here.
- .env.example +0 -4
- .github/workflows/deploy-prod.yml +0 -77
- .gitignore +1 -7
- Dockerfile +6 -9
- MCP-SERVER.md +428 -0
- README.md +12 -7
- actions/mentions.ts +0 -31
- actions/projects.ts +0 -175
- app/(public)/layout.tsx +0 -14
- app/(public)/page.tsx +0 -25
- app/(public)/signin/page.tsx +0 -21
- app/[owner]/[repoId]/page.tsx +0 -35
- app/api/ask/route.ts +0 -183
- app/api/auth/[...nextauth]/route.ts +0 -6
- app/api/healthcheck/route.ts +0 -5
- app/api/projects/[repoId]/[commitId]/route.ts +0 -49
- app/api/projects/[repoId]/download/route.ts +0 -76
- app/api/projects/[repoId]/medias/route.ts +0 -87
- app/api/projects/[repoId]/rename/route.ts +0 -92
- app/api/projects/[repoId]/route.ts +0 -104
- app/api/projects/route.ts +0 -145
- app/api/redesign/route.ts +0 -73
- app/favicon.ico +0 -0
- app/layout.tsx +0 -108
- app/new/page.tsx +0 -18
- app/not-found.tsx +0 -17
- {app → assets}/globals.css +244 -32
- assets/hf-logo.svg +0 -7
- assets/logo.svg +316 -0
- assets/pro.svg +0 -10
- chart/Chart.yaml +0 -5
- chart/env/prod.yaml +0 -59
- chart/templates/_helpers.tpl +0 -22
- chart/templates/config.yaml +0 -10
- chart/templates/deployment.yaml +0 -81
- chart/templates/hpa.yaml +0 -45
- chart/templates/infisical.yaml +0 -24
- chart/templates/ingress-internal.yaml +0 -32
- chart/templates/ingress.yaml +0 -33
- chart/templates/network-policy.yaml +0 -36
- chart/templates/service-account.yaml +0 -13
- chart/templates/service-monitor.yaml +0 -17
- chart/templates/service.yaml +0 -21
- chart/values.yaml +0 -81
- components.json +4 -5
- components/animated-blobs/index.tsx +34 -0
- components/animated-text/index.tsx +123 -0
- components/ask-ai/ask-ai-landing.tsx +0 -75
- components/ask-ai/ask-ai.tsx +0 -215
- components/ask-ai/context.tsx +0 -123
.env.example
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
AUTH_HUGGINGFACE_ID=
|
| 2 |
-
AUTH_HUGGINGFACE_SECRET=
|
| 3 |
-
NEXTAUTH_URL=http://localhost:3001
|
| 4 |
-
AUTH_SECRET=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.github/workflows/deploy-prod.yml
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
name: Deploy to k8s
|
| 2 |
-
on:
|
| 3 |
-
# run this workflow manually from the Actions tab
|
| 4 |
-
workflow_dispatch:
|
| 5 |
-
|
| 6 |
-
jobs:
|
| 7 |
-
build-and-publish:
|
| 8 |
-
runs-on:
|
| 9 |
-
group: cpu-high
|
| 10 |
-
steps:
|
| 11 |
-
- name: Checkout
|
| 12 |
-
uses: actions/checkout@v4
|
| 13 |
-
|
| 14 |
-
- name: Login to Registry
|
| 15 |
-
uses: docker/login-action@v3
|
| 16 |
-
with:
|
| 17 |
-
registry: registry.internal.huggingface.tech
|
| 18 |
-
username: ${{ secrets.DOCKER_INTERNAL_USERNAME }}
|
| 19 |
-
password: ${{ secrets.DOCKER_INTERNAL_PASSWORD }}
|
| 20 |
-
|
| 21 |
-
- name: Docker metadata
|
| 22 |
-
id: meta
|
| 23 |
-
uses: docker/metadata-action@v5
|
| 24 |
-
with:
|
| 25 |
-
images: |
|
| 26 |
-
registry.internal.huggingface.tech/deepsite/deepsite
|
| 27 |
-
tags: |
|
| 28 |
-
type=raw,value=latest,enable={{is_default_branch}}
|
| 29 |
-
type=sha,enable=true,prefix=sha-,format=short,sha-len=8
|
| 30 |
-
|
| 31 |
-
- name: Set up Docker Buildx
|
| 32 |
-
uses: docker/setup-buildx-action@v3
|
| 33 |
-
|
| 34 |
-
- name: Inject slug/short variables
|
| 35 |
-
uses: rlespinasse/github-slug-action@v4
|
| 36 |
-
|
| 37 |
-
- name: Build and Publish image
|
| 38 |
-
uses: docker/build-push-action@v5
|
| 39 |
-
with:
|
| 40 |
-
context: .
|
| 41 |
-
file: Dockerfile
|
| 42 |
-
push: ${{ github.event_name != 'pull_request' }}
|
| 43 |
-
tags: ${{ steps.meta.outputs.tags }}
|
| 44 |
-
labels: ${{ steps.meta.outputs.labels }}
|
| 45 |
-
platforms: linux/amd64
|
| 46 |
-
cache-to: type=gha,mode=max,scope=amd64
|
| 47 |
-
cache-from: type=gha,scope=amd64
|
| 48 |
-
provenance: false
|
| 49 |
-
|
| 50 |
-
deploy:
|
| 51 |
-
name: Deploy on prod
|
| 52 |
-
runs-on: ubuntu-latest
|
| 53 |
-
needs: ["build-and-publish"]
|
| 54 |
-
steps:
|
| 55 |
-
- name: Inject slug/short variables
|
| 56 |
-
uses: rlespinasse/github-slug-action@v4
|
| 57 |
-
|
| 58 |
-
- name: Gen values
|
| 59 |
-
run: |
|
| 60 |
-
VALUES=$(cat <<-END
|
| 61 |
-
image:
|
| 62 |
-
tag: "sha-${{ env.GITHUB_SHA_SHORT }}"
|
| 63 |
-
END
|
| 64 |
-
)
|
| 65 |
-
echo "VALUES=$(echo "$VALUES" | yq -o=json | jq tostring)" >> $GITHUB_ENV
|
| 66 |
-
|
| 67 |
-
- name: Deploy on infra-deployments
|
| 68 |
-
uses: the-actions-org/workflow-dispatch@v2
|
| 69 |
-
with:
|
| 70 |
-
workflow: Update application single value
|
| 71 |
-
repo: huggingface/infra-deployments
|
| 72 |
-
wait-for-completion: true
|
| 73 |
-
wait-for-completion-interval: 10s
|
| 74 |
-
display-workflow-run-url-interval: 10s
|
| 75 |
-
ref: refs/heads/main
|
| 76 |
-
token: ${{ secrets.GIT_TOKEN_INFRA_DEPLOYMENT }}
|
| 77 |
-
inputs: '{"path": "hub/deepsite/deepsite.yaml", "value": ${{ env.VALUES }}, "url": "${{ github.event.head_commit.url }}"}'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
CHANGED
|
@@ -31,7 +31,7 @@ yarn-error.log*
|
|
| 31 |
.pnpm-debug.log*
|
| 32 |
|
| 33 |
# env files (can opt-in for committing if needed)
|
| 34 |
-
.env
|
| 35 |
|
| 36 |
# vercel
|
| 37 |
.vercel
|
|
@@ -39,9 +39,3 @@ yarn-error.log*
|
|
| 39 |
# typescript
|
| 40 |
*.tsbuildinfo
|
| 41 |
next-env.d.ts
|
| 42 |
-
|
| 43 |
-
.idea
|
| 44 |
-
|
| 45 |
-
# binary assets (hosted on CDN)
|
| 46 |
-
assets/assistant.jpg
|
| 47 |
-
.gitattributes
|
|
|
|
| 31 |
.pnpm-debug.log*
|
| 32 |
|
| 33 |
# env files (can opt-in for committing if needed)
|
| 34 |
+
.env*
|
| 35 |
|
| 36 |
# vercel
|
| 37 |
.vercel
|
|
|
|
| 39 |
# typescript
|
| 40 |
*.tsbuildinfo
|
| 41 |
next-env.d.ts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -1,22 +1,19 @@
|
|
| 1 |
FROM node:20-alpine
|
| 2 |
USER root
|
| 3 |
|
| 4 |
-
# Install pnpm
|
| 5 |
-
RUN corepack enable && corepack prepare pnpm@latest --activate
|
| 6 |
-
|
| 7 |
USER 1000
|
| 8 |
WORKDIR /usr/src/app
|
| 9 |
-
# Copy package.json and
|
| 10 |
-
COPY --chown=1000 package.json
|
| 11 |
|
| 12 |
# Copy the rest of the application files to the container
|
| 13 |
COPY --chown=1000 . .
|
| 14 |
|
| 15 |
-
RUN
|
| 16 |
-
RUN
|
| 17 |
|
| 18 |
# Expose the application port (assuming your app runs on port 3000)
|
| 19 |
-
EXPOSE
|
| 20 |
|
| 21 |
# Start the application
|
| 22 |
-
CMD ["
|
|
|
|
| 1 |
FROM node:20-alpine
|
| 2 |
USER root
|
| 3 |
|
|
|
|
|
|
|
|
|
|
| 4 |
USER 1000
|
| 5 |
WORKDIR /usr/src/app
|
| 6 |
+
# Copy package.json and package-lock.json to the container
|
| 7 |
+
COPY --chown=1000 package.json package-lock.json ./
|
| 8 |
|
| 9 |
# Copy the rest of the application files to the container
|
| 10 |
COPY --chown=1000 . .
|
| 11 |
|
| 12 |
+
RUN npm install
|
| 13 |
+
RUN npm run build
|
| 14 |
|
| 15 |
# Expose the application port (assuming your app runs on port 3000)
|
| 16 |
+
EXPOSE 3000
|
| 17 |
|
| 18 |
# Start the application
|
| 19 |
+
CMD ["npm", "start"]
|
MCP-SERVER.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DeepSite MCP Server
|
| 2 |
+
|
| 3 |
+
DeepSite is now available as an MCP (Model Context Protocol) server, enabling AI assistants like Claude to create websites directly using natural language.
|
| 4 |
+
|
| 5 |
+
## Two Ways to Use DeepSite MCP
|
| 6 |
+
|
| 7 |
+
**Quick Comparison:**
|
| 8 |
+
|
| 9 |
+
| Feature | Option 1: HTTP Server | Option 2: Local Server |
|
| 10 |
+
|---------|----------------------|------------------------|
|
| 11 |
+
| **Setup Difficulty** | ✅ Easy (just config) | ⚠️ Requires installation |
|
| 12 |
+
| **Authentication** | HF Token in config header | HF Token or session cookie in env |
|
| 13 |
+
| **Best For** | Most users | Developers, custom modifications |
|
| 14 |
+
| **Maintenance** | ✅ Always up-to-date | Need to rebuild for updates |
|
| 15 |
+
|
| 16 |
+
**Recommendation:** Use Option 1 (HTTP Server) unless you need to modify the MCP server code.
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
### 🌐 Option 1: HTTP Server (Recommended)
|
| 21 |
+
|
| 22 |
+
**No installation required!** Use DeepSite's hosted MCP server.
|
| 23 |
+
|
| 24 |
+
#### Setup for Claude Desktop
|
| 25 |
+
|
| 26 |
+
Add to your Claude Desktop configuration file:
|
| 27 |
+
|
| 28 |
+
**MacOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
| 29 |
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
| 30 |
+
|
| 31 |
+
```json
|
| 32 |
+
{
|
| 33 |
+
"mcpServers": {
|
| 34 |
+
"deepsite": {
|
| 35 |
+
"url": "https://huggingface.co/deepsite/api/mcp",
|
| 36 |
+
"transport": {
|
| 37 |
+
"type": "sse"
|
| 38 |
+
},
|
| 39 |
+
"headers": {
|
| 40 |
+
"Authorization": "Bearer hf_your_token_here"
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
**Getting Your Hugging Face Token:**
|
| 48 |
+
|
| 49 |
+
1. Go to https://huggingface.co/settings/tokens
|
| 50 |
+
2. Create a new token with `write` access
|
| 51 |
+
3. Copy the token
|
| 52 |
+
4. Add it to the `Authorization` header in your config (recommended for security)
|
| 53 |
+
5. Alternatively, you can pass it as the `hf_token` parameter when using the tool
|
| 54 |
+
|
| 55 |
+
**⚠️ Security Recommendation:** Use the `Authorization` header in your config instead of passing the token in chat. This keeps your token secure and out of conversation history.
|
| 56 |
+
|
| 57 |
+
#### Example Usage with Claude
|
| 58 |
+
|
| 59 |
+
> "Create a portfolio website using DeepSite. Include a hero section, about section, and contact form."
|
| 60 |
+
|
| 61 |
+
Claude will automatically:
|
| 62 |
+
1. Use the `create_project` tool
|
| 63 |
+
2. Authenticate using the token from your config
|
| 64 |
+
3. Create the website on Hugging Face Spaces
|
| 65 |
+
4. Return the URLs to access your new site
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
### 💻 Option 2: Local Server
|
| 70 |
+
|
| 71 |
+
Run the MCP server locally for more control or offline use.
|
| 72 |
+
|
| 73 |
+
> **Note:** Most users should use Option 1 (HTTP Server) instead. Option 2 is only needed if you want to run the MCP server locally or modify its behavior.
|
| 74 |
+
|
| 75 |
+
#### Installation
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
cd mcp-server
|
| 79 |
+
npm install
|
| 80 |
+
npm run build
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
#### Setup for Claude Desktop
|
| 84 |
+
|
| 85 |
+
**Method A: Using HF Token (Recommended)**
|
| 86 |
+
|
| 87 |
+
```json
|
| 88 |
+
{
|
| 89 |
+
"mcpServers": {
|
| 90 |
+
"deepsite-local": {
|
| 91 |
+
"command": "node",
|
| 92 |
+
"args": ["/absolute/path/to/deepsite-v3/mcp-server/dist/index.js"],
|
| 93 |
+
"env": {
|
| 94 |
+
"HF_TOKEN": "hf_your_token_here",
|
| 95 |
+
"DEEPSITE_API_URL": "https://huggingface.co/deepsite"
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
**Method B: Using Session Cookie (Alternative)**
|
| 103 |
+
|
| 104 |
+
```json
|
| 105 |
+
{
|
| 106 |
+
"mcpServers": {
|
| 107 |
+
"deepsite-local": {
|
| 108 |
+
"command": "node",
|
| 109 |
+
"args": ["/absolute/path/to/deepsite-v3/mcp-server/dist/index.js"],
|
| 110 |
+
"env": {
|
| 111 |
+
"DEEPSITE_AUTH_COOKIE": "your-session-cookie",
|
| 112 |
+
"DEEPSITE_API_URL": "https://huggingface.co/deepsite"
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
}
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
**Getting Your Session Cookie (Method B only):**
|
| 120 |
+
|
| 121 |
+
1. Log in to https://huggingface.co/deepsite
|
| 122 |
+
2. Open Developer Tools (F12)
|
| 123 |
+
3. Go to Application → Cookies
|
| 124 |
+
4. Copy the session cookie value
|
| 125 |
+
5. Set as `DEEPSITE_AUTH_COOKIE` in the config
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## Available Tools
|
| 130 |
+
|
| 131 |
+
### `create_project`
|
| 132 |
+
|
| 133 |
+
Creates a new DeepSite project with HTML/CSS/JS files.
|
| 134 |
+
|
| 135 |
+
**Parameters:**
|
| 136 |
+
|
| 137 |
+
| Parameter | Type | Required | Description |
|
| 138 |
+
|-----------|------|----------|-------------|
|
| 139 |
+
| `title` | string | No | Project title (defaults to "DeepSite Project") |
|
| 140 |
+
| `pages` | array | Yes | Array of file objects with `path` and `html` |
|
| 141 |
+
| `prompt` | string | No | Commit message/description |
|
| 142 |
+
| `hf_token` | string | No* | Hugging Face API token (*optional if provided via Authorization header in config) |
|
| 143 |
+
|
| 144 |
+
**Page Object:**
|
| 145 |
+
```typescript
|
| 146 |
+
{
|
| 147 |
+
path: string; // e.g., "index.html", "styles.css", "script.js"
|
| 148 |
+
html: string; // File content
|
| 149 |
+
}
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
**Returns:**
|
| 153 |
+
```json
|
| 154 |
+
{
|
| 155 |
+
"success": true,
|
| 156 |
+
"message": "Project created successfully!",
|
| 157 |
+
"projectUrl": "https://huggingface.co/deepsite/username/project-name",
|
| 158 |
+
"spaceUrl": "https://huggingface.co/spaces/username/project-name",
|
| 159 |
+
"liveUrl": "https://username-project-name.hf.space",
|
| 160 |
+
"spaceId": "username/project-name",
|
| 161 |
+
"projectId": "space-id",
|
| 162 |
+
"files": ["index.html", "styles.css"]
|
| 163 |
+
}
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
---
|
| 167 |
+
|
| 168 |
+
## Example Prompts for Claude
|
| 169 |
+
|
| 170 |
+
### Simple Landing Page
|
| 171 |
+
> "Create a modern landing page for my SaaS product using DeepSite. Include a hero section with CTA, features grid, and footer. Use gradient background."
|
| 172 |
+
|
| 173 |
+
### Portfolio Website
|
| 174 |
+
> "Build a portfolio website with DeepSite. I need:
|
| 175 |
+
> - Hero section with my name and photo
|
| 176 |
+
> - Projects gallery with 3 sample projects
|
| 177 |
+
> - Skills section with tech stack
|
| 178 |
+
> - Contact form
|
| 179 |
+
> Use dark mode with accent colors."
|
| 180 |
+
|
| 181 |
+
### Blog Homepage
|
| 182 |
+
> "Create a blog homepage using DeepSite. Include:
|
| 183 |
+
> - Header with navigation
|
| 184 |
+
> - Featured post section
|
| 185 |
+
> - Grid of recent posts (3 cards)
|
| 186 |
+
> - Sidebar with categories
|
| 187 |
+
> - Footer with social links
|
| 188 |
+
> Clean, minimal design."
|
| 189 |
+
|
| 190 |
+
### Interactive Dashboard
|
| 191 |
+
> "Make an analytics dashboard with DeepSite:
|
| 192 |
+
> - Sidebar navigation
|
| 193 |
+
> - 4 metric cards at top
|
| 194 |
+
> - 2 chart placeholders
|
| 195 |
+
> - Data table
|
| 196 |
+
> - Modern, professional UI with charts.css"
|
| 197 |
+
|
| 198 |
+
---
|
| 199 |
+
|
| 200 |
+
## Direct API Usage
|
| 201 |
+
|
| 202 |
+
You can also call the HTTP endpoint directly:
|
| 203 |
+
|
| 204 |
+
### Using Authorization Header (Recommended)
|
| 205 |
+
|
| 206 |
+
```bash
|
| 207 |
+
curl -X POST https://huggingface.co/deepsite/api/mcp \
|
| 208 |
+
-H "Content-Type: application/json" \
|
| 209 |
+
-H "Authorization: Bearer hf_your_token_here" \
|
| 210 |
+
-d '{
|
| 211 |
+
"jsonrpc": "2.0",
|
| 212 |
+
"id": 1,
|
| 213 |
+
"method": "tools/call",
|
| 214 |
+
"params": {
|
| 215 |
+
"name": "create_project",
|
| 216 |
+
"arguments": {
|
| 217 |
+
"title": "My Website",
|
| 218 |
+
"pages": [
|
| 219 |
+
{
|
| 220 |
+
"path": "index.html",
|
| 221 |
+
"html": "<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello World!</h1></body></html>"
|
| 222 |
+
}
|
| 223 |
+
]
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
}'
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
### Using Token Parameter (Fallback)
|
| 230 |
+
|
| 231 |
+
```bash
|
| 232 |
+
curl -X POST https://huggingface.co/deepsite/api/mcp \
|
| 233 |
+
-H "Content-Type: application/json" \
|
| 234 |
+
-d '{
|
| 235 |
+
"jsonrpc": "2.0",
|
| 236 |
+
"id": 1,
|
| 237 |
+
"method": "tools/call",
|
| 238 |
+
"params": {
|
| 239 |
+
"name": "create_project",
|
| 240 |
+
"arguments": {
|
| 241 |
+
"title": "My Website",
|
| 242 |
+
"pages": [
|
| 243 |
+
{
|
| 244 |
+
"path": "index.html",
|
| 245 |
+
"html": "<!DOCTYPE html><html><head><title>Hello</title></head><body><h1>Hello World!</h1></body></html>"
|
| 246 |
+
}
|
| 247 |
+
],
|
| 248 |
+
"hf_token": "hf_xxxxx"
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
}'
|
| 252 |
+
```
|
| 253 |
+
|
| 254 |
+
### List Available Tools
|
| 255 |
+
|
| 256 |
+
```bash
|
| 257 |
+
curl -X POST https://huggingface.co/deepsite/api/mcp \
|
| 258 |
+
-H "Content-Type: application/json" \
|
| 259 |
+
-d '{
|
| 260 |
+
"jsonrpc": "2.0",
|
| 261 |
+
"id": 1,
|
| 262 |
+
"method": "tools/list",
|
| 263 |
+
"params": {}
|
| 264 |
+
}'
|
| 265 |
+
```
|
| 266 |
+
|
| 267 |
+
---
|
| 268 |
+
|
| 269 |
+
## Testing
|
| 270 |
+
|
| 271 |
+
### Test Local Server
|
| 272 |
+
|
| 273 |
+
```bash
|
| 274 |
+
cd mcp-server
|
| 275 |
+
./test.sh
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
### Test HTTP Server
|
| 279 |
+
|
| 280 |
+
```bash
|
| 281 |
+
curl -X POST https://huggingface.co/deepsite/api/mcp \
|
| 282 |
+
-H "Content-Type: application/json" \
|
| 283 |
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
|
| 284 |
+
```
|
| 285 |
+
|
| 286 |
+
---
|
| 287 |
+
|
| 288 |
+
## Migration Guide: From Parameter to Header Auth
|
| 289 |
+
|
| 290 |
+
If you're currently passing the token as a parameter in your prompts, here's how to migrate to the more secure header-based authentication:
|
| 291 |
+
|
| 292 |
+
### Step 1: Update Your Config
|
| 293 |
+
|
| 294 |
+
Edit your Claude Desktop config file and add the `headers` section:
|
| 295 |
+
|
| 296 |
+
```json
|
| 297 |
+
{
|
| 298 |
+
"mcpServers": {
|
| 299 |
+
"deepsite": {
|
| 300 |
+
"url": "https://huggingface.co/deepsite/api/mcp",
|
| 301 |
+
"transport": {
|
| 302 |
+
"type": "sse"
|
| 303 |
+
},
|
| 304 |
+
"headers": {
|
| 305 |
+
"Authorization": "Bearer hf_your_actual_token_here"
|
| 306 |
+
}
|
| 307 |
+
}
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
```
|
| 311 |
+
|
| 312 |
+
### Step 2: Restart Claude Desktop
|
| 313 |
+
|
| 314 |
+
Completely quit and restart Claude Desktop for the changes to take effect.
|
| 315 |
+
|
| 316 |
+
### Step 3: Use Simpler Prompts
|
| 317 |
+
|
| 318 |
+
Now you can simply say:
|
| 319 |
+
> "Create a portfolio website with DeepSite"
|
| 320 |
+
|
| 321 |
+
Instead of:
|
| 322 |
+
> "Create a portfolio website with DeepSite using token `hf_xxxxx`"
|
| 323 |
+
|
| 324 |
+
Your token is automatically included in all requests via the header!
|
| 325 |
+
|
| 326 |
+
---
|
| 327 |
+
|
| 328 |
+
## Security Notes
|
| 329 |
+
|
| 330 |
+
### HTTP Server (Option 1)
|
| 331 |
+
- **✅ Recommended:** Store your HF token in the `Authorization` header in your Claude Desktop config
|
| 332 |
+
- The token is stored locally on your machine and never exposed in chat
|
| 333 |
+
- The token is sent with each request but only used to authenticate with Hugging Face API
|
| 334 |
+
- DeepSite does not store your token
|
| 335 |
+
- Use tokens with minimal required permissions (write access to spaces)
|
| 336 |
+
- You can revoke tokens anytime at https://huggingface.co/settings/tokens
|
| 337 |
+
- **⚠️ Fallback:** You can still pass the token as a parameter, but this is less secure as it appears in conversation history
|
| 338 |
+
|
| 339 |
+
### Local Server (Option 2)
|
| 340 |
+
- Use `HF_TOKEN` environment variable (same security as Option 1)
|
| 341 |
+
- Or use `DEEPSITE_AUTH_COOKIE` if you prefer session-based auth
|
| 342 |
+
- All authentication data stays on your local machine
|
| 343 |
+
- Better for development and testing
|
| 344 |
+
- No need for both HTTP Server and Local Server - choose one!
|
| 345 |
+
|
| 346 |
+
---
|
| 347 |
+
|
| 348 |
+
## Troubleshooting
|
| 349 |
+
|
| 350 |
+
### "Invalid Hugging Face token"
|
| 351 |
+
- Verify your token at https://huggingface.co/settings/tokens
|
| 352 |
+
- Ensure the token has write permissions
|
| 353 |
+
- Check that you copied the full token (starts with `hf_`)
|
| 354 |
+
|
| 355 |
+
### "At least one page is required"
|
| 356 |
+
- Make sure you're providing the `pages` array
|
| 357 |
+
- Each page must have both `path` and `html` properties
|
| 358 |
+
|
| 359 |
+
### "Failed to create project"
|
| 360 |
+
- Check your token permissions
|
| 361 |
+
- Ensure the project title doesn't conflict with existing spaces
|
| 362 |
+
- Verify your Hugging Face account is in good standing
|
| 363 |
+
|
| 364 |
+
### Claude doesn't see the tool
|
| 365 |
+
- Restart Claude Desktop after modifying the config
|
| 366 |
+
- Check that the JSON config is valid (no trailing commas)
|
| 367 |
+
- For HTTP: verify the URL is correct
|
| 368 |
+
- For local: check the absolute path to index.js
|
| 369 |
+
|
| 370 |
+
---
|
| 371 |
+
|
| 372 |
+
## Architecture
|
| 373 |
+
|
| 374 |
+
### HTTP Server Flow
|
| 375 |
+
```
|
| 376 |
+
Claude Desktop
|
| 377 |
+
↓
|
| 378 |
+
(HTTP Request)
|
| 379 |
+
↓
|
| 380 |
+
huggingface.co/deepsite/api/mcp
|
| 381 |
+
↓
|
| 382 |
+
Hugging Face API (with user's token)
|
| 383 |
+
↓
|
| 384 |
+
New Space Created
|
| 385 |
+
↓
|
| 386 |
+
URLs returned to Claude
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
### Local Server Flow
|
| 390 |
+
```
|
| 391 |
+
Claude Desktop
|
| 392 |
+
↓
|
| 393 |
+
(stdio transport)
|
| 394 |
+
↓
|
| 395 |
+
Local MCP Server
|
| 396 |
+
↓
|
| 397 |
+
(HTTP to DeepSite API)
|
| 398 |
+
↓
|
| 399 |
+
huggingface.co/deepsite/api/me/projects
|
| 400 |
+
↓
|
| 401 |
+
New Space Created
|
| 402 |
+
```
|
| 403 |
+
|
| 404 |
+
---
|
| 405 |
+
|
| 406 |
+
## Contributing
|
| 407 |
+
|
| 408 |
+
The MCP server implementation lives in:
|
| 409 |
+
- HTTP Server: `/app/api/mcp/route.ts`
|
| 410 |
+
- Local Server: `/mcp-server/index.ts`
|
| 411 |
+
|
| 412 |
+
Both use the same core DeepSite logic for creating projects - no duplication!
|
| 413 |
+
|
| 414 |
+
---
|
| 415 |
+
|
| 416 |
+
## License
|
| 417 |
+
|
| 418 |
+
MIT
|
| 419 |
+
|
| 420 |
+
---
|
| 421 |
+
|
| 422 |
+
## Resources
|
| 423 |
+
|
| 424 |
+
- [Model Context Protocol Spec](https://modelcontextprotocol.io/)
|
| 425 |
+
- [DeepSite Documentation](https://huggingface.co/deepsite)
|
| 426 |
+
- [Hugging Face Spaces](https://huggingface.co/docs/hub/spaces)
|
| 427 |
+
- [Claude Desktop](https://claude.ai/desktop)
|
| 428 |
+
|
README.md
CHANGED
|
@@ -1,21 +1,26 @@
|
|
| 1 |
---
|
| 2 |
-
title: DeepSite
|
| 3 |
emoji: 🐳
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: blue
|
| 6 |
sdk: docker
|
| 7 |
pinned: true
|
| 8 |
-
app_port:
|
| 9 |
license: mit
|
| 10 |
failure_strategy: rollback
|
| 11 |
-
short_description: Generate any application by Vibe Coding
|
| 12 |
models:
|
| 13 |
- deepseek-ai/DeepSeek-V3-0324
|
| 14 |
-
- deepseek-ai/DeepSeek-
|
| 15 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
- moonshotai/Kimi-K2-Instruct-0905
|
| 17 |
-
- zai-org/GLM-4.
|
| 18 |
-
- MiniMaxAI/MiniMax-M2
|
|
|
|
| 19 |
---
|
| 20 |
|
| 21 |
# DeepSite 🐳
|
|
|
|
| 1 |
---
|
| 2 |
+
title: DeepSite v3
|
| 3 |
emoji: 🐳
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: blue
|
| 6 |
sdk: docker
|
| 7 |
pinned: true
|
| 8 |
+
app_port: 3000
|
| 9 |
license: mit
|
| 10 |
failure_strategy: rollback
|
| 11 |
+
short_description: Generate any application by Vibe Coding
|
| 12 |
models:
|
| 13 |
- deepseek-ai/DeepSeek-V3-0324
|
| 14 |
+
- deepseek-ai/DeepSeek-R1-0528
|
| 15 |
+
- deepseek-ai/DeepSeek-V3.1
|
| 16 |
+
- deepseek-ai/DeepSeek-V3.1-Terminus
|
| 17 |
+
- deepseek-ai/DeepSeek-V3.2-Exp
|
| 18 |
+
- Qwen/Qwen3-Coder-480B-A35B-Instruct
|
| 19 |
+
- moonshotai/Kimi-K2-Instruct
|
| 20 |
- moonshotai/Kimi-K2-Instruct-0905
|
| 21 |
+
- zai-org/GLM-4.6
|
| 22 |
+
- MiniMaxAI/MiniMax-M2
|
| 23 |
+
- moonshotai/Kimi-K2-Thinking
|
| 24 |
---
|
| 25 |
|
| 26 |
# DeepSite 🐳
|
actions/mentions.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
"use client";
|
| 2 |
-
|
| 3 |
-
import { File } from "@/lib/type";
|
| 4 |
-
|
| 5 |
-
export const searchMentions = async (query: string) => {
|
| 6 |
-
const promises = [searchModels(query), searchDatasets(query)];
|
| 7 |
-
const results = await Promise.all(promises);
|
| 8 |
-
return { models: results[0], datasets: results[1] };
|
| 9 |
-
};
|
| 10 |
-
|
| 11 |
-
const searchModels = async (query: string) => {
|
| 12 |
-
const response = await fetch(
|
| 13 |
-
`https://huggingface.co/api/quicksearch?q=${query}&type=model&limit=3`
|
| 14 |
-
);
|
| 15 |
-
const data = await response.json();
|
| 16 |
-
return data?.models ?? [];
|
| 17 |
-
};
|
| 18 |
-
|
| 19 |
-
const searchDatasets = async (query: string) => {
|
| 20 |
-
const response = await fetch(
|
| 21 |
-
`https://huggingface.co/api/quicksearch?q=${query}&type=dataset&limit=3`
|
| 22 |
-
);
|
| 23 |
-
const data = await response.json();
|
| 24 |
-
return data?.datasets ?? [];
|
| 25 |
-
};
|
| 26 |
-
|
| 27 |
-
export const searchFilesMentions = async (query: string, files: File[]) => {
|
| 28 |
-
if (!query) return files;
|
| 29 |
-
const lowerQuery = query.toLowerCase();
|
| 30 |
-
return files.filter((file) => file.path.toLowerCase().includes(lowerQuery));
|
| 31 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
actions/projects.ts
DELETED
|
@@ -1,175 +0,0 @@
|
|
| 1 |
-
"use server";
|
| 2 |
-
import {
|
| 3 |
-
downloadFile,
|
| 4 |
-
listCommits,
|
| 5 |
-
listFiles,
|
| 6 |
-
listSpaces,
|
| 7 |
-
RepoDesignation,
|
| 8 |
-
SpaceEntry,
|
| 9 |
-
spaceInfo,
|
| 10 |
-
} from "@huggingface/hub";
|
| 11 |
-
|
| 12 |
-
import { auth } from "@/lib/auth";
|
| 13 |
-
import { Commit, File } from "@/lib/type";
|
| 14 |
-
|
| 15 |
-
export interface ProjectWithCommits extends SpaceEntry {
|
| 16 |
-
commits?: Commit[];
|
| 17 |
-
medias?: string[];
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
const IGNORED_PATHS = ["README.md", ".gitignore", ".gitattributes"];
|
| 21 |
-
const IGNORED_FORMATS = [
|
| 22 |
-
".png",
|
| 23 |
-
".jpg",
|
| 24 |
-
".jpeg",
|
| 25 |
-
".gif",
|
| 26 |
-
".svg",
|
| 27 |
-
".webp",
|
| 28 |
-
".mp4",
|
| 29 |
-
".mp3",
|
| 30 |
-
".wav",
|
| 31 |
-
];
|
| 32 |
-
|
| 33 |
-
export const getProjects = async () => {
|
| 34 |
-
const projects: SpaceEntry[] = [];
|
| 35 |
-
const session = await auth();
|
| 36 |
-
if (!session?.user) {
|
| 37 |
-
return projects;
|
| 38 |
-
}
|
| 39 |
-
const token = session.accessToken;
|
| 40 |
-
for await (const space of listSpaces({
|
| 41 |
-
accessToken: token,
|
| 42 |
-
additionalFields: ["author", "cardData"],
|
| 43 |
-
search: {
|
| 44 |
-
owner: session.user.username,
|
| 45 |
-
},
|
| 46 |
-
})) {
|
| 47 |
-
if (
|
| 48 |
-
space.sdk === "static" &&
|
| 49 |
-
Array.isArray((space.cardData as { tags?: string[] })?.tags) &&
|
| 50 |
-
(space.cardData as { tags?: string[] })?.tags?.some((tag) =>
|
| 51 |
-
tag.includes("deepsite")
|
| 52 |
-
)
|
| 53 |
-
) {
|
| 54 |
-
projects.push(space);
|
| 55 |
-
}
|
| 56 |
-
}
|
| 57 |
-
return projects;
|
| 58 |
-
};
|
| 59 |
-
export const getProject = async (id: string, commitId?: string) => {
|
| 60 |
-
const session = await auth();
|
| 61 |
-
if (!session?.user) {
|
| 62 |
-
return null;
|
| 63 |
-
}
|
| 64 |
-
const token = session.accessToken;
|
| 65 |
-
try {
|
| 66 |
-
const project: ProjectWithCommits | null = await spaceInfo({
|
| 67 |
-
name: id,
|
| 68 |
-
accessToken: token,
|
| 69 |
-
additionalFields: ["author", "cardData"],
|
| 70 |
-
});
|
| 71 |
-
const repo: RepoDesignation = {
|
| 72 |
-
type: "space",
|
| 73 |
-
name: id,
|
| 74 |
-
};
|
| 75 |
-
const files: File[] = [];
|
| 76 |
-
const medias: string[] = [];
|
| 77 |
-
const params = { repo, accessToken: token };
|
| 78 |
-
if (commitId) {
|
| 79 |
-
Object.assign(params, { revision: commitId });
|
| 80 |
-
}
|
| 81 |
-
for await (const fileInfo of listFiles(params)) {
|
| 82 |
-
if (IGNORED_PATHS.includes(fileInfo.path)) continue;
|
| 83 |
-
if (IGNORED_FORMATS.some((format) => fileInfo.path.endsWith(format))) {
|
| 84 |
-
medias.push(
|
| 85 |
-
`https://huggingface.co/spaces/${id}/resolve/main/${fileInfo.path}`
|
| 86 |
-
);
|
| 87 |
-
continue;
|
| 88 |
-
}
|
| 89 |
-
|
| 90 |
-
if (fileInfo.type === "directory") {
|
| 91 |
-
for await (const subFile of listFiles({
|
| 92 |
-
repo,
|
| 93 |
-
accessToken: token,
|
| 94 |
-
path: fileInfo.path,
|
| 95 |
-
})) {
|
| 96 |
-
if (IGNORED_FORMATS.some((format) => subFile.path.endsWith(format))) {
|
| 97 |
-
medias.push(
|
| 98 |
-
`https://huggingface.co/spaces/${id}/resolve/main/${subFile.path}`
|
| 99 |
-
);
|
| 100 |
-
}
|
| 101 |
-
const blob = await downloadFile({
|
| 102 |
-
repo,
|
| 103 |
-
accessToken: token,
|
| 104 |
-
path: subFile.path,
|
| 105 |
-
raw: true,
|
| 106 |
-
...(commitId ? { revision: commitId } : {}),
|
| 107 |
-
}).catch((_) => {
|
| 108 |
-
return null;
|
| 109 |
-
});
|
| 110 |
-
if (!blob) {
|
| 111 |
-
continue;
|
| 112 |
-
}
|
| 113 |
-
const html = await blob?.text();
|
| 114 |
-
if (!html) {
|
| 115 |
-
continue;
|
| 116 |
-
}
|
| 117 |
-
files[subFile.path === "index.html" ? "unshift" : "push"]({
|
| 118 |
-
path: subFile.path,
|
| 119 |
-
content: html,
|
| 120 |
-
});
|
| 121 |
-
}
|
| 122 |
-
} else {
|
| 123 |
-
const blob = await downloadFile({
|
| 124 |
-
repo,
|
| 125 |
-
accessToken: token,
|
| 126 |
-
path: fileInfo.path,
|
| 127 |
-
raw: true,
|
| 128 |
-
...(commitId ? { revision: commitId } : {}),
|
| 129 |
-
}).catch((_) => {
|
| 130 |
-
return null;
|
| 131 |
-
});
|
| 132 |
-
if (!blob) {
|
| 133 |
-
continue;
|
| 134 |
-
}
|
| 135 |
-
const html = await blob?.text();
|
| 136 |
-
if (!html) {
|
| 137 |
-
continue;
|
| 138 |
-
}
|
| 139 |
-
files[fileInfo.path === "index.html" ? "unshift" : "push"]({
|
| 140 |
-
path: fileInfo.path,
|
| 141 |
-
content: html,
|
| 142 |
-
});
|
| 143 |
-
}
|
| 144 |
-
}
|
| 145 |
-
const commits: Commit[] = [];
|
| 146 |
-
const commitIterator = listCommits({ repo, accessToken: token });
|
| 147 |
-
for await (const commit of commitIterator) {
|
| 148 |
-
if (
|
| 149 |
-
commit.title?.toLowerCase() === "initial commit" ||
|
| 150 |
-
commit.title
|
| 151 |
-
?.toLowerCase()
|
| 152 |
-
?.includes("upload media files through deepsite")
|
| 153 |
-
)
|
| 154 |
-
continue;
|
| 155 |
-
commits.push({
|
| 156 |
-
title: commit.title,
|
| 157 |
-
oid: commit.oid,
|
| 158 |
-
date: commit.date,
|
| 159 |
-
});
|
| 160 |
-
if (commits.length >= 20) {
|
| 161 |
-
break;
|
| 162 |
-
}
|
| 163 |
-
}
|
| 164 |
-
|
| 165 |
-
project.commits = commits;
|
| 166 |
-
project.medias = medias;
|
| 167 |
-
|
| 168 |
-
return { project, files };
|
| 169 |
-
} catch (error) {
|
| 170 |
-
return {
|
| 171 |
-
project: null,
|
| 172 |
-
files: [],
|
| 173 |
-
};
|
| 174 |
-
}
|
| 175 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/(public)/layout.tsx
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
import { Navigation } from "@/components/public/navigation";
|
| 2 |
-
|
| 3 |
-
export default function PublicLayout({
|
| 4 |
-
children,
|
| 5 |
-
}: Readonly<{
|
| 6 |
-
children: React.ReactNode;
|
| 7 |
-
}>) {
|
| 8 |
-
return (
|
| 9 |
-
<div className="min-h-screen font-sans">
|
| 10 |
-
<Navigation />
|
| 11 |
-
{children}
|
| 12 |
-
</div>
|
| 13 |
-
);
|
| 14 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/(public)/page.tsx
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
import { AnimatedDotsBackground } from "@/components/public/animated-dots-background";
|
| 2 |
-
import { HeroHeader } from "@/components/public/hero-header";
|
| 3 |
-
import { UserProjects } from "@/components/projects/user-projects";
|
| 4 |
-
import { AskAiLanding } from "@/components/ask-ai/ask-ai-landing";
|
| 5 |
-
import { Bento } from "@/components/public/bento";
|
| 6 |
-
|
| 7 |
-
export const dynamic = "force-dynamic";
|
| 8 |
-
|
| 9 |
-
export default async function Homepage() {
|
| 10 |
-
return (
|
| 11 |
-
<>
|
| 12 |
-
<section className="container mx-auto relative z-10">
|
| 13 |
-
<HeroHeader />
|
| 14 |
-
<div className="absolute inset-0 -z-10">
|
| 15 |
-
<AnimatedDotsBackground />
|
| 16 |
-
</div>
|
| 17 |
-
<div className="max-w-xl mx-auto px-6">
|
| 18 |
-
<AskAiLanding />
|
| 19 |
-
</div>
|
| 20 |
-
</section>
|
| 21 |
-
<UserProjects />
|
| 22 |
-
<Bento />
|
| 23 |
-
</>
|
| 24 |
-
);
|
| 25 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/(public)/signin/page.tsx
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
import { LoginButtons } from "@/components/login/login-buttons";
|
| 2 |
-
|
| 3 |
-
export default async function SignInPage({
|
| 4 |
-
searchParams,
|
| 5 |
-
}: {
|
| 6 |
-
searchParams: Promise<{ callbackUrl: string }>;
|
| 7 |
-
}) {
|
| 8 |
-
const { callbackUrl } = await searchParams;
|
| 9 |
-
console.log(callbackUrl);
|
| 10 |
-
return (
|
| 11 |
-
<section className="min-h-screen font-sans">
|
| 12 |
-
<div className="px-6 py-16 max-w-5xl mx-auto text-center">
|
| 13 |
-
<h1 className="text-5xl font-bold mb-5">You shall not pass 🧙</h1>
|
| 14 |
-
<p className="text-lg text-muted-foreground mb-8">
|
| 15 |
-
You can't access this resource without being signed in.
|
| 16 |
-
</p>
|
| 17 |
-
<LoginButtons callbackUrl={callbackUrl ?? "/"} />
|
| 18 |
-
</div>
|
| 19 |
-
</section>
|
| 20 |
-
);
|
| 21 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/[owner]/[repoId]/page.tsx
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
import { getProject } from "@/actions/projects";
|
| 2 |
-
import { AppEditor } from "@/components/editor";
|
| 3 |
-
import { auth } from "@/lib/auth";
|
| 4 |
-
import { notFound, redirect } from "next/navigation";
|
| 5 |
-
|
| 6 |
-
export default async function ProjectPage({
|
| 7 |
-
params,
|
| 8 |
-
searchParams,
|
| 9 |
-
}: {
|
| 10 |
-
params: Promise<{ owner: string; repoId: string }>;
|
| 11 |
-
searchParams: Promise<{ commit?: string }>;
|
| 12 |
-
}) {
|
| 13 |
-
const session = await auth();
|
| 14 |
-
|
| 15 |
-
const { owner, repoId } = await params;
|
| 16 |
-
const { commit } = await searchParams;
|
| 17 |
-
if (!session) {
|
| 18 |
-
redirect(
|
| 19 |
-
`/api/auth/signin?callbackUrl=/${owner}/${repoId}${
|
| 20 |
-
commit ? `?commit=${commit}` : ""
|
| 21 |
-
}`
|
| 22 |
-
);
|
| 23 |
-
}
|
| 24 |
-
const datas = await getProject(`${owner}/${repoId}`, commit);
|
| 25 |
-
if (!datas?.project) {
|
| 26 |
-
return notFound();
|
| 27 |
-
}
|
| 28 |
-
return (
|
| 29 |
-
<AppEditor
|
| 30 |
-
project={datas.project}
|
| 31 |
-
files={datas.files ?? []}
|
| 32 |
-
isHistoryView={!!commit}
|
| 33 |
-
/>
|
| 34 |
-
);
|
| 35 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/ask/route.ts
DELETED
|
@@ -1,183 +0,0 @@
|
|
| 1 |
-
import { NextResponse } from "next/server";
|
| 2 |
-
import { InferenceClient } from "@huggingface/inference";
|
| 3 |
-
|
| 4 |
-
import { FOLLOW_UP_SYSTEM_PROMPT, INITIAL_SYSTEM_PROMPT } from "@/lib/prompts";
|
| 5 |
-
import { auth } from "@/lib/auth";
|
| 6 |
-
import { File, Message } from "@/lib/type";
|
| 7 |
-
import { DEFAULT_MODEL, MODELS } from "@/lib/providers";
|
| 8 |
-
|
| 9 |
-
export async function POST(request: Request) {
|
| 10 |
-
const session = await auth();
|
| 11 |
-
if (!session) {
|
| 12 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 13 |
-
}
|
| 14 |
-
const token = session.accessToken;
|
| 15 |
-
|
| 16 |
-
const body = await request.json();
|
| 17 |
-
const {
|
| 18 |
-
prompt,
|
| 19 |
-
previousMessages = [],
|
| 20 |
-
files = [],
|
| 21 |
-
provider,
|
| 22 |
-
model,
|
| 23 |
-
redesignMd,
|
| 24 |
-
medias,
|
| 25 |
-
} = body;
|
| 26 |
-
|
| 27 |
-
if (!prompt) {
|
| 28 |
-
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
| 29 |
-
}
|
| 30 |
-
if (!model || !MODELS.find((m: (typeof MODELS)[0]) => m.value === model)) {
|
| 31 |
-
return NextResponse.json({ error: "Model is required" }, { status: 400 });
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
const client = new InferenceClient(token);
|
| 35 |
-
|
| 36 |
-
try {
|
| 37 |
-
const encoder = new TextEncoder();
|
| 38 |
-
const stream = new TransformStream();
|
| 39 |
-
const writer = stream.writable.getWriter();
|
| 40 |
-
|
| 41 |
-
const response = new NextResponse(stream.readable, {
|
| 42 |
-
headers: {
|
| 43 |
-
"Content-Type": "text/plain; charset=utf-8",
|
| 44 |
-
"Cache-Control": "no-cache",
|
| 45 |
-
Connection: "keep-alive",
|
| 46 |
-
},
|
| 47 |
-
});
|
| 48 |
-
(async () => {
|
| 49 |
-
let hasRetried = false;
|
| 50 |
-
let currentModel = model;
|
| 51 |
-
|
| 52 |
-
const tryGeneration = async (): Promise<void> => {
|
| 53 |
-
try {
|
| 54 |
-
const chatCompletion = client.chatCompletionStream({
|
| 55 |
-
model: currentModel + (provider !== "auto" ? `:${provider}` : ""),
|
| 56 |
-
messages: [
|
| 57 |
-
{
|
| 58 |
-
role: "system",
|
| 59 |
-
content:
|
| 60 |
-
files.length > 0
|
| 61 |
-
? FOLLOW_UP_SYSTEM_PROMPT
|
| 62 |
-
: INITIAL_SYSTEM_PROMPT,
|
| 63 |
-
},
|
| 64 |
-
...previousMessages.map((message: Message) => ({
|
| 65 |
-
role: message.role,
|
| 66 |
-
content: message.content,
|
| 67 |
-
})),
|
| 68 |
-
...(files?.length > 0
|
| 69 |
-
? [
|
| 70 |
-
{
|
| 71 |
-
role: "user",
|
| 72 |
-
content: `Here are the files that the user has provider:${files
|
| 73 |
-
.map(
|
| 74 |
-
(file: File) =>
|
| 75 |
-
`File: ${file.path}\nContent: ${file.content}`
|
| 76 |
-
)
|
| 77 |
-
.join("\n")}\n\n${prompt}`,
|
| 78 |
-
},
|
| 79 |
-
]
|
| 80 |
-
: []),
|
| 81 |
-
{
|
| 82 |
-
role: "user",
|
| 83 |
-
content: `${
|
| 84 |
-
redesignMd?.url &&
|
| 85 |
-
`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`
|
| 86 |
-
}${prompt} ${
|
| 87 |
-
medias && medias.length > 0
|
| 88 |
-
? `\nHere is the list of my media files: ${medias.join(
|
| 89 |
-
", "
|
| 90 |
-
)}\n`
|
| 91 |
-
: ""
|
| 92 |
-
}`,
|
| 93 |
-
}
|
| 94 |
-
],
|
| 95 |
-
stream: true,
|
| 96 |
-
max_tokens: 16_000,
|
| 97 |
-
});
|
| 98 |
-
while (true) {
|
| 99 |
-
const { done, value } = await chatCompletion.next();
|
| 100 |
-
if (done) {
|
| 101 |
-
break;
|
| 102 |
-
}
|
| 103 |
-
|
| 104 |
-
const chunk = value.choices[0]?.delta?.content;
|
| 105 |
-
if (chunk) {
|
| 106 |
-
await writer.write(encoder.encode(chunk));
|
| 107 |
-
}
|
| 108 |
-
}
|
| 109 |
-
|
| 110 |
-
await writer.close();
|
| 111 |
-
} catch (error) {
|
| 112 |
-
const errorMessage =
|
| 113 |
-
error instanceof Error
|
| 114 |
-
? error.message
|
| 115 |
-
: "An error occurred while processing your request";
|
| 116 |
-
|
| 117 |
-
if (
|
| 118 |
-
!hasRetried &&
|
| 119 |
-
errorMessage?.includes(
|
| 120 |
-
"Failed to perform inference: Model not found"
|
| 121 |
-
)
|
| 122 |
-
) {
|
| 123 |
-
hasRetried = true;
|
| 124 |
-
if (model === DEFAULT_MODEL) {
|
| 125 |
-
const availableFallbackModels = MODELS.filter(
|
| 126 |
-
(m) => m.value !== model
|
| 127 |
-
);
|
| 128 |
-
const randomIndex = Math.floor(
|
| 129 |
-
Math.random() * availableFallbackModels.length
|
| 130 |
-
);
|
| 131 |
-
currentModel = availableFallbackModels[randomIndex];
|
| 132 |
-
} else {
|
| 133 |
-
currentModel = DEFAULT_MODEL;
|
| 134 |
-
}
|
| 135 |
-
const switchMessage = `\n\n_Note: The selected model was not available. Switched to \`${currentModel}\`._\n\n`;
|
| 136 |
-
await writer.write(encoder.encode(switchMessage));
|
| 137 |
-
|
| 138 |
-
return tryGeneration();
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
-
try {
|
| 142 |
-
let errorPayload = "";
|
| 143 |
-
if (
|
| 144 |
-
errorMessage?.includes("exceeded your monthly included credits") ||
|
| 145 |
-
errorMessage?.includes("reached the free monthly usage limit")
|
| 146 |
-
) {
|
| 147 |
-
errorPayload = JSON.stringify({
|
| 148 |
-
messageError: errorMessage,
|
| 149 |
-
showProMessage: true,
|
| 150 |
-
isError: true,
|
| 151 |
-
});
|
| 152 |
-
} else {
|
| 153 |
-
errorPayload = JSON.stringify({
|
| 154 |
-
messageError: errorMessage,
|
| 155 |
-
isError: true,
|
| 156 |
-
});
|
| 157 |
-
}
|
| 158 |
-
await writer.write(encoder.encode(`\n\n__ERROR__:${errorPayload}`));
|
| 159 |
-
await writer.close();
|
| 160 |
-
} catch (closeError) {
|
| 161 |
-
console.error("Failed to send error message:", closeError);
|
| 162 |
-
try {
|
| 163 |
-
await writer.abort(error);
|
| 164 |
-
} catch (abortError) {
|
| 165 |
-
console.error("Failed to abort writer:", abortError);
|
| 166 |
-
}
|
| 167 |
-
}
|
| 168 |
-
}
|
| 169 |
-
};
|
| 170 |
-
|
| 171 |
-
await tryGeneration();
|
| 172 |
-
})();
|
| 173 |
-
|
| 174 |
-
return response;
|
| 175 |
-
} catch (error) {
|
| 176 |
-
return NextResponse.json(
|
| 177 |
-
{
|
| 178 |
-
error: error instanceof Error ? error.message : "Internal Server Error",
|
| 179 |
-
},
|
| 180 |
-
{ status: 500 }
|
| 181 |
-
);
|
| 182 |
-
}
|
| 183 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/auth/[...nextauth]/route.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
import NextAuth from "next-auth";
|
| 2 |
-
import { authOptions } from "@/lib/auth";
|
| 3 |
-
|
| 4 |
-
const handler = NextAuth(authOptions);
|
| 5 |
-
|
| 6 |
-
export { handler as GET, handler as POST };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/healthcheck/route.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
import { NextResponse } from "next/server";
|
| 2 |
-
|
| 3 |
-
export async function GET() {
|
| 4 |
-
return NextResponse.json({ status: "ok" }, { status: 200 });
|
| 5 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/[repoId]/[commitId]/route.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
import { auth } from "@/lib/auth";
|
| 2 |
-
import { createBranch, RepoDesignation } from "@huggingface/hub";
|
| 3 |
-
import { format } from "date-fns";
|
| 4 |
-
import { NextResponse } from "next/server";
|
| 5 |
-
|
| 6 |
-
export async function POST(
|
| 7 |
-
request: Request,
|
| 8 |
-
{ params }: { params: Promise<{ repoId: string; commitId: string }> }
|
| 9 |
-
) {
|
| 10 |
-
const { repoId, commitId }: { repoId: string; commitId: string } =
|
| 11 |
-
await params;
|
| 12 |
-
const session = await auth();
|
| 13 |
-
if (!session) {
|
| 14 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 15 |
-
}
|
| 16 |
-
const token = session.accessToken;
|
| 17 |
-
|
| 18 |
-
const repo: RepoDesignation = {
|
| 19 |
-
type: "space",
|
| 20 |
-
name: session.user?.username + "/" + repoId,
|
| 21 |
-
};
|
| 22 |
-
|
| 23 |
-
const commitTitle = `🔖 ${format(new Date(), "dd/MM")} - ${format(
|
| 24 |
-
new Date(),
|
| 25 |
-
"HH:mm"
|
| 26 |
-
)} - Set commit ${commitId} as default.`;
|
| 27 |
-
|
| 28 |
-
await fetch(
|
| 29 |
-
`https://huggingface.co/api/spaces/${session.user?.username}/${repoId}/branch/main`,
|
| 30 |
-
{
|
| 31 |
-
method: "POST",
|
| 32 |
-
headers: {
|
| 33 |
-
Authorization: `Bearer ${token}`,
|
| 34 |
-
"Content-Type": "application/json",
|
| 35 |
-
},
|
| 36 |
-
body: JSON.stringify({
|
| 37 |
-
startingPoint: commitId,
|
| 38 |
-
overwrite: true,
|
| 39 |
-
}),
|
| 40 |
-
}
|
| 41 |
-
).catch((error) => {
|
| 42 |
-
return NextResponse.json(
|
| 43 |
-
{ error: error ?? "Failed to create branch" },
|
| 44 |
-
{ status: 500 }
|
| 45 |
-
);
|
| 46 |
-
});
|
| 47 |
-
|
| 48 |
-
return NextResponse.json({ success: true }, { status: 200 });
|
| 49 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/[repoId]/download/route.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
| 1 |
-
import { auth } from "@/lib/auth";
|
| 2 |
-
import { downloadFile, listFiles, RepoDesignation } from "@huggingface/hub";
|
| 3 |
-
import { NextResponse } from "next/server";
|
| 4 |
-
import JSZip from "jszip";
|
| 5 |
-
|
| 6 |
-
export async function GET(
|
| 7 |
-
request: Request,
|
| 8 |
-
{ params }: { params: Promise<{ repoId: string }> }
|
| 9 |
-
) {
|
| 10 |
-
const { repoId }: { repoId: string } = await params;
|
| 11 |
-
const session = await auth();
|
| 12 |
-
if (!session) {
|
| 13 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 14 |
-
}
|
| 15 |
-
const token = session.accessToken;
|
| 16 |
-
const repo: RepoDesignation = {
|
| 17 |
-
type: "space",
|
| 18 |
-
name: session.user?.username + "/" + repoId,
|
| 19 |
-
};
|
| 20 |
-
|
| 21 |
-
try {
|
| 22 |
-
const zip = new JSZip();
|
| 23 |
-
for await (const fileInfo of listFiles({
|
| 24 |
-
repo,
|
| 25 |
-
accessToken: token as string,
|
| 26 |
-
recursive: true,
|
| 27 |
-
})) {
|
| 28 |
-
if (fileInfo.type === "directory" || fileInfo.path.startsWith(".")) {
|
| 29 |
-
continue;
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
try {
|
| 33 |
-
const blob = await downloadFile({
|
| 34 |
-
repo,
|
| 35 |
-
accessToken: token as string,
|
| 36 |
-
path: fileInfo.path,
|
| 37 |
-
raw: true
|
| 38 |
-
}).catch((error) => {
|
| 39 |
-
return null;
|
| 40 |
-
});
|
| 41 |
-
if (!blob) {
|
| 42 |
-
continue;
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
if (blob) {
|
| 46 |
-
const arrayBuffer = await blob.arrayBuffer();
|
| 47 |
-
zip.file(fileInfo.path, arrayBuffer);
|
| 48 |
-
}
|
| 49 |
-
} catch (error) {
|
| 50 |
-
console.error(`Error downloading file ${fileInfo.path}:`, error);
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
const zipBlob = await zip.generateAsync({
|
| 55 |
-
type: "blob",
|
| 56 |
-
compression: "DEFLATE",
|
| 57 |
-
compressionOptions: {
|
| 58 |
-
level: 6
|
| 59 |
-
}
|
| 60 |
-
});
|
| 61 |
-
|
| 62 |
-
const projectName = `${session.user?.username}-${repoId}`.replace(/[^a-zA-Z0-9-_]/g, '_');
|
| 63 |
-
const filename = `${projectName}.zip`;
|
| 64 |
-
|
| 65 |
-
return new NextResponse(zipBlob, {
|
| 66 |
-
headers: {
|
| 67 |
-
"Content-Type": "application/zip",
|
| 68 |
-
"Content-Disposition": `attachment; filename="${filename}"`,
|
| 69 |
-
"Content-Length": zipBlob.size.toString(),
|
| 70 |
-
},
|
| 71 |
-
});
|
| 72 |
-
} catch (error) {
|
| 73 |
-
console.error("Error downloading project:", error);
|
| 74 |
-
return NextResponse.json({ error: "Failed to download project" }, { status: 500 });
|
| 75 |
-
}
|
| 76 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/[repoId]/medias/route.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
| 1 |
-
import { auth } from "@/lib/auth";
|
| 2 |
-
import { RepoDesignation, uploadFiles } from "@huggingface/hub";
|
| 3 |
-
import { NextResponse } from "next/server";
|
| 4 |
-
|
| 5 |
-
export async function POST(
|
| 6 |
-
request: Request,
|
| 7 |
-
{ params }: { params: Promise<{ repoId: string }> }
|
| 8 |
-
) {
|
| 9 |
-
const { repoId }: { repoId: string } = await params;
|
| 10 |
-
const session = await auth();
|
| 11 |
-
if (!session) {
|
| 12 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 13 |
-
}
|
| 14 |
-
const token = session.accessToken;
|
| 15 |
-
|
| 16 |
-
const repo: RepoDesignation = {
|
| 17 |
-
type: "space",
|
| 18 |
-
name: session.user?.username + "/" + repoId,
|
| 19 |
-
};
|
| 20 |
-
|
| 21 |
-
const formData = await request.formData();
|
| 22 |
-
const newMedias = formData.getAll("images") as File[];
|
| 23 |
-
|
| 24 |
-
const filesToUpload: File[] = [];
|
| 25 |
-
|
| 26 |
-
if (!newMedias || newMedias.length === 0) {
|
| 27 |
-
return NextResponse.json(
|
| 28 |
-
{
|
| 29 |
-
ok: false,
|
| 30 |
-
error: "At least one media file is required under the 'images' key",
|
| 31 |
-
},
|
| 32 |
-
{ status: 400 }
|
| 33 |
-
);
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
try {
|
| 37 |
-
for (const media of newMedias) {
|
| 38 |
-
const isImage = media.type.startsWith("image/");
|
| 39 |
-
const isVideo = media.type.startsWith("video/");
|
| 40 |
-
const isAudio = media.type.startsWith("audio/");
|
| 41 |
-
|
| 42 |
-
const folderPath = isImage
|
| 43 |
-
? "images/"
|
| 44 |
-
: isVideo
|
| 45 |
-
? "videos/"
|
| 46 |
-
: isAudio
|
| 47 |
-
? "audios/"
|
| 48 |
-
: null;
|
| 49 |
-
|
| 50 |
-
if (!folderPath) {
|
| 51 |
-
return NextResponse.json(
|
| 52 |
-
{ ok: false, error: "Unsupported media type: " + media.type },
|
| 53 |
-
{ status: 400 }
|
| 54 |
-
);
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
const mediaName = `${folderPath}${media.name}`;
|
| 58 |
-
const processedFile = new File([media], mediaName, { type: media.type });
|
| 59 |
-
filesToUpload.push(processedFile);
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
await uploadFiles({
|
| 63 |
-
repo,
|
| 64 |
-
files: filesToUpload,
|
| 65 |
-
accessToken: token,
|
| 66 |
-
commitTitle: `📁 Upload media files through DeepSite`,
|
| 67 |
-
});
|
| 68 |
-
|
| 69 |
-
return NextResponse.json(
|
| 70 |
-
{
|
| 71 |
-
success: true,
|
| 72 |
-
medias: filesToUpload.map(
|
| 73 |
-
(file) =>
|
| 74 |
-
`https://huggingface.co/spaces/${session.user?.username}/${repoId}/resolve/main/${file.name}`
|
| 75 |
-
),
|
| 76 |
-
},
|
| 77 |
-
{ status: 200 }
|
| 78 |
-
);
|
| 79 |
-
} catch (error) {
|
| 80 |
-
return NextResponse.json(
|
| 81 |
-
{ ok: false, error: error ?? "Failed to upload media files" },
|
| 82 |
-
{ status: 500 }
|
| 83 |
-
);
|
| 84 |
-
}
|
| 85 |
-
|
| 86 |
-
return NextResponse.json({ success: true }, { status: 200 });
|
| 87 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/[repoId]/rename/route.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
import { auth } from "@/lib/auth";
|
| 2 |
-
import { downloadFile, RepoDesignation, uploadFile } from "@huggingface/hub";
|
| 3 |
-
import { format } from "date-fns";
|
| 4 |
-
import { NextResponse } from "next/server";
|
| 5 |
-
|
| 6 |
-
export async function PUT(
|
| 7 |
-
request: Request,
|
| 8 |
-
{ params }: { params: Promise<{ repoId: string }> }
|
| 9 |
-
) {
|
| 10 |
-
const { repoId }: { repoId: string } = await params;
|
| 11 |
-
const session = await auth();
|
| 12 |
-
if (!session) {
|
| 13 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 14 |
-
}
|
| 15 |
-
const token = session.accessToken;
|
| 16 |
-
|
| 17 |
-
const body = await request.json();
|
| 18 |
-
const { newTitle } = body;
|
| 19 |
-
|
| 20 |
-
if (!newTitle) {
|
| 21 |
-
return NextResponse.json(
|
| 22 |
-
{ error: "newTitle is required" },
|
| 23 |
-
{ status: 400 }
|
| 24 |
-
);
|
| 25 |
-
}
|
| 26 |
-
|
| 27 |
-
const repo: RepoDesignation = {
|
| 28 |
-
type: "space",
|
| 29 |
-
name: session.user?.username + "/" + repoId,
|
| 30 |
-
};
|
| 31 |
-
|
| 32 |
-
const blob = await downloadFile({
|
| 33 |
-
repo,
|
| 34 |
-
accessToken: token,
|
| 35 |
-
path: "README.md",
|
| 36 |
-
raw: true,
|
| 37 |
-
}).catch((_) => {
|
| 38 |
-
return null;
|
| 39 |
-
});
|
| 40 |
-
|
| 41 |
-
if (!blob) {
|
| 42 |
-
return NextResponse.json(
|
| 43 |
-
{ error: "Could not fetch README.md" },
|
| 44 |
-
{ status: 500 }
|
| 45 |
-
);
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
const readmeFile = await blob?.text();
|
| 49 |
-
if (!readmeFile) {
|
| 50 |
-
return NextResponse.json(
|
| 51 |
-
{ error: "Could not read README.md content" },
|
| 52 |
-
{ status: 500 }
|
| 53 |
-
);
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
// Escape YAML values to prevent injection attacks
|
| 57 |
-
const escapeYamlValue = (value: string): string => {
|
| 58 |
-
if (/[:|>]|^[-*#]|^\s|['"]/.test(value) || value.includes("\n")) {
|
| 59 |
-
return `"${value.replace(/"/g, '\\"')}"`;
|
| 60 |
-
}
|
| 61 |
-
return value;
|
| 62 |
-
};
|
| 63 |
-
|
| 64 |
-
// Escape commit message to prevent injection
|
| 65 |
-
const escapeCommitMessage = (message: string): string => {
|
| 66 |
-
return message.replace(/[\r\n]/g, " ").slice(0, 200);
|
| 67 |
-
};
|
| 68 |
-
|
| 69 |
-
const updatedReadmeFile = readmeFile.replace(
|
| 70 |
-
/^title:\s*(.*)$/m,
|
| 71 |
-
`title: ${escapeYamlValue(newTitle)}`
|
| 72 |
-
);
|
| 73 |
-
|
| 74 |
-
await uploadFile({
|
| 75 |
-
repo,
|
| 76 |
-
accessToken: token,
|
| 77 |
-
file: new File([updatedReadmeFile], "README.md", { type: "text/markdown" }),
|
| 78 |
-
commitTitle: escapeCommitMessage(
|
| 79 |
-
`🐳 ${format(new Date(), "dd/MM")} - ${format(
|
| 80 |
-
new Date(),
|
| 81 |
-
"HH:mm"
|
| 82 |
-
)} - Rename project to "${newTitle}"`
|
| 83 |
-
),
|
| 84 |
-
});
|
| 85 |
-
|
| 86 |
-
return NextResponse.json(
|
| 87 |
-
{
|
| 88 |
-
success: true,
|
| 89 |
-
},
|
| 90 |
-
{ status: 200 }
|
| 91 |
-
);
|
| 92 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/[repoId]/route.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
| 1 |
-
import { auth } from "@/lib/auth";
|
| 2 |
-
import { RepoDesignation, deleteRepo, uploadFiles } from "@huggingface/hub";
|
| 3 |
-
import { format } from "date-fns";
|
| 4 |
-
import { NextResponse } from "next/server";
|
| 5 |
-
|
| 6 |
-
export async function PUT(
|
| 7 |
-
request: Request,
|
| 8 |
-
{ params }: { params: Promise<{ repoId: string }> }
|
| 9 |
-
) {
|
| 10 |
-
const { repoId }: { repoId: string } = await params;
|
| 11 |
-
const session = await auth();
|
| 12 |
-
if (!session) {
|
| 13 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 14 |
-
}
|
| 15 |
-
const token = session.accessToken;
|
| 16 |
-
|
| 17 |
-
const body = await request.json();
|
| 18 |
-
const { files, prompt, isManualChanges } = body;
|
| 19 |
-
|
| 20 |
-
if (!files) {
|
| 21 |
-
return NextResponse.json({ error: "Files are required" }, { status: 400 });
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
if (!prompt) {
|
| 25 |
-
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
const repo: RepoDesignation = {
|
| 29 |
-
type: "space",
|
| 30 |
-
name: session.user?.username + "/" + repoId,
|
| 31 |
-
};
|
| 32 |
-
|
| 33 |
-
const filesToUpload: File[] = [];
|
| 34 |
-
for (const file of files) {
|
| 35 |
-
let mimeType = "text/x-python";
|
| 36 |
-
if (file.path.endsWith(".txt")) {
|
| 37 |
-
mimeType = "text/plain";
|
| 38 |
-
} else if (file.path.endsWith(".md")) {
|
| 39 |
-
mimeType = "text/markdown";
|
| 40 |
-
} else if (file.path.endsWith(".json")) {
|
| 41 |
-
mimeType = "application/json";
|
| 42 |
-
}
|
| 43 |
-
filesToUpload.push(new File([file.content], file.path, { type: mimeType }));
|
| 44 |
-
}
|
| 45 |
-
// Escape commit title to prevent injection
|
| 46 |
-
const escapeCommitTitle = (title: string): string => {
|
| 47 |
-
return title.replace(/[\r\n]/g, " ").slice(0, 200);
|
| 48 |
-
};
|
| 49 |
-
|
| 50 |
-
const baseTitle = isManualChanges
|
| 51 |
-
? ""
|
| 52 |
-
: `🐳 ${format(new Date(), "dd/MM")} - ${format(new Date(), "HH:mm")} - `;
|
| 53 |
-
const commitTitle = escapeCommitTitle(
|
| 54 |
-
baseTitle + (prompt ?? "Follow-up DeepSite commit")
|
| 55 |
-
);
|
| 56 |
-
const response = await uploadFiles({
|
| 57 |
-
repo,
|
| 58 |
-
files: filesToUpload,
|
| 59 |
-
accessToken: token,
|
| 60 |
-
commitTitle,
|
| 61 |
-
});
|
| 62 |
-
|
| 63 |
-
return NextResponse.json(
|
| 64 |
-
{
|
| 65 |
-
success: true,
|
| 66 |
-
commit: {
|
| 67 |
-
oid: response.commit,
|
| 68 |
-
title: commitTitle,
|
| 69 |
-
date: new Date(),
|
| 70 |
-
},
|
| 71 |
-
},
|
| 72 |
-
{ status: 200 }
|
| 73 |
-
);
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
export async function DELETE(
|
| 77 |
-
request: Request,
|
| 78 |
-
{ params }: { params: Promise<{ repoId: string }> }
|
| 79 |
-
) {
|
| 80 |
-
const { repoId }: { repoId: string } = await params;
|
| 81 |
-
const session = await auth();
|
| 82 |
-
if (!session) {
|
| 83 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 84 |
-
}
|
| 85 |
-
const token = session.accessToken;
|
| 86 |
-
|
| 87 |
-
const repo: RepoDesignation = {
|
| 88 |
-
type: "space",
|
| 89 |
-
name: session.user?.username + "/" + repoId,
|
| 90 |
-
};
|
| 91 |
-
|
| 92 |
-
try {
|
| 93 |
-
await deleteRepo({
|
| 94 |
-
repo,
|
| 95 |
-
accessToken: token as string,
|
| 96 |
-
});
|
| 97 |
-
|
| 98 |
-
return NextResponse.json({ success: true }, { status: 200 });
|
| 99 |
-
} catch (error) {
|
| 100 |
-
const errMsg =
|
| 101 |
-
error instanceof Error ? error.message : "Failed to delete project";
|
| 102 |
-
return NextResponse.json({ error: errMsg }, { status: 500 });
|
| 103 |
-
}
|
| 104 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/projects/route.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
| 1 |
-
import { NextResponse } from "next/server";
|
| 2 |
-
import { RepoDesignation, createRepo, uploadFiles } from "@huggingface/hub";
|
| 3 |
-
|
| 4 |
-
import { auth } from "@/lib/auth";
|
| 5 |
-
import {
|
| 6 |
-
COLORS,
|
| 7 |
-
EMOJIS_FOR_SPACE,
|
| 8 |
-
injectDeepSiteBadge,
|
| 9 |
-
isIndexPage,
|
| 10 |
-
} from "@/lib/utils";
|
| 11 |
-
|
| 12 |
-
// todo: catch error while publishing project, and return the error to the user
|
| 13 |
-
// if space has been created, but can't push, try again or catch well the error and return the error to the user
|
| 14 |
-
|
| 15 |
-
export async function POST(request: Request) {
|
| 16 |
-
const session = await auth();
|
| 17 |
-
if (!session) {
|
| 18 |
-
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
| 19 |
-
}
|
| 20 |
-
const token = session.accessToken;
|
| 21 |
-
|
| 22 |
-
const body = await request.json();
|
| 23 |
-
const { projectTitle, files, prompt } = body;
|
| 24 |
-
|
| 25 |
-
if (!files) {
|
| 26 |
-
return NextResponse.json(
|
| 27 |
-
{ error: "Project title and files are required" },
|
| 28 |
-
{ status: 400 }
|
| 29 |
-
);
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
const title =
|
| 33 |
-
projectTitle || projectTitle !== "" ? projectTitle : "DeepSite Project";
|
| 34 |
-
|
| 35 |
-
let formattedTitle = title
|
| 36 |
-
.toLowerCase()
|
| 37 |
-
.replace(/[^a-z0-9]+/g, "-")
|
| 38 |
-
.split("-")
|
| 39 |
-
.filter(Boolean)
|
| 40 |
-
.join("-")
|
| 41 |
-
.slice(0, 75);
|
| 42 |
-
|
| 43 |
-
formattedTitle =
|
| 44 |
-
formattedTitle + "-" + Math.random().toString(36).substring(2, 7);
|
| 45 |
-
|
| 46 |
-
const repo: RepoDesignation = {
|
| 47 |
-
type: "space",
|
| 48 |
-
name: session.user?.username + "/" + formattedTitle,
|
| 49 |
-
};
|
| 50 |
-
|
| 51 |
-
// Escape YAML values to prevent injection attacks
|
| 52 |
-
const escapeYamlValue = (value: string): string => {
|
| 53 |
-
if (/[:|>]|^[-*#]|^\s|['"]/.test(value) || value.includes("\n")) {
|
| 54 |
-
return `"${value.replace(/"/g, '\\"')}"`;
|
| 55 |
-
}
|
| 56 |
-
return value;
|
| 57 |
-
};
|
| 58 |
-
|
| 59 |
-
// Escape markdown headers to prevent injection
|
| 60 |
-
const escapeMarkdownHeader = (value: string): string => {
|
| 61 |
-
return value.replace(/^#+\s*/g, "").replace(/\n/g, " ");
|
| 62 |
-
};
|
| 63 |
-
|
| 64 |
-
const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)];
|
| 65 |
-
const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)];
|
| 66 |
-
const emoji =
|
| 67 |
-
EMOJIS_FOR_SPACE[Math.floor(Math.random() * EMOJIS_FOR_SPACE.length)];
|
| 68 |
-
const README = `---
|
| 69 |
-
title: ${escapeYamlValue(projectTitle)}
|
| 70 |
-
colorFrom: ${colorFrom}
|
| 71 |
-
colorTo: ${colorTo}
|
| 72 |
-
sdk: static
|
| 73 |
-
emoji: ${emoji}
|
| 74 |
-
tags:
|
| 75 |
-
- deepsite-v4
|
| 76 |
-
---
|
| 77 |
-
|
| 78 |
-
# ${escapeMarkdownHeader(title)}
|
| 79 |
-
|
| 80 |
-
This project has been created with [DeepSite](https://deepsite.hf.co) AI Vibe Coding.
|
| 81 |
-
`;
|
| 82 |
-
|
| 83 |
-
const filesToUpload: File[] = [
|
| 84 |
-
new File([README], "README.md", { type: "text/markdown" }),
|
| 85 |
-
];
|
| 86 |
-
for (const file of files) {
|
| 87 |
-
let mimeType = "text/html";
|
| 88 |
-
if (file.path.endsWith(".css")) {
|
| 89 |
-
mimeType = "text/css";
|
| 90 |
-
} else if (file.path.endsWith(".js")) {
|
| 91 |
-
mimeType = "text/javascript";
|
| 92 |
-
}
|
| 93 |
-
const content =
|
| 94 |
-
mimeType === "text/html" && isIndexPage(file.path)
|
| 95 |
-
? injectDeepSiteBadge(file.content)
|
| 96 |
-
: file.content;
|
| 97 |
-
|
| 98 |
-
filesToUpload.push(new File([content], file.path, { type: mimeType }));
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
let repoUrl: string | undefined;
|
| 102 |
-
|
| 103 |
-
try {
|
| 104 |
-
// Create the space first
|
| 105 |
-
const createResult = await createRepo({
|
| 106 |
-
accessToken: token as string,
|
| 107 |
-
repo: repo,
|
| 108 |
-
sdk: "static",
|
| 109 |
-
});
|
| 110 |
-
repoUrl = createResult.repoUrl;
|
| 111 |
-
|
| 112 |
-
// Escape commit message to prevent injection
|
| 113 |
-
const escapeCommitMessage = (message: string): string => {
|
| 114 |
-
return message.replace(/[\r\n]/g, " ").slice(0, 200);
|
| 115 |
-
};
|
| 116 |
-
const commitMessage = escapeCommitMessage(prompt ?? "Initial DeepSite commit");
|
| 117 |
-
|
| 118 |
-
// Upload files to the created space
|
| 119 |
-
await uploadFiles({
|
| 120 |
-
repo,
|
| 121 |
-
files: filesToUpload,
|
| 122 |
-
accessToken: token as string,
|
| 123 |
-
commitTitle: commitMessage,
|
| 124 |
-
});
|
| 125 |
-
|
| 126 |
-
const path = repoUrl.split("/").slice(-2).join("/");
|
| 127 |
-
|
| 128 |
-
return NextResponse.json({ repoUrl: path }, { status: 200 });
|
| 129 |
-
} catch (error) {
|
| 130 |
-
const errMsg =
|
| 131 |
-
error instanceof Error ? error.message : "Failed to create or upload to space";
|
| 132 |
-
|
| 133 |
-
// If space was created but upload failed, include the repo URL in the error
|
| 134 |
-
if (repoUrl) {
|
| 135 |
-
const path = repoUrl.split("/").slice(-2).join("/");
|
| 136 |
-
return NextResponse.json({
|
| 137 |
-
error: `${errMsg}. Space was created at ${path} but files could not be uploaded.`,
|
| 138 |
-
repoUrl: path,
|
| 139 |
-
partialSuccess: true
|
| 140 |
-
}, { status: 500 });
|
| 141 |
-
}
|
| 142 |
-
|
| 143 |
-
return NextResponse.json({ error: errMsg }, { status: 500 });
|
| 144 |
-
}
|
| 145 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/redesign/route.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
| 2 |
-
import { NextRequest, NextResponse } from "next/server";
|
| 3 |
-
|
| 4 |
-
const FETCH_TIMEOUT = 30_000;
|
| 5 |
-
export const maxDuration = 60;
|
| 6 |
-
|
| 7 |
-
export async function PUT(request: NextRequest) {
|
| 8 |
-
const body = await request.json();
|
| 9 |
-
const { url } = body;
|
| 10 |
-
|
| 11 |
-
if (!url) {
|
| 12 |
-
return NextResponse.json({ error: "URL is required" }, { status: 400 });
|
| 13 |
-
}
|
| 14 |
-
|
| 15 |
-
try {
|
| 16 |
-
const controller = new AbortController();
|
| 17 |
-
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
|
| 18 |
-
|
| 19 |
-
try {
|
| 20 |
-
const response = await fetch(
|
| 21 |
-
`https://r.jina.ai/${encodeURIComponent(url)}`,
|
| 22 |
-
{
|
| 23 |
-
method: "POST",
|
| 24 |
-
signal: controller.signal,
|
| 25 |
-
}
|
| 26 |
-
);
|
| 27 |
-
|
| 28 |
-
clearTimeout(timeoutId);
|
| 29 |
-
|
| 30 |
-
if (!response.ok) {
|
| 31 |
-
return NextResponse.json(
|
| 32 |
-
{ error: "Failed to fetch redesign" },
|
| 33 |
-
{ status: 500 }
|
| 34 |
-
);
|
| 35 |
-
}
|
| 36 |
-
const markdown = await response.text();
|
| 37 |
-
return NextResponse.json(
|
| 38 |
-
{
|
| 39 |
-
ok: true,
|
| 40 |
-
markdown,
|
| 41 |
-
},
|
| 42 |
-
{ status: 200 }
|
| 43 |
-
);
|
| 44 |
-
} catch (fetchError: any) {
|
| 45 |
-
clearTimeout(timeoutId);
|
| 46 |
-
|
| 47 |
-
if (fetchError.name === "AbortError") {
|
| 48 |
-
return NextResponse.json(
|
| 49 |
-
{
|
| 50 |
-
error:
|
| 51 |
-
"Request timeout: The external service took too long to respond. Please try again.",
|
| 52 |
-
},
|
| 53 |
-
{ status: 504 }
|
| 54 |
-
);
|
| 55 |
-
}
|
| 56 |
-
throw fetchError;
|
| 57 |
-
}
|
| 58 |
-
} catch (error: any) {
|
| 59 |
-
if (error.name === "AbortError" || error.message?.includes("timeout")) {
|
| 60 |
-
return NextResponse.json(
|
| 61 |
-
{
|
| 62 |
-
error:
|
| 63 |
-
"Request timeout: The external service took too long to respond. Please try again.",
|
| 64 |
-
},
|
| 65 |
-
{ status: 504 }
|
| 66 |
-
);
|
| 67 |
-
}
|
| 68 |
-
return NextResponse.json(
|
| 69 |
-
{ error: error.message || "An error occurred" },
|
| 70 |
-
{ status: 500 }
|
| 71 |
-
);
|
| 72 |
-
}
|
| 73 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/favicon.ico
DELETED
|
Binary file (25.9 kB)
|
|
|
app/layout.tsx
DELETED
|
@@ -1,108 +0,0 @@
|
|
| 1 |
-
import type { Metadata, Viewport } from "next";
|
| 2 |
-
import { Geist, Geist_Mono } from "next/font/google";
|
| 3 |
-
import { NextStepProvider } from "nextstepjs";
|
| 4 |
-
import Script from "next/script";
|
| 5 |
-
|
| 6 |
-
import "@/app/globals.css";
|
| 7 |
-
import { ThemeProvider } from "@/components/providers/theme";
|
| 8 |
-
import { AuthProvider } from "@/components/providers/session";
|
| 9 |
-
import { Toaster } from "@/components/ui/sonner";
|
| 10 |
-
import { ReactQueryProvider } from "@/components/providers/react-query";
|
| 11 |
-
import { generateSEO, generateStructuredData } from "@/lib/seo";
|
| 12 |
-
import { NotAuthorizedDomain } from "@/components/not-authorized";
|
| 13 |
-
|
| 14 |
-
const geistSans = Geist({
|
| 15 |
-
variable: "--font-geist-sans",
|
| 16 |
-
subsets: ["latin"],
|
| 17 |
-
});
|
| 18 |
-
|
| 19 |
-
const geistMono = Geist_Mono({
|
| 20 |
-
variable: "--font-geist-mono",
|
| 21 |
-
subsets: ["latin"],
|
| 22 |
-
});
|
| 23 |
-
|
| 24 |
-
export const metadata: Metadata = {
|
| 25 |
-
...generateSEO({
|
| 26 |
-
title: "DeepSite | Build with AI ✨",
|
| 27 |
-
description:
|
| 28 |
-
"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.",
|
| 29 |
-
path: "/",
|
| 30 |
-
}),
|
| 31 |
-
appleWebApp: {
|
| 32 |
-
capable: true,
|
| 33 |
-
title: "DeepSite",
|
| 34 |
-
statusBarStyle: "black-translucent",
|
| 35 |
-
},
|
| 36 |
-
icons: {
|
| 37 |
-
icon: "/logo.svg",
|
| 38 |
-
shortcut: "/logo.svg",
|
| 39 |
-
apple: "/logo.svg",
|
| 40 |
-
},
|
| 41 |
-
verification: {
|
| 42 |
-
google: process.env.GOOGLE_SITE_VERIFICATION,
|
| 43 |
-
},
|
| 44 |
-
};
|
| 45 |
-
|
| 46 |
-
export const viewport: Viewport = {
|
| 47 |
-
initialScale: 1,
|
| 48 |
-
maximumScale: 1,
|
| 49 |
-
themeColor: "#4f46e5",
|
| 50 |
-
};
|
| 51 |
-
|
| 52 |
-
export default async function RootLayout({
|
| 53 |
-
children,
|
| 54 |
-
}: Readonly<{
|
| 55 |
-
children: React.ReactNode;
|
| 56 |
-
}>) {
|
| 57 |
-
const structuredData = generateStructuredData("WebApplication", {
|
| 58 |
-
name: "DeepSite",
|
| 59 |
-
description: "Build websites with AI, no code required",
|
| 60 |
-
url: "https://deepsite.hf.co",
|
| 61 |
-
});
|
| 62 |
-
const organizationData = generateStructuredData("Organization", {
|
| 63 |
-
name: "DeepSite",
|
| 64 |
-
url: "https://deepsite.hf.co",
|
| 65 |
-
});
|
| 66 |
-
|
| 67 |
-
return (
|
| 68 |
-
<html lang="en" suppressHydrationWarning>
|
| 69 |
-
<body
|
| 70 |
-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
| 71 |
-
>
|
| 72 |
-
<script
|
| 73 |
-
type="application/ld+json"
|
| 74 |
-
dangerouslySetInnerHTML={{
|
| 75 |
-
__html: JSON.stringify(structuredData),
|
| 76 |
-
}}
|
| 77 |
-
/>
|
| 78 |
-
<script
|
| 79 |
-
type="application/ld+json"
|
| 80 |
-
dangerouslySetInnerHTML={{
|
| 81 |
-
__html: JSON.stringify(organizationData),
|
| 82 |
-
}}
|
| 83 |
-
/>
|
| 84 |
-
<Script
|
| 85 |
-
defer
|
| 86 |
-
data-domain="deepsite.hf.co"
|
| 87 |
-
src="https://plausible.io/js/script.js"
|
| 88 |
-
/>
|
| 89 |
-
<Toaster richColors />
|
| 90 |
-
<AuthProvider>
|
| 91 |
-
<ReactQueryProvider>
|
| 92 |
-
<ThemeProvider
|
| 93 |
-
attribute="class"
|
| 94 |
-
defaultTheme="dark"
|
| 95 |
-
enableSystem
|
| 96 |
-
disableTransitionOnChange
|
| 97 |
-
>
|
| 98 |
-
<NextStepProvider>
|
| 99 |
-
{children}
|
| 100 |
-
<NotAuthorizedDomain />
|
| 101 |
-
</NextStepProvider>
|
| 102 |
-
</ThemeProvider>
|
| 103 |
-
</ReactQueryProvider>
|
| 104 |
-
</AuthProvider>
|
| 105 |
-
</body>
|
| 106 |
-
</html>
|
| 107 |
-
);
|
| 108 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/new/page.tsx
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
import { AppEditor } from "@/components/editor";
|
| 2 |
-
import { auth } from "@/lib/auth";
|
| 3 |
-
import { redirect } from "next/navigation";
|
| 4 |
-
|
| 5 |
-
export default async function NewProjectPage({
|
| 6 |
-
searchParams,
|
| 7 |
-
}: {
|
| 8 |
-
searchParams: Promise<{ prompt: string }>;
|
| 9 |
-
}) {
|
| 10 |
-
const session = await auth();
|
| 11 |
-
|
| 12 |
-
if (!session) {
|
| 13 |
-
redirect("/api/auth/signin?callbackUrl=/new");
|
| 14 |
-
}
|
| 15 |
-
|
| 16 |
-
const { prompt } = await searchParams;
|
| 17 |
-
return <AppEditor isNew={true} initialPrompt={prompt} />;
|
| 18 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/not-found.tsx
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
import { NotFoundButtons } from "@/components/not-found/buttons";
|
| 2 |
-
import { Navigation } from "@/components/public/navigation";
|
| 3 |
-
|
| 4 |
-
export default function NotFound() {
|
| 5 |
-
return (
|
| 6 |
-
<div className="min-h-screen font-sans">
|
| 7 |
-
<Navigation />
|
| 8 |
-
<div className="px-6 py-16 max-w-5xl mx-auto text-center">
|
| 9 |
-
<h1 className="text-5xl font-bold mb-5">Oh no! Page not found.</h1>
|
| 10 |
-
<p className="text-lg text-muted-foreground mb-8">
|
| 11 |
-
The page you are looking for does not exist.
|
| 12 |
-
</p>
|
| 13 |
-
<NotFoundButtons />
|
| 14 |
-
</div>
|
| 15 |
-
</div>
|
| 16 |
-
);
|
| 17 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{app → assets}/globals.css
RENAMED
|
@@ -6,8 +6,8 @@
|
|
| 6 |
@theme inline {
|
| 7 |
--color-background: var(--background);
|
| 8 |
--color-foreground: var(--foreground);
|
| 9 |
-
--font-sans: var(--font-
|
| 10 |
-
--font-mono: var(--font-
|
| 11 |
--color-sidebar-ring: var(--sidebar-ring);
|
| 12 |
--color-sidebar-border: var(--sidebar-border);
|
| 13 |
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
@@ -44,7 +44,7 @@
|
|
| 44 |
}
|
| 45 |
|
| 46 |
:root {
|
| 47 |
-
--radius: 0.
|
| 48 |
--background: oklch(1 0 0);
|
| 49 |
--foreground: oklch(0.145 0 0);
|
| 50 |
--card: oklch(1 0 0);
|
|
@@ -68,7 +68,6 @@
|
|
| 68 |
--chart-3: oklch(0.398 0.07 227.392);
|
| 69 |
--chart-4: oklch(0.828 0.189 84.429);
|
| 70 |
--chart-5: oklch(0.769 0.188 70.08);
|
| 71 |
-
--radius: 0.625rem;
|
| 72 |
--sidebar: oklch(0.985 0 0);
|
| 73 |
--sidebar-foreground: oklch(0.145 0 0);
|
| 74 |
--sidebar-primary: oklch(0.205 0 0);
|
|
@@ -113,6 +112,10 @@
|
|
| 113 |
--sidebar-ring: oklch(0.556 0 0);
|
| 114 |
}
|
| 115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
@layer base {
|
| 117 |
* {
|
| 118 |
@apply border-border outline-ring/50;
|
|
@@ -120,49 +123,258 @@
|
|
| 120 |
body {
|
| 121 |
@apply bg-background text-foreground;
|
| 122 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
}
|
| 124 |
|
| 125 |
.monaco-editor .margin {
|
| 126 |
-
@apply bg-
|
| 127 |
}
|
| 128 |
.monaco-editor .monaco-editor-background {
|
| 129 |
-
@apply bg-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
}
|
| 134 |
-
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
}
|
| 137 |
-
|
| 138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
}
|
| 140 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
}
|
| 143 |
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
position: absolute;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
pointer-events: none;
|
| 148 |
-
|
| 149 |
-
|
| 150 |
}
|
| 151 |
|
| 152 |
-
.
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
.sp-explorer[data-active="true"] {
|
| 156 |
-
@apply text-indigo-500!;
|
| 157 |
}
|
| 158 |
|
| 159 |
-
.
|
| 160 |
-
|
| 161 |
-
.sp-tabs
|
| 162 |
-
.sp-tab-container[aria-selected="true"]
|
| 163 |
-
.sp-tab-button {
|
| 164 |
-
@apply text-indigo-500!;
|
| 165 |
-
}
|
| 166 |
-
.sp-layout .sp-stack .sp-tabs .sp-tab-container:has(button:focus) {
|
| 167 |
-
@apply outline-none! border-none!;
|
| 168 |
}
|
|
|
|
| 6 |
@theme inline {
|
| 7 |
--color-background: var(--background);
|
| 8 |
--color-foreground: var(--foreground);
|
| 9 |
+
--font-sans: var(--font-inter-sans);
|
| 10 |
+
--font-mono: var(--font-ptSans-mono);
|
| 11 |
--color-sidebar-ring: var(--sidebar-ring);
|
| 12 |
--color-sidebar-border: var(--sidebar-border);
|
| 13 |
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
|
|
| 44 |
}
|
| 45 |
|
| 46 |
:root {
|
| 47 |
+
--radius: 0.625rem;
|
| 48 |
--background: oklch(1 0 0);
|
| 49 |
--foreground: oklch(0.145 0 0);
|
| 50 |
--card: oklch(1 0 0);
|
|
|
|
| 68 |
--chart-3: oklch(0.398 0.07 227.392);
|
| 69 |
--chart-4: oklch(0.828 0.189 84.429);
|
| 70 |
--chart-5: oklch(0.769 0.188 70.08);
|
|
|
|
| 71 |
--sidebar: oklch(0.985 0 0);
|
| 72 |
--sidebar-foreground: oklch(0.145 0 0);
|
| 73 |
--sidebar-primary: oklch(0.205 0 0);
|
|
|
|
| 112 |
--sidebar-ring: oklch(0.556 0 0);
|
| 113 |
}
|
| 114 |
|
| 115 |
+
body {
|
| 116 |
+
@apply scroll-smooth
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
@layer base {
|
| 120 |
* {
|
| 121 |
@apply border-border outline-ring/50;
|
|
|
|
| 123 |
body {
|
| 124 |
@apply bg-background text-foreground;
|
| 125 |
}
|
| 126 |
+
html {
|
| 127 |
+
@apply scroll-smooth;
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
.background__noisy {
|
| 132 |
+
@apply bg-blend-normal pointer-events-none opacity-90;
|
| 133 |
+
background-size: 25ww auto;
|
| 134 |
+
background-image: url("/deepsite/background_noisy.webp");
|
| 135 |
+
@apply fixed w-screen h-screen -z-1 top-0 left-0;
|
| 136 |
}
|
| 137 |
|
| 138 |
.monaco-editor .margin {
|
| 139 |
+
@apply !bg-neutral-900;
|
| 140 |
}
|
| 141 |
.monaco-editor .monaco-editor-background {
|
| 142 |
+
@apply !bg-neutral-900;
|
| 143 |
+
}
|
| 144 |
+
.monaco-editor .line-numbers {
|
| 145 |
+
@apply !text-neutral-500;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.matched-line {
|
| 149 |
+
@apply bg-sky-500/30;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/* Fast liquid deformation animations */
|
| 153 |
+
@keyframes liquidBlob1 {
|
| 154 |
+
0%, 100% {
|
| 155 |
+
border-radius: 40% 60% 50% 50%;
|
| 156 |
+
transform: scaleX(1) scaleY(1) rotate(0deg);
|
| 157 |
+
}
|
| 158 |
+
12.5% {
|
| 159 |
+
border-radius: 20% 80% 70% 30%;
|
| 160 |
+
transform: scaleX(1.6) scaleY(0.4) rotate(25deg);
|
| 161 |
+
}
|
| 162 |
+
25% {
|
| 163 |
+
border-radius: 80% 20% 30% 70%;
|
| 164 |
+
transform: scaleX(0.5) scaleY(2.1) rotate(-15deg);
|
| 165 |
+
}
|
| 166 |
+
37.5% {
|
| 167 |
+
border-radius: 30% 70% 80% 20%;
|
| 168 |
+
transform: scaleX(1.8) scaleY(0.6) rotate(40deg);
|
| 169 |
+
}
|
| 170 |
+
50% {
|
| 171 |
+
border-radius: 70% 30% 20% 80%;
|
| 172 |
+
transform: scaleX(0.4) scaleY(1.9) rotate(-30deg);
|
| 173 |
+
}
|
| 174 |
+
62.5% {
|
| 175 |
+
border-radius: 25% 75% 60% 40%;
|
| 176 |
+
transform: scaleX(1.5) scaleY(0.7) rotate(55deg);
|
| 177 |
+
}
|
| 178 |
+
75% {
|
| 179 |
+
border-radius: 75% 25% 40% 60%;
|
| 180 |
+
transform: scaleX(0.6) scaleY(1.7) rotate(-10deg);
|
| 181 |
+
}
|
| 182 |
+
87.5% {
|
| 183 |
+
border-radius: 50% 50% 75% 25%;
|
| 184 |
+
transform: scaleX(1.3) scaleY(0.8) rotate(35deg);
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
@keyframes liquidBlob2 {
|
| 189 |
+
0%, 100% {
|
| 190 |
+
border-radius: 60% 40% 50% 50%;
|
| 191 |
+
transform: scaleX(1) scaleY(1) rotate(12deg);
|
| 192 |
+
}
|
| 193 |
+
16% {
|
| 194 |
+
border-radius: 15% 85% 60% 40%;
|
| 195 |
+
transform: scaleX(0.3) scaleY(2.3) rotate(50deg);
|
| 196 |
+
}
|
| 197 |
+
32% {
|
| 198 |
+
border-radius: 85% 15% 25% 75%;
|
| 199 |
+
transform: scaleX(2.0) scaleY(0.5) rotate(-20deg);
|
| 200 |
+
}
|
| 201 |
+
48% {
|
| 202 |
+
border-radius: 30% 70% 85% 15%;
|
| 203 |
+
transform: scaleX(0.4) scaleY(1.8) rotate(70deg);
|
| 204 |
+
}
|
| 205 |
+
64% {
|
| 206 |
+
border-radius: 70% 30% 15% 85%;
|
| 207 |
+
transform: scaleX(1.9) scaleY(0.6) rotate(-35deg);
|
| 208 |
+
}
|
| 209 |
+
80% {
|
| 210 |
+
border-radius: 40% 60% 70% 30%;
|
| 211 |
+
transform: scaleX(0.7) scaleY(1.6) rotate(45deg);
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
@keyframes liquidBlob3 {
|
| 216 |
+
0%, 100% {
|
| 217 |
+
border-radius: 50% 50% 40% 60%;
|
| 218 |
+
transform: scaleX(1) scaleY(1) rotate(0deg);
|
| 219 |
+
}
|
| 220 |
+
20% {
|
| 221 |
+
border-radius: 10% 90% 75% 25%;
|
| 222 |
+
transform: scaleX(2.2) scaleY(0.3) rotate(-45deg);
|
| 223 |
+
}
|
| 224 |
+
40% {
|
| 225 |
+
border-radius: 90% 10% 20% 80%;
|
| 226 |
+
transform: scaleX(0.4) scaleY(2.5) rotate(60deg);
|
| 227 |
+
}
|
| 228 |
+
60% {
|
| 229 |
+
border-radius: 25% 75% 90% 10%;
|
| 230 |
+
transform: scaleX(1.7) scaleY(0.5) rotate(-25deg);
|
| 231 |
+
}
|
| 232 |
+
80% {
|
| 233 |
+
border-radius: 75% 25% 10% 90%;
|
| 234 |
+
transform: scaleX(0.6) scaleY(2.0) rotate(80deg);
|
| 235 |
+
}
|
| 236 |
}
|
| 237 |
+
|
| 238 |
+
@keyframes liquidBlob4 {
|
| 239 |
+
0%, 100% {
|
| 240 |
+
border-radius: 45% 55% 50% 50%;
|
| 241 |
+
transform: scaleX(1) scaleY(1) rotate(-15deg);
|
| 242 |
+
}
|
| 243 |
+
14% {
|
| 244 |
+
border-radius: 90% 10% 65% 35%;
|
| 245 |
+
transform: scaleX(0.2) scaleY(2.8) rotate(35deg);
|
| 246 |
+
}
|
| 247 |
+
28% {
|
| 248 |
+
border-radius: 10% 90% 20% 80%;
|
| 249 |
+
transform: scaleX(2.4) scaleY(0.4) rotate(-50deg);
|
| 250 |
+
}
|
| 251 |
+
42% {
|
| 252 |
+
border-radius: 35% 65% 90% 10%;
|
| 253 |
+
transform: scaleX(0.3) scaleY(2.1) rotate(70deg);
|
| 254 |
+
}
|
| 255 |
+
56% {
|
| 256 |
+
border-radius: 80% 20% 10% 90%;
|
| 257 |
+
transform: scaleX(2.0) scaleY(0.5) rotate(-40deg);
|
| 258 |
+
}
|
| 259 |
+
70% {
|
| 260 |
+
border-radius: 20% 80% 55% 45%;
|
| 261 |
+
transform: scaleX(0.5) scaleY(1.9) rotate(55deg);
|
| 262 |
+
}
|
| 263 |
+
84% {
|
| 264 |
+
border-radius: 65% 35% 80% 20%;
|
| 265 |
+
transform: scaleX(1.6) scaleY(0.6) rotate(-25deg);
|
| 266 |
+
}
|
| 267 |
}
|
| 268 |
+
|
| 269 |
+
/* Fast flowing movement animations */
|
| 270 |
+
@keyframes liquidFlow1 {
|
| 271 |
+
0%, 100% { transform: translate(0, 0); }
|
| 272 |
+
16% { transform: translate(60px, -40px); }
|
| 273 |
+
32% { transform: translate(-45px, -70px); }
|
| 274 |
+
48% { transform: translate(80px, 25px); }
|
| 275 |
+
64% { transform: translate(-30px, 60px); }
|
| 276 |
+
80% { transform: translate(50px, -20px); }
|
| 277 |
}
|
| 278 |
+
|
| 279 |
+
@keyframes liquidFlow2 {
|
| 280 |
+
0%, 100% { transform: translate(0, 0); }
|
| 281 |
+
20% { transform: translate(-70px, 50px); }
|
| 282 |
+
40% { transform: translate(90px, -30px); }
|
| 283 |
+
60% { transform: translate(-40px, -55px); }
|
| 284 |
+
80% { transform: translate(65px, 35px); }
|
| 285 |
}
|
| 286 |
+
|
| 287 |
+
@keyframes liquidFlow3 {
|
| 288 |
+
0%, 100% { transform: translate(0, 0); }
|
| 289 |
+
12% { transform: translate(-50px, -60px); }
|
| 290 |
+
24% { transform: translate(40px, -20px); }
|
| 291 |
+
36% { transform: translate(-30px, 70px); }
|
| 292 |
+
48% { transform: translate(70px, 20px); }
|
| 293 |
+
60% { transform: translate(-60px, -35px); }
|
| 294 |
+
72% { transform: translate(35px, 55px); }
|
| 295 |
+
84% { transform: translate(-25px, -45px); }
|
| 296 |
}
|
| 297 |
|
| 298 |
+
@keyframes liquidFlow4 {
|
| 299 |
+
0%, 100% { transform: translate(0, 0); }
|
| 300 |
+
14% { transform: translate(50px, 60px); }
|
| 301 |
+
28% { transform: translate(-80px, -40px); }
|
| 302 |
+
42% { transform: translate(30px, -90px); }
|
| 303 |
+
56% { transform: translate(-55px, 45px); }
|
| 304 |
+
70% { transform: translate(75px, -25px); }
|
| 305 |
+
84% { transform: translate(-35px, 65px); }
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
/* Light sweep animation for buttons */
|
| 309 |
+
@keyframes lightSweep {
|
| 310 |
+
0% {
|
| 311 |
+
transform: translateX(-150%);
|
| 312 |
+
opacity: 0;
|
| 313 |
+
}
|
| 314 |
+
8% {
|
| 315 |
+
opacity: 0.3;
|
| 316 |
+
}
|
| 317 |
+
25% {
|
| 318 |
+
opacity: 0.8;
|
| 319 |
+
}
|
| 320 |
+
42% {
|
| 321 |
+
opacity: 0.3;
|
| 322 |
+
}
|
| 323 |
+
50% {
|
| 324 |
+
transform: translateX(150%);
|
| 325 |
+
opacity: 0;
|
| 326 |
+
}
|
| 327 |
+
58% {
|
| 328 |
+
opacity: 0.3;
|
| 329 |
+
}
|
| 330 |
+
75% {
|
| 331 |
+
opacity: 0.8;
|
| 332 |
+
}
|
| 333 |
+
92% {
|
| 334 |
+
opacity: 0.3;
|
| 335 |
+
}
|
| 336 |
+
100% {
|
| 337 |
+
transform: translateX(-150%);
|
| 338 |
+
opacity: 0;
|
| 339 |
+
}
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.light-sweep {
|
| 343 |
+
position: relative;
|
| 344 |
+
overflow: hidden;
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
.light-sweep::before {
|
| 348 |
+
content: '';
|
| 349 |
position: absolute;
|
| 350 |
+
top: 0;
|
| 351 |
+
left: 0;
|
| 352 |
+
right: 0;
|
| 353 |
+
bottom: 0;
|
| 354 |
+
width: 300%;
|
| 355 |
+
background: linear-gradient(
|
| 356 |
+
90deg,
|
| 357 |
+
transparent 0%,
|
| 358 |
+
transparent 20%,
|
| 359 |
+
rgba(56, 189, 248, 0.1) 35%,
|
| 360 |
+
rgba(56, 189, 248, 0.2) 45%,
|
| 361 |
+
rgba(255, 255, 255, 0.2) 50%,
|
| 362 |
+
rgba(168, 85, 247, 0.2) 55%,
|
| 363 |
+
rgba(168, 85, 247, 0.1) 65%,
|
| 364 |
+
transparent 80%,
|
| 365 |
+
transparent 100%
|
| 366 |
+
);
|
| 367 |
+
animation: lightSweep 7s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
| 368 |
pointer-events: none;
|
| 369 |
+
z-index: 1;
|
| 370 |
+
filter: blur(1px);
|
| 371 |
}
|
| 372 |
|
| 373 |
+
.transparent-scroll {
|
| 374 |
+
scrollbar-width: none; /* Firefox */
|
| 375 |
+
-ms-overflow-style: none; /* IE and Edge */
|
|
|
|
|
|
|
| 376 |
}
|
| 377 |
|
| 378 |
+
.transparent-scroll::-webkit-scrollbar {
|
| 379 |
+
display: none; /* Chrome, Safari, Opera */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
}
|
assets/hf-logo.svg
DELETED
assets/logo.svg
ADDED
|
|
assets/pro.svg
DELETED
chart/Chart.yaml
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
apiVersion: v2
|
| 2 |
-
name: deepsite
|
| 3 |
-
version: 0.0.0-latest
|
| 4 |
-
type: application
|
| 5 |
-
icon: https://huggingface.co/front/assets/huggingface_logo-noborder.svg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/env/prod.yaml
DELETED
|
@@ -1,59 +0,0 @@
|
|
| 1 |
-
nodeSelector:
|
| 2 |
-
role-deepsite: "true"
|
| 3 |
-
|
| 4 |
-
tolerations:
|
| 5 |
-
- key: "huggingface.co/deepsite"
|
| 6 |
-
operator: "Equal"
|
| 7 |
-
value: "true"
|
| 8 |
-
effect: "NoSchedule"
|
| 9 |
-
|
| 10 |
-
serviceAccount:
|
| 11 |
-
enabled: true
|
| 12 |
-
create: true
|
| 13 |
-
name: deepsite-prod
|
| 14 |
-
|
| 15 |
-
ingress:
|
| 16 |
-
path: "/"
|
| 17 |
-
annotations:
|
| 18 |
-
alb.ingress.kubernetes.io/healthcheck-path: "/api/healthcheck"
|
| 19 |
-
alb.ingress.kubernetes.io/listen-ports: "[{\"HTTP\": 80}, {\"HTTPS\": 443}]"
|
| 20 |
-
alb.ingress.kubernetes.io/load-balancer-name: "hub-utils-prod-cloudfront"
|
| 21 |
-
alb.ingress.kubernetes.io/group.name: "hub-utils-prod-cloudfront"
|
| 22 |
-
alb.ingress.kubernetes.io/scheme: "internal"
|
| 23 |
-
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
| 24 |
-
alb.ingress.kubernetes.io/tags: "Env=prod,Project=hub,Terraform=true"
|
| 25 |
-
alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=30
|
| 26 |
-
alb.ingress.kubernetes.io/target-type: "ip"
|
| 27 |
-
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"
|
| 28 |
-
kubernetes.io/ingress.class: "alb"
|
| 29 |
-
|
| 30 |
-
networkPolicy:
|
| 31 |
-
enabled: true
|
| 32 |
-
allowedBlocks:
|
| 33 |
-
- 10.0.0.0/16
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
ingressInternal:
|
| 37 |
-
enabled: false
|
| 38 |
-
|
| 39 |
-
envVars:
|
| 40 |
-
NEXTAUTH_URL: https://deepsite.hf.co/api/auth
|
| 41 |
-
|
| 42 |
-
infisical:
|
| 43 |
-
enabled: true
|
| 44 |
-
env: "prod-us-east-1"
|
| 45 |
-
|
| 46 |
-
autoscaling:
|
| 47 |
-
enabled: true
|
| 48 |
-
minReplicas: 1
|
| 49 |
-
maxReplicas: 10
|
| 50 |
-
targetMemoryUtilizationPercentage: "50"
|
| 51 |
-
targetCPUUtilizationPercentage: "50"
|
| 52 |
-
|
| 53 |
-
resources:
|
| 54 |
-
requests:
|
| 55 |
-
cpu: 2
|
| 56 |
-
memory: 4Gi
|
| 57 |
-
limits:
|
| 58 |
-
cpu: 4
|
| 59 |
-
memory: 8Gi
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/_helpers.tpl
DELETED
|
@@ -1,22 +0,0 @@
|
|
| 1 |
-
{{- define "name" -}}
|
| 2 |
-
{{- default $.Release.Name | trunc 63 | trimSuffix "-" -}}
|
| 3 |
-
{{- end -}}
|
| 4 |
-
|
| 5 |
-
{{- define "app.name" -}}
|
| 6 |
-
chat-ui
|
| 7 |
-
{{- end -}}
|
| 8 |
-
|
| 9 |
-
{{- define "labels.standard" -}}
|
| 10 |
-
release: {{ $.Release.Name | quote }}
|
| 11 |
-
heritage: {{ $.Release.Service | quote }}
|
| 12 |
-
chart: "{{ include "name" . }}"
|
| 13 |
-
app: "{{ include "app.name" . }}"
|
| 14 |
-
{{- end -}}
|
| 15 |
-
|
| 16 |
-
{{- define "labels.resolver" -}}
|
| 17 |
-
release: {{ $.Release.Name | quote }}
|
| 18 |
-
heritage: {{ $.Release.Service | quote }}
|
| 19 |
-
chart: "{{ include "name" . }}"
|
| 20 |
-
app: "{{ include "app.name" . }}-resolver"
|
| 21 |
-
{{- end -}}
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/config.yaml
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
apiVersion: v1
|
| 2 |
-
kind: ConfigMap
|
| 3 |
-
metadata:
|
| 4 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 5 |
-
name: {{ include "name" . }}
|
| 6 |
-
namespace: {{ .Release.Namespace }}
|
| 7 |
-
data:
|
| 8 |
-
{{- range $key, $value := $.Values.envVars }}
|
| 9 |
-
{{ $key }}: {{ $value | quote }}
|
| 10 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/deployment.yaml
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
apiVersion: apps/v1
|
| 2 |
-
kind: Deployment
|
| 3 |
-
metadata:
|
| 4 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 5 |
-
name: {{ include "name" . }}
|
| 6 |
-
namespace: {{ .Release.Namespace }}
|
| 7 |
-
{{- if .Values.infisical.enabled }}
|
| 8 |
-
annotations:
|
| 9 |
-
secrets.infisical.com/auto-reload: "true"
|
| 10 |
-
{{- end }}
|
| 11 |
-
spec:
|
| 12 |
-
progressDeadlineSeconds: 600
|
| 13 |
-
{{- if not $.Values.autoscaling.enabled }}
|
| 14 |
-
replicas: {{ .Values.replicas }}
|
| 15 |
-
{{- end }}
|
| 16 |
-
revisionHistoryLimit: 10
|
| 17 |
-
selector:
|
| 18 |
-
matchLabels: {{ include "labels.standard" . | nindent 6 }}
|
| 19 |
-
strategy:
|
| 20 |
-
rollingUpdate:
|
| 21 |
-
maxSurge: 25%
|
| 22 |
-
maxUnavailable: 25%
|
| 23 |
-
type: RollingUpdate
|
| 24 |
-
template:
|
| 25 |
-
metadata:
|
| 26 |
-
labels: {{ include "labels.standard" . | nindent 8 }}
|
| 27 |
-
annotations:
|
| 28 |
-
checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}
|
| 29 |
-
{{- if $.Values.envVars.NODE_LOG_STRUCTURED_DATA }}
|
| 30 |
-
co.elastic.logs/json.expand_keys: "true"
|
| 31 |
-
{{- end }}
|
| 32 |
-
spec:
|
| 33 |
-
{{- if .Values.serviceAccount.enabled }}
|
| 34 |
-
serviceAccountName: "{{ .Values.serviceAccount.name | default (include "name" .) }}"
|
| 35 |
-
{{- end }}
|
| 36 |
-
containers:
|
| 37 |
-
- name: chat-ui
|
| 38 |
-
image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
|
| 39 |
-
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
| 40 |
-
readinessProbe:
|
| 41 |
-
failureThreshold: 30
|
| 42 |
-
periodSeconds: 10
|
| 43 |
-
httpGet:
|
| 44 |
-
path: {{ $.Values.envVars.APP_BASE | default "" }}/api/healthcheck
|
| 45 |
-
port: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
|
| 46 |
-
livenessProbe:
|
| 47 |
-
failureThreshold: 30
|
| 48 |
-
periodSeconds: 10
|
| 49 |
-
httpGet:
|
| 50 |
-
path: {{ $.Values.envVars.APP_BASE | default "" }}/api/healthcheck
|
| 51 |
-
port: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
|
| 52 |
-
ports:
|
| 53 |
-
- containerPort: {{ $.Values.envVars.APP_PORT | default 3001 | int }}
|
| 54 |
-
name: http
|
| 55 |
-
protocol: TCP
|
| 56 |
-
{{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
|
| 57 |
-
- containerPort: {{ $.Values.envVars.METRICS_PORT | default 5565 | int }}
|
| 58 |
-
name: metrics
|
| 59 |
-
protocol: TCP
|
| 60 |
-
{{- end }}
|
| 61 |
-
resources: {{ toYaml .Values.resources | nindent 12 }}
|
| 62 |
-
{{- with $.Values.extraEnv }}
|
| 63 |
-
env:
|
| 64 |
-
{{- toYaml . | nindent 14 }}
|
| 65 |
-
{{- end }}
|
| 66 |
-
envFrom:
|
| 67 |
-
- configMapRef:
|
| 68 |
-
name: {{ include "name" . }}
|
| 69 |
-
{{- if $.Values.infisical.enabled }}
|
| 70 |
-
- secretRef:
|
| 71 |
-
name: {{ include "name" $ }}-secs
|
| 72 |
-
{{- end }}
|
| 73 |
-
{{- with $.Values.extraEnvFrom }}
|
| 74 |
-
{{- toYaml . | nindent 14 }}
|
| 75 |
-
{{- end }}
|
| 76 |
-
nodeSelector: {{ toYaml .Values.nodeSelector | nindent 8 }}
|
| 77 |
-
tolerations: {{ toYaml .Values.tolerations | nindent 8 }}
|
| 78 |
-
volumes:
|
| 79 |
-
- name: config
|
| 80 |
-
configMap:
|
| 81 |
-
name: {{ include "name" . }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/hpa.yaml
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
{{- if $.Values.autoscaling.enabled }}
|
| 2 |
-
apiVersion: autoscaling/v2
|
| 3 |
-
kind: HorizontalPodAutoscaler
|
| 4 |
-
metadata:
|
| 5 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 6 |
-
name: {{ include "name" . }}
|
| 7 |
-
namespace: {{ .Release.Namespace }}
|
| 8 |
-
spec:
|
| 9 |
-
scaleTargetRef:
|
| 10 |
-
apiVersion: apps/v1
|
| 11 |
-
kind: Deployment
|
| 12 |
-
name: {{ include "name" . }}
|
| 13 |
-
minReplicas: {{ $.Values.autoscaling.minReplicas }}
|
| 14 |
-
maxReplicas: {{ $.Values.autoscaling.maxReplicas }}
|
| 15 |
-
metrics:
|
| 16 |
-
{{- if ne "" $.Values.autoscaling.targetMemoryUtilizationPercentage }}
|
| 17 |
-
- type: Resource
|
| 18 |
-
resource:
|
| 19 |
-
name: memory
|
| 20 |
-
target:
|
| 21 |
-
type: Utilization
|
| 22 |
-
averageUtilization: {{ $.Values.autoscaling.targetMemoryUtilizationPercentage | int }}
|
| 23 |
-
{{- end }}
|
| 24 |
-
{{- if ne "" $.Values.autoscaling.targetCPUUtilizationPercentage }}
|
| 25 |
-
- type: Resource
|
| 26 |
-
resource:
|
| 27 |
-
name: cpu
|
| 28 |
-
target:
|
| 29 |
-
type: Utilization
|
| 30 |
-
averageUtilization: {{ $.Values.autoscaling.targetCPUUtilizationPercentage | int }}
|
| 31 |
-
{{- end }}
|
| 32 |
-
behavior:
|
| 33 |
-
scaleDown:
|
| 34 |
-
stabilizationWindowSeconds: 600
|
| 35 |
-
policies:
|
| 36 |
-
- type: Percent
|
| 37 |
-
value: 10
|
| 38 |
-
periodSeconds: 60
|
| 39 |
-
scaleUp:
|
| 40 |
-
stabilizationWindowSeconds: 0
|
| 41 |
-
policies:
|
| 42 |
-
- type: Pods
|
| 43 |
-
value: 1
|
| 44 |
-
periodSeconds: 30
|
| 45 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/infisical.yaml
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
{{- if .Values.infisical.enabled }}
|
| 2 |
-
apiVersion: secrets.infisical.com/v1alpha1
|
| 3 |
-
kind: InfisicalSecret
|
| 4 |
-
metadata:
|
| 5 |
-
name: {{ include "name" $ }}-infisical-secret
|
| 6 |
-
namespace: {{ $.Release.Namespace }}
|
| 7 |
-
spec:
|
| 8 |
-
authentication:
|
| 9 |
-
universalAuth:
|
| 10 |
-
credentialsRef:
|
| 11 |
-
secretName: {{ .Values.infisical.operatorSecretName | quote }}
|
| 12 |
-
secretNamespace: {{ .Values.infisical.operatorSecretNamespace | quote }}
|
| 13 |
-
secretsScope:
|
| 14 |
-
envSlug: {{ .Values.infisical.env | quote }}
|
| 15 |
-
projectSlug: {{ .Values.infisical.project | quote }}
|
| 16 |
-
secretsPath: /
|
| 17 |
-
hostAPI: {{ .Values.infisical.url | quote }}
|
| 18 |
-
managedSecretReference:
|
| 19 |
-
creationPolicy: Owner
|
| 20 |
-
secretName: {{ include "name" $ }}-secs
|
| 21 |
-
secretNamespace: {{ .Release.Namespace | quote }}
|
| 22 |
-
secretType: Opaque
|
| 23 |
-
resyncInterval: {{ .Values.infisical.resyncInterval }}
|
| 24 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/ingress-internal.yaml
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
{{- if $.Values.ingressInternal.enabled }}
|
| 2 |
-
apiVersion: networking.k8s.io/v1
|
| 3 |
-
kind: Ingress
|
| 4 |
-
metadata:
|
| 5 |
-
annotations: {{ toYaml .Values.ingressInternal.annotations | nindent 4 }}
|
| 6 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 7 |
-
name: {{ include "name" . }}-internal
|
| 8 |
-
namespace: {{ .Release.Namespace }}
|
| 9 |
-
spec:
|
| 10 |
-
{{ if $.Values.ingressInternal.className }}
|
| 11 |
-
ingressClassName: {{ .Values.ingressInternal.className }}
|
| 12 |
-
{{ end }}
|
| 13 |
-
{{- with .Values.ingressInternal.tls }}
|
| 14 |
-
tls:
|
| 15 |
-
- hosts:
|
| 16 |
-
- {{ $.Values.domain | quote }}
|
| 17 |
-
{{- with .secretName }}
|
| 18 |
-
secretName: {{ . }}
|
| 19 |
-
{{- end }}
|
| 20 |
-
{{- end }}
|
| 21 |
-
rules:
|
| 22 |
-
- host: {{ .Values.domain }}
|
| 23 |
-
http:
|
| 24 |
-
paths:
|
| 25 |
-
- backend:
|
| 26 |
-
service:
|
| 27 |
-
name: {{ include "name" . }}
|
| 28 |
-
port:
|
| 29 |
-
name: http
|
| 30 |
-
path: {{ $.Values.ingressInternal.path | default "/" }}
|
| 31 |
-
pathType: Prefix
|
| 32 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/ingress.yaml
DELETED
|
@@ -1,33 +0,0 @@
|
|
| 1 |
-
{{- if $.Values.ingress.enabled }}
|
| 2 |
-
apiVersion: networking.k8s.io/v1
|
| 3 |
-
kind: Ingress
|
| 4 |
-
metadata:
|
| 5 |
-
annotations: {{ toYaml .Values.ingress.annotations | nindent 4 }}
|
| 6 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 7 |
-
name: {{ include "name" . }}
|
| 8 |
-
namespace: {{ .Release.Namespace }}
|
| 9 |
-
spec:
|
| 10 |
-
{{ if $.Values.ingress.className }}
|
| 11 |
-
ingressClassName: {{ .Values.ingress.className }}
|
| 12 |
-
{{ end }}
|
| 13 |
-
{{- with .Values.ingress.tls }}
|
| 14 |
-
tls:
|
| 15 |
-
- hosts:
|
| 16 |
-
- {{ $.Values.domain | quote }}
|
| 17 |
-
{{- with .secretName }}
|
| 18 |
-
secretName: {{ . }}
|
| 19 |
-
{{- end }}
|
| 20 |
-
{{- end }}
|
| 21 |
-
rules:
|
| 22 |
-
- host: {{ .Values.domain }}
|
| 23 |
-
http:
|
| 24 |
-
paths:
|
| 25 |
-
- backend:
|
| 26 |
-
service:
|
| 27 |
-
name: {{ include "name" . }}
|
| 28 |
-
port:
|
| 29 |
-
name: http
|
| 30 |
-
path: {{ $.Values.ingress.path | default "/" }}
|
| 31 |
-
pathType: Prefix
|
| 32 |
-
|
| 33 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/network-policy.yaml
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
{{- if $.Values.networkPolicy.enabled }}
|
| 2 |
-
apiVersion: networking.k8s.io/v1
|
| 3 |
-
kind: NetworkPolicy
|
| 4 |
-
metadata:
|
| 5 |
-
name: {{ include "name" . }}
|
| 6 |
-
namespace: {{ .Release.Namespace }}
|
| 7 |
-
spec:
|
| 8 |
-
egress:
|
| 9 |
-
- ports:
|
| 10 |
-
- port: 53
|
| 11 |
-
protocol: UDP
|
| 12 |
-
to:
|
| 13 |
-
- namespaceSelector:
|
| 14 |
-
matchLabels:
|
| 15 |
-
kubernetes.io/metadata.name: kube-system
|
| 16 |
-
podSelector:
|
| 17 |
-
matchLabels:
|
| 18 |
-
k8s-app: kube-dns
|
| 19 |
-
- to:
|
| 20 |
-
{{- range $ip := .Values.networkPolicy.allowedBlocks }}
|
| 21 |
-
- ipBlock:
|
| 22 |
-
cidr: {{ $ip | quote }}
|
| 23 |
-
{{- end }}
|
| 24 |
-
- to:
|
| 25 |
-
- ipBlock:
|
| 26 |
-
cidr: 0.0.0.0/0
|
| 27 |
-
except:
|
| 28 |
-
- 10.0.0.0/8
|
| 29 |
-
- 172.16.0.0/12
|
| 30 |
-
- 192.168.0.0/16
|
| 31 |
-
- 169.254.169.254/32
|
| 32 |
-
podSelector:
|
| 33 |
-
matchLabels: {{ include "labels.standard" . | nindent 6 }}
|
| 34 |
-
policyTypes:
|
| 35 |
-
- Egress
|
| 36 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/service-account.yaml
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
{{- if and .Values.serviceAccount.enabled .Values.serviceAccount.create }}
|
| 2 |
-
apiVersion: v1
|
| 3 |
-
kind: ServiceAccount
|
| 4 |
-
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
|
| 5 |
-
metadata:
|
| 6 |
-
name: "{{ .Values.serviceAccount.name | default (include "name" .) }}"
|
| 7 |
-
namespace: {{ .Release.Namespace }}
|
| 8 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 9 |
-
{{- with .Values.serviceAccount.annotations }}
|
| 10 |
-
annotations:
|
| 11 |
-
{{- toYaml . | nindent 4 }}
|
| 12 |
-
{{- end }}
|
| 13 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/service-monitor.yaml
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
{{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
|
| 2 |
-
apiVersion: monitoring.coreos.com/v1
|
| 3 |
-
kind: ServiceMonitor
|
| 4 |
-
metadata:
|
| 5 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 6 |
-
name: {{ include "name" . }}
|
| 7 |
-
namespace: {{ .Release.Namespace }}
|
| 8 |
-
spec:
|
| 9 |
-
selector:
|
| 10 |
-
matchLabels: {{ include "labels.standard" . | nindent 6 }}
|
| 11 |
-
endpoints:
|
| 12 |
-
- port: metrics
|
| 13 |
-
path: /metrics
|
| 14 |
-
interval: 10s
|
| 15 |
-
scheme: http
|
| 16 |
-
scrapeTimeout: 10s
|
| 17 |
-
{{- end }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/templates/service.yaml
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
apiVersion: v1
|
| 2 |
-
kind: Service
|
| 3 |
-
metadata:
|
| 4 |
-
name: "{{ include "name" . }}"
|
| 5 |
-
annotations: {{ toYaml .Values.service.annotations | nindent 4 }}
|
| 6 |
-
namespace: {{ .Release.Namespace }}
|
| 7 |
-
labels: {{ include "labels.standard" . | nindent 4 }}
|
| 8 |
-
spec:
|
| 9 |
-
ports:
|
| 10 |
-
- name: http
|
| 11 |
-
port: 80
|
| 12 |
-
protocol: TCP
|
| 13 |
-
targetPort: http
|
| 14 |
-
{{- if eq "true" $.Values.envVars.METRICS_ENABLED }}
|
| 15 |
-
- name: metrics
|
| 16 |
-
port: {{ $.Values.envVars.METRICS_PORT | default 5565 | int }}
|
| 17 |
-
protocol: TCP
|
| 18 |
-
targetPort: metrics
|
| 19 |
-
{{- end }}
|
| 20 |
-
selector: {{ include "labels.standard" . | nindent 4 }}
|
| 21 |
-
type: {{.Values.service.type}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart/values.yaml
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
image:
|
| 2 |
-
repository: registry.internal.huggingface.tech/deepsite
|
| 3 |
-
name: deepsite
|
| 4 |
-
tag: 0.0.0-latest
|
| 5 |
-
pullPolicy: IfNotPresent
|
| 6 |
-
|
| 7 |
-
replicas: 1
|
| 8 |
-
|
| 9 |
-
domain: deepsite.hf.co
|
| 10 |
-
|
| 11 |
-
networkPolicy:
|
| 12 |
-
enabled: true
|
| 13 |
-
allowedBlocks: []
|
| 14 |
-
# allowedBlocks:
|
| 15 |
-
# - 10.0.240.0/24
|
| 16 |
-
# - 10.0.241.0/24
|
| 17 |
-
# - 10.0.242.0/24
|
| 18 |
-
# - 10.0.243.0/24
|
| 19 |
-
# - 10.0.244.0/24
|
| 20 |
-
# - 10.240.0.0/24
|
| 21 |
-
# - 10.16.0.0/16
|
| 22 |
-
|
| 23 |
-
service:
|
| 24 |
-
type: NodePort
|
| 25 |
-
annotations: { }
|
| 26 |
-
|
| 27 |
-
serviceAccount:
|
| 28 |
-
enabled: false
|
| 29 |
-
create: false
|
| 30 |
-
name: ""
|
| 31 |
-
automountServiceAccountToken: true
|
| 32 |
-
annotations: { }
|
| 33 |
-
|
| 34 |
-
ingress:
|
| 35 |
-
enabled: true
|
| 36 |
-
path: "/"
|
| 37 |
-
annotations: { }
|
| 38 |
-
# className: "nginx"
|
| 39 |
-
tls: { }
|
| 40 |
-
# secretName: XXX
|
| 41 |
-
|
| 42 |
-
ingressInternal:
|
| 43 |
-
enabled: false
|
| 44 |
-
path: "/"
|
| 45 |
-
annotations: { }
|
| 46 |
-
# className: "nginx"
|
| 47 |
-
tls: { }
|
| 48 |
-
|
| 49 |
-
resources:
|
| 50 |
-
requests:
|
| 51 |
-
cpu: 2
|
| 52 |
-
memory: 4Gi
|
| 53 |
-
limits:
|
| 54 |
-
cpu: 2
|
| 55 |
-
memory: 4Gi
|
| 56 |
-
nodeSelector: {}
|
| 57 |
-
tolerations: []
|
| 58 |
-
|
| 59 |
-
envVars: { }
|
| 60 |
-
|
| 61 |
-
infisical:
|
| 62 |
-
enabled: false
|
| 63 |
-
env: ""
|
| 64 |
-
project: "deepsite-f-hvj"
|
| 65 |
-
url: ""
|
| 66 |
-
resyncInterval: 60
|
| 67 |
-
operatorSecretName: "deepsite-operator-secrets"
|
| 68 |
-
operatorSecretNamespace: "hub-utils"
|
| 69 |
-
|
| 70 |
-
# Allow to environment injections on top or instead of infisical
|
| 71 |
-
extraEnvFrom: []
|
| 72 |
-
extraEnv: []
|
| 73 |
-
|
| 74 |
-
autoscaling:
|
| 75 |
-
enabled: false
|
| 76 |
-
minReplicas: 1
|
| 77 |
-
maxReplicas: 2
|
| 78 |
-
targetMemoryUtilizationPercentage: ""
|
| 79 |
-
targetCPUUtilizationPercentage: ""
|
| 80 |
-
|
| 81 |
-
## Metrics removed; monitoring configuration no longer used
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components.json
CHANGED
|
@@ -5,12 +5,11 @@
|
|
| 5 |
"tsx": true,
|
| 6 |
"tailwind": {
|
| 7 |
"config": "",
|
| 8 |
-
"css": "
|
| 9 |
-
"baseColor": "
|
| 10 |
"cssVariables": true,
|
| 11 |
"prefix": ""
|
| 12 |
},
|
| 13 |
-
"iconLibrary": "lucide",
|
| 14 |
"aliases": {
|
| 15 |
"components": "@/components",
|
| 16 |
"utils": "@/lib/utils",
|
|
@@ -18,5 +17,5 @@
|
|
| 18 |
"lib": "@/lib",
|
| 19 |
"hooks": "@/hooks"
|
| 20 |
},
|
| 21 |
-
"
|
| 22 |
-
}
|
|
|
|
| 5 |
"tsx": true,
|
| 6 |
"tailwind": {
|
| 7 |
"config": "",
|
| 8 |
+
"css": "assets/globals.css",
|
| 9 |
+
"baseColor": "neutral",
|
| 10 |
"cssVariables": true,
|
| 11 |
"prefix": ""
|
| 12 |
},
|
|
|
|
| 13 |
"aliases": {
|
| 14 |
"components": "@/components",
|
| 15 |
"utils": "@/lib/utils",
|
|
|
|
| 17 |
"lib": "@/lib",
|
| 18 |
"hooks": "@/hooks"
|
| 19 |
},
|
| 20 |
+
"iconLibrary": "lucide"
|
| 21 |
+
}
|
components/animated-blobs/index.tsx
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function AnimatedBlobs() {
|
| 2 |
+
return (
|
| 3 |
+
<div className="absolute inset-0 pointer-events-none -z-[1]">
|
| 4 |
+
<div
|
| 5 |
+
className="w-full h-full bg-gradient-to-r from-purple-500 to-pink-500 opacity-10 blur-3xl"
|
| 6 |
+
style={{
|
| 7 |
+
animation:
|
| 8 |
+
"liquidBlob1 4s ease-in-out infinite, liquidFlow1 6s ease-in-out infinite",
|
| 9 |
+
}}
|
| 10 |
+
/>
|
| 11 |
+
<div
|
| 12 |
+
className="w-2/3 h-3/4 bg-gradient-to-r from-blue-500 to-teal-500 opacity-24 blur-3xl absolute -top-20 right-10"
|
| 13 |
+
style={{
|
| 14 |
+
animation:
|
| 15 |
+
"liquidBlob2 5s ease-in-out infinite, liquidFlow2 7s ease-in-out infinite",
|
| 16 |
+
}}
|
| 17 |
+
/>
|
| 18 |
+
<div
|
| 19 |
+
className="w-1/2 h-1/2 bg-gradient-to-r from-amber-500 to-rose-500 opacity-20 blur-3xl absolute bottom-0 left-10"
|
| 20 |
+
style={{
|
| 21 |
+
animation:
|
| 22 |
+
"liquidBlob3 3.5s ease-in-out infinite, liquidFlow3 8s ease-in-out infinite",
|
| 23 |
+
}}
|
| 24 |
+
/>
|
| 25 |
+
<div
|
| 26 |
+
className="w-48 h-48 bg-gradient-to-r from-cyan-500 to-indigo-500 opacity-20 blur-3xl absolute top-1/3 right-1/3"
|
| 27 |
+
style={{
|
| 28 |
+
animation:
|
| 29 |
+
"liquidBlob4 4.5s ease-in-out infinite, liquidFlow4 6.5s ease-in-out infinite",
|
| 30 |
+
}}
|
| 31 |
+
/>
|
| 32 |
+
</div>
|
| 33 |
+
);
|
| 34 |
+
}
|
components/animated-text/index.tsx
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
|
| 3 |
+
import { useState, useEffect } from "react";
|
| 4 |
+
|
| 5 |
+
interface AnimatedTextProps {
|
| 6 |
+
className?: string;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
export function AnimatedText({ className = "" }: AnimatedTextProps) {
|
| 10 |
+
const [displayText, setDisplayText] = useState("");
|
| 11 |
+
const [currentSuggestionIndex, setCurrentSuggestionIndex] = useState(0);
|
| 12 |
+
const [isTyping, setIsTyping] = useState(true);
|
| 13 |
+
const [showCursor, setShowCursor] = useState(true);
|
| 14 |
+
const [lastTypedIndex, setLastTypedIndex] = useState(-1);
|
| 15 |
+
const [animationComplete, setAnimationComplete] = useState(false);
|
| 16 |
+
|
| 17 |
+
// Randomize suggestions on each component mount
|
| 18 |
+
const [suggestions] = useState(() => {
|
| 19 |
+
const baseSuggestions = [
|
| 20 |
+
"create a stunning portfolio!",
|
| 21 |
+
"build a tic tac toe game!",
|
| 22 |
+
"design a website for my restaurant!",
|
| 23 |
+
"make a sleek landing page!",
|
| 24 |
+
"build an e-commerce store!",
|
| 25 |
+
"create a personal blog!",
|
| 26 |
+
"develop a modern dashboard!",
|
| 27 |
+
"design a company website!",
|
| 28 |
+
"build a todo app!",
|
| 29 |
+
"create an online gallery!",
|
| 30 |
+
"make a contact form!",
|
| 31 |
+
"build a weather app!",
|
| 32 |
+
];
|
| 33 |
+
|
| 34 |
+
// Fisher-Yates shuffle algorithm
|
| 35 |
+
const shuffled = [...baseSuggestions];
|
| 36 |
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
| 37 |
+
const j = Math.floor(Math.random() * (i + 1));
|
| 38 |
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
return shuffled;
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
useEffect(() => {
|
| 45 |
+
if (animationComplete) return;
|
| 46 |
+
|
| 47 |
+
let timeout: NodeJS.Timeout;
|
| 48 |
+
|
| 49 |
+
const typeText = () => {
|
| 50 |
+
const currentSuggestion = suggestions[currentSuggestionIndex];
|
| 51 |
+
|
| 52 |
+
if (isTyping) {
|
| 53 |
+
if (displayText.length < currentSuggestion.length) {
|
| 54 |
+
setDisplayText(currentSuggestion.slice(0, displayText.length + 1));
|
| 55 |
+
setLastTypedIndex(displayText.length);
|
| 56 |
+
timeout = setTimeout(typeText, 80);
|
| 57 |
+
} else {
|
| 58 |
+
// Finished typing, wait then start erasing
|
| 59 |
+
setLastTypedIndex(-1);
|
| 60 |
+
timeout = setTimeout(() => {
|
| 61 |
+
setIsTyping(false);
|
| 62 |
+
}, 2000);
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
timeout = setTimeout(typeText, 100);
|
| 68 |
+
return () => clearTimeout(timeout);
|
| 69 |
+
}, [
|
| 70 |
+
displayText,
|
| 71 |
+
currentSuggestionIndex,
|
| 72 |
+
isTyping,
|
| 73 |
+
suggestions,
|
| 74 |
+
animationComplete,
|
| 75 |
+
]);
|
| 76 |
+
|
| 77 |
+
// Cursor blinking effect
|
| 78 |
+
useEffect(() => {
|
| 79 |
+
if (animationComplete) {
|
| 80 |
+
setShowCursor(false);
|
| 81 |
+
return;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
const cursorInterval = setInterval(() => {
|
| 85 |
+
setShowCursor((prev) => !prev);
|
| 86 |
+
}, 600);
|
| 87 |
+
|
| 88 |
+
return () => clearInterval(cursorInterval);
|
| 89 |
+
}, [animationComplete]);
|
| 90 |
+
|
| 91 |
+
useEffect(() => {
|
| 92 |
+
if (lastTypedIndex >= 0) {
|
| 93 |
+
const timeout = setTimeout(() => {
|
| 94 |
+
setLastTypedIndex(-1);
|
| 95 |
+
}, 400);
|
| 96 |
+
|
| 97 |
+
return () => clearTimeout(timeout);
|
| 98 |
+
}
|
| 99 |
+
}, [lastTypedIndex]);
|
| 100 |
+
|
| 101 |
+
return (
|
| 102 |
+
<p className={`font-mono ${className}`}>
|
| 103 |
+
Hey DeepSite,
|
| 104 |
+
{displayText.split("").map((char, index) => (
|
| 105 |
+
<span
|
| 106 |
+
key={`${currentSuggestionIndex}-${index}`}
|
| 107 |
+
className={`transition-colors duration-300 ${
|
| 108 |
+
index === lastTypedIndex ? "text-neutral-100" : ""
|
| 109 |
+
}`}
|
| 110 |
+
>
|
| 111 |
+
{char}
|
| 112 |
+
</span>
|
| 113 |
+
))}
|
| 114 |
+
<span
|
| 115 |
+
className={`${
|
| 116 |
+
showCursor ? "opacity-100" : "opacity-0"
|
| 117 |
+
} transition-opacity`}
|
| 118 |
+
>
|
| 119 |
+
|
|
| 120 |
+
</span>
|
| 121 |
+
</p>
|
| 122 |
+
);
|
| 123 |
+
}
|
components/ask-ai/ask-ai-landing.tsx
DELETED
|
@@ -1,75 +0,0 @@
|
|
| 1 |
-
"use client";
|
| 2 |
-
import { ArrowUp } from "lucide-react";
|
| 3 |
-
import { useState } from "react";
|
| 4 |
-
import { useLocalStorage, useMount } from "react-use";
|
| 5 |
-
import { useRouter } from "next/navigation";
|
| 6 |
-
|
| 7 |
-
import { Button } from "@/components/ui/button";
|
| 8 |
-
import { ProviderType } from "@/lib/type";
|
| 9 |
-
import { Models } from "./models";
|
| 10 |
-
import { DEFAULT_MODEL } from "@/lib/providers";
|
| 11 |
-
import { cn } from "@/lib/utils";
|
| 12 |
-
|
| 13 |
-
export function AskAiLanding({ className }: { className?: string }) {
|
| 14 |
-
const [model = DEFAULT_MODEL, setModel] = useLocalStorage<string>(
|
| 15 |
-
"deepsite-model",
|
| 16 |
-
DEFAULT_MODEL
|
| 17 |
-
);
|
| 18 |
-
const [provider, setProvider] = useLocalStorage<ProviderType>(
|
| 19 |
-
"deepsite-provider",
|
| 20 |
-
"auto" as ProviderType
|
| 21 |
-
);
|
| 22 |
-
const router = useRouter();
|
| 23 |
-
const [prompt, setPrompt] = useState<string>("");
|
| 24 |
-
const [mounted, setMounted] = useState<boolean>(false);
|
| 25 |
-
|
| 26 |
-
useMount(() => {
|
| 27 |
-
setMounted(true);
|
| 28 |
-
});
|
| 29 |
-
|
| 30 |
-
return (
|
| 31 |
-
<div
|
| 32 |
-
className={cn(
|
| 33 |
-
"dark:bg-[#222222] bg-accent border border-border-muted rounded-xl p-3 block relative",
|
| 34 |
-
className
|
| 35 |
-
)}
|
| 36 |
-
>
|
| 37 |
-
<textarea
|
| 38 |
-
id="prompt-input"
|
| 39 |
-
className="w-full h-full resize-none outline-none text-primary text-sm"
|
| 40 |
-
placeholder="Ask me anything..."
|
| 41 |
-
value={prompt}
|
| 42 |
-
onChange={(e) => setPrompt(e.target.value)}
|
| 43 |
-
onKeyDown={(e) => {
|
| 44 |
-
if (e.key === "Enter" && !e.shiftKey) {
|
| 45 |
-
e.preventDefault();
|
| 46 |
-
router.push(`/new?prompt=${prompt}`);
|
| 47 |
-
}
|
| 48 |
-
}}
|
| 49 |
-
/>
|
| 50 |
-
<footer className="flex items-center justify-between">
|
| 51 |
-
<div className="flex items-center gap-2">
|
| 52 |
-
{mounted && (
|
| 53 |
-
<Models
|
| 54 |
-
model={model}
|
| 55 |
-
setModel={setModel}
|
| 56 |
-
provider={provider as ProviderType}
|
| 57 |
-
setProvider={setProvider}
|
| 58 |
-
/>
|
| 59 |
-
)}
|
| 60 |
-
</div>
|
| 61 |
-
<div>
|
| 62 |
-
<Button
|
| 63 |
-
size="icon-sm"
|
| 64 |
-
className="rounded-full!"
|
| 65 |
-
onClick={() => {
|
| 66 |
-
router.push(`/new?prompt=${prompt}`);
|
| 67 |
-
}}
|
| 68 |
-
>
|
| 69 |
-
<ArrowUp />
|
| 70 |
-
</Button>
|
| 71 |
-
</div>
|
| 72 |
-
</footer>
|
| 73 |
-
</div>
|
| 74 |
-
);
|
| 75 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/ask-ai/ask-ai.tsx
DELETED
|
@@ -1,215 +0,0 @@
|
|
| 1 |
-
"use client";
|
| 2 |
-
import { ArrowUp, Paintbrush, X } from "lucide-react";
|
| 3 |
-
import { FaHand } from "react-icons/fa6";
|
| 4 |
-
import { useRef, useState } from "react";
|
| 5 |
-
import { HiStop } from "react-icons/hi2";
|
| 6 |
-
import { useLocalStorage, useMount, useUpdateEffect } from "react-use";
|
| 7 |
-
import { useRouter } from "next/navigation";
|
| 8 |
-
import { useNextStep } from "nextstepjs";
|
| 9 |
-
|
| 10 |
-
import { Button } from "@/components/ui/button";
|
| 11 |
-
import { cn } from "@/lib/utils";
|
| 12 |
-
import { useGeneration } from "./useGeneration";
|
| 13 |
-
import { File, MobileTabType, ProviderType } from "@/lib/type";
|
| 14 |
-
import { Models } from "./models";
|
| 15 |
-
import { DEFAULT_MODEL, MODELS } from "@/lib/providers";
|
| 16 |
-
import { Redesign } from "./redesign";
|
| 17 |
-
import { Uploader } from "./uploader";
|
| 18 |
-
import { InputMentions } from "./input-mentions";
|
| 19 |
-
|
| 20 |
-
export function AskAI({
|
| 21 |
-
initialPrompt,
|
| 22 |
-
className,
|
| 23 |
-
onToggleMobileTab,
|
| 24 |
-
files,
|
| 25 |
-
medias,
|
| 26 |
-
tourHasBeenShown,
|
| 27 |
-
isNew = false,
|
| 28 |
-
isHistoryView,
|
| 29 |
-
projectName = "new",
|
| 30 |
-
}: {
|
| 31 |
-
initialPrompt?: string;
|
| 32 |
-
className?: string;
|
| 33 |
-
files?: File[] | null;
|
| 34 |
-
medias?: string[] | null;
|
| 35 |
-
tourHasBeenShown?: boolean;
|
| 36 |
-
onToggleMobileTab?: (tab: MobileTabType) => void;
|
| 37 |
-
isNew?: boolean;
|
| 38 |
-
isHistoryView?: boolean;
|
| 39 |
-
projectName?: string;
|
| 40 |
-
}) {
|
| 41 |
-
const contentEditableRef = useRef<HTMLDivElement | null>(null);
|
| 42 |
-
const [model = DEFAULT_MODEL, setModel] = useLocalStorage<string>(
|
| 43 |
-
"deepsite-model",
|
| 44 |
-
DEFAULT_MODEL
|
| 45 |
-
);
|
| 46 |
-
const [provider, setProvider] = useLocalStorage<ProviderType>(
|
| 47 |
-
"deepsite-provider",
|
| 48 |
-
"auto" as ProviderType
|
| 49 |
-
);
|
| 50 |
-
|
| 51 |
-
const [prompt, setPrompt] = useState(initialPrompt ?? "");
|
| 52 |
-
const [redesignMd, setRedesignMd] = useState<{
|
| 53 |
-
md: string;
|
| 54 |
-
url: string;
|
| 55 |
-
} | null>(null);
|
| 56 |
-
const [selectedMedias, setSelectedMedias] = useState<string[]>([]);
|
| 57 |
-
const [imageLinks, setImageLinks] = useState<string[]>([]);
|
| 58 |
-
const [startTour, setStartTour] = useState<boolean>(false);
|
| 59 |
-
|
| 60 |
-
const router = useRouter();
|
| 61 |
-
const { callAi, isLoading, stopGeneration, audio } =
|
| 62 |
-
useGeneration(projectName);
|
| 63 |
-
const { startNextStep } = useNextStep();
|
| 64 |
-
|
| 65 |
-
const onComplete = () => {
|
| 66 |
-
onToggleMobileTab?.("right-sidebar");
|
| 67 |
-
};
|
| 68 |
-
|
| 69 |
-
useMount(() => {
|
| 70 |
-
if (initialPrompt && initialPrompt.trim() !== "" && isNew) {
|
| 71 |
-
setTimeout(() => {
|
| 72 |
-
if (isHistoryView) return;
|
| 73 |
-
callAi(
|
| 74 |
-
{
|
| 75 |
-
prompt: initialPrompt,
|
| 76 |
-
model,
|
| 77 |
-
onComplete,
|
| 78 |
-
provider,
|
| 79 |
-
},
|
| 80 |
-
setModel
|
| 81 |
-
);
|
| 82 |
-
router.replace("/new");
|
| 83 |
-
}, 200);
|
| 84 |
-
}
|
| 85 |
-
});
|
| 86 |
-
|
| 87 |
-
const onSubmit = () => {
|
| 88 |
-
if (isHistoryView) return;
|
| 89 |
-
if (contentEditableRef.current) {
|
| 90 |
-
contentEditableRef.current.innerHTML = "";
|
| 91 |
-
}
|
| 92 |
-
callAi(
|
| 93 |
-
{
|
| 94 |
-
prompt,
|
| 95 |
-
model,
|
| 96 |
-
onComplete,
|
| 97 |
-
provider,
|
| 98 |
-
redesignMd,
|
| 99 |
-
medias: [...(selectedMedias ?? []), ...(imageLinks ?? [])],
|
| 100 |
-
},
|
| 101 |
-
setModel
|
| 102 |
-
);
|
| 103 |
-
if (selectedMedias.length > 0) setSelectedMedias([]);
|
| 104 |
-
if (imageLinks.length > 0) setImageLinks([]);
|
| 105 |
-
if (redesignMd) setRedesignMd(null);
|
| 106 |
-
};
|
| 107 |
-
|
| 108 |
-
return (
|
| 109 |
-
<div
|
| 110 |
-
id="tour-ask-ai-section"
|
| 111 |
-
className={cn(
|
| 112 |
-
"dark:bg-[#222222] bg-accent border border-border-muted rounded-xl p-2.5 block relative",
|
| 113 |
-
className
|
| 114 |
-
)}
|
| 115 |
-
>
|
| 116 |
-
<InputMentions
|
| 117 |
-
ref={contentEditableRef}
|
| 118 |
-
files={files}
|
| 119 |
-
prompt={prompt}
|
| 120 |
-
setPrompt={setPrompt}
|
| 121 |
-
redesignMdUrl={redesignMd?.url?.replace(/(^\w+:|^)\/\//, "")}
|
| 122 |
-
onSubmit={onSubmit}
|
| 123 |
-
imageLinks={imageLinks}
|
| 124 |
-
setImageLinks={setImageLinks}
|
| 125 |
-
/>
|
| 126 |
-
<footer className="flex items-center justify-between mt-0">
|
| 127 |
-
<div className="flex items-center gap-1.5">
|
| 128 |
-
{!tourHasBeenShown && (
|
| 129 |
-
<div className="relative z-1">
|
| 130 |
-
<Button
|
| 131 |
-
variant="indigo"
|
| 132 |
-
size="icon-xs"
|
| 133 |
-
className="rounded-full!"
|
| 134 |
-
onClick={() => {
|
| 135 |
-
setStartTour(true);
|
| 136 |
-
startNextStep("onboarding");
|
| 137 |
-
}}
|
| 138 |
-
>
|
| 139 |
-
<FaHand className="size-3" />
|
| 140 |
-
</Button>
|
| 141 |
-
{!startTour && (
|
| 142 |
-
<div className="animate-ping h-full rounded-full bg-indigo-500 w-full top-0 left-0 absolute -z-1" />
|
| 143 |
-
)}
|
| 144 |
-
</div>
|
| 145 |
-
)}
|
| 146 |
-
{!isNew && (
|
| 147 |
-
<Uploader
|
| 148 |
-
medias={medias}
|
| 149 |
-
selected={selectedMedias}
|
| 150 |
-
setSelected={setSelectedMedias}
|
| 151 |
-
/>
|
| 152 |
-
)}
|
| 153 |
-
<Models
|
| 154 |
-
model={model}
|
| 155 |
-
setModel={setModel}
|
| 156 |
-
provider={provider as ProviderType}
|
| 157 |
-
setProvider={setProvider}
|
| 158 |
-
/>
|
| 159 |
-
{!files ||
|
| 160 |
-
(files?.length === 0 &&
|
| 161 |
-
(redesignMd ? (
|
| 162 |
-
<Button
|
| 163 |
-
size="xs"
|
| 164 |
-
variant="indigo"
|
| 165 |
-
className="rounded-full! px-2.5!"
|
| 166 |
-
onClick={() => setRedesignMd(null)}
|
| 167 |
-
>
|
| 168 |
-
<Paintbrush className="size-3" />
|
| 169 |
-
{redesignMd.url?.replace(/(^\w+:|^)\/\//, "")}
|
| 170 |
-
<X className="size-3.5" onClick={() => setRedesignMd(null)} />
|
| 171 |
-
</Button>
|
| 172 |
-
) : (
|
| 173 |
-
<Redesign
|
| 174 |
-
onRedesign={(md, url) => {
|
| 175 |
-
setRedesignMd({
|
| 176 |
-
md,
|
| 177 |
-
url,
|
| 178 |
-
});
|
| 179 |
-
}}
|
| 180 |
-
/>
|
| 181 |
-
)))}
|
| 182 |
-
</div>
|
| 183 |
-
<div>
|
| 184 |
-
{isLoading ? (
|
| 185 |
-
<Button
|
| 186 |
-
size="icon-sm"
|
| 187 |
-
className="rounded-full!"
|
| 188 |
-
variant="bordered"
|
| 189 |
-
onClick={stopGeneration}
|
| 190 |
-
>
|
| 191 |
-
<HiStop />
|
| 192 |
-
</Button>
|
| 193 |
-
) : (
|
| 194 |
-
<Button
|
| 195 |
-
size="icon-sm"
|
| 196 |
-
className="rounded-full!"
|
| 197 |
-
disabled={
|
| 198 |
-
isHistoryView ||
|
| 199 |
-
isLoading ||
|
| 200 |
-
(prompt.trim() === "" && !redesignMd)
|
| 201 |
-
}
|
| 202 |
-
onClick={onSubmit}
|
| 203 |
-
>
|
| 204 |
-
<ArrowUp />
|
| 205 |
-
</Button>
|
| 206 |
-
)}
|
| 207 |
-
</div>
|
| 208 |
-
</footer>
|
| 209 |
-
<audio ref={audio} id="audio" className="hidden">
|
| 210 |
-
<source src="/ding.mp3" type="audio/mpeg" />
|
| 211 |
-
Your browser does not support the audio element.
|
| 212 |
-
</audio>
|
| 213 |
-
</div>
|
| 214 |
-
);
|
| 215 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/ask-ai/context.tsx
DELETED
|
@@ -1,123 +0,0 @@
|
|
| 1 |
-
import { AtSign, Braces, FileCode, FileText, X } from "lucide-react";
|
| 2 |
-
import { useMemo, useState } from "react";
|
| 3 |
-
import { useQueryClient } from "@tanstack/react-query";
|
| 4 |
-
|
| 5 |
-
import {
|
| 6 |
-
Popover,
|
| 7 |
-
PopoverContent,
|
| 8 |
-
PopoverTrigger,
|
| 9 |
-
} from "@/components/ui/popover";
|
| 10 |
-
import { Button } from "@/components/ui/button";
|
| 11 |
-
import { File } from "@/lib/type";
|
| 12 |
-
import { cn } from "@/lib/utils";
|
| 13 |
-
|
| 14 |
-
export const Context = ({
|
| 15 |
-
files,
|
| 16 |
-
setFiles,
|
| 17 |
-
}: {
|
| 18 |
-
files: File[];
|
| 19 |
-
setFiles: (files: File[]) => void;
|
| 20 |
-
}) => {
|
| 21 |
-
const queryClient = useQueryClient();
|
| 22 |
-
const [open, setOpen] = useState(false);
|
| 23 |
-
|
| 24 |
-
const getFileIcon = (filePath: string, size = "size-3.5") => {
|
| 25 |
-
if (filePath.endsWith(".css")) {
|
| 26 |
-
return <Braces className={size} />;
|
| 27 |
-
} else if (filePath.endsWith(".js")) {
|
| 28 |
-
return <FileCode className={size} />;
|
| 29 |
-
} else if (filePath.endsWith(".json")) {
|
| 30 |
-
return <Braces className={size} />;
|
| 31 |
-
} else {
|
| 32 |
-
return <FileText className={size} />;
|
| 33 |
-
}
|
| 34 |
-
};
|
| 35 |
-
|
| 36 |
-
const getFiles = () => queryClient.getQueryData<File[]>(["files"]) ?? [];
|
| 37 |
-
|
| 38 |
-
return (
|
| 39 |
-
<div className="flex items-center justify-start gap-1 flex-wrap">
|
| 40 |
-
<Popover open={open} onOpenChange={setOpen}>
|
| 41 |
-
<PopoverTrigger asChild>
|
| 42 |
-
<Button size="xxs" variant={open ? "default" : "bordered"}>
|
| 43 |
-
<AtSign className="size-3" />
|
| 44 |
-
Add Context...
|
| 45 |
-
</Button>
|
| 46 |
-
</PopoverTrigger>
|
| 47 |
-
<PopoverContent
|
| 48 |
-
align="start"
|
| 49 |
-
className="translate-x-6 space-y-4 min-w-fit rounded-2xl! p-0!"
|
| 50 |
-
>
|
| 51 |
-
<main className="p-4">
|
| 52 |
-
<p className="text-xs text-muted-foreground mb-2.5">
|
| 53 |
-
Select a file to send as context
|
| 54 |
-
</p>
|
| 55 |
-
<div className="max-h-[200px] overflow-y-auto space-y-0.5">
|
| 56 |
-
{getFiles().length === 0 ? (
|
| 57 |
-
<div className="text-xs text-muted-foreground">
|
| 58 |
-
No files available
|
| 59 |
-
</div>
|
| 60 |
-
) : (
|
| 61 |
-
<>
|
| 62 |
-
<button
|
| 63 |
-
onClick={() => {
|
| 64 |
-
setFiles([]);
|
| 65 |
-
setOpen(false);
|
| 66 |
-
}}
|
| 67 |
-
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 ${
|
| 68 |
-
files.length === 0
|
| 69 |
-
? "bg-linear-to-r from-indigo-500/20 to-indigo-500/5 text-primary font-medium"
|
| 70 |
-
: "text-muted-foreground"
|
| 71 |
-
}`}
|
| 72 |
-
>
|
| 73 |
-
All files (default)
|
| 74 |
-
</button>
|
| 75 |
-
{getFiles()?.map((page) => (
|
| 76 |
-
<button
|
| 77 |
-
key={page.path}
|
| 78 |
-
onClick={() => {
|
| 79 |
-
if (files.some((f) => f.path === page.path))
|
| 80 |
-
setFiles(files.filter((f) => f.path !== page.path));
|
| 81 |
-
else setFiles(files ? [...files, page] : [page]);
|
| 82 |
-
setOpen(false);
|
| 83 |
-
}}
|
| 84 |
-
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 ${
|
| 85 |
-
files?.some((f) => f.path === page.path)
|
| 86 |
-
? "bg-linear-to-r from-indigo-500/20 to-indigo-500/5 text-primary font-medium"
|
| 87 |
-
: "text-muted-foreground"
|
| 88 |
-
}`}
|
| 89 |
-
>
|
| 90 |
-
<span className="shrink-0">
|
| 91 |
-
{getFileIcon(page.path, "size-3")}
|
| 92 |
-
</span>
|
| 93 |
-
<span className="truncate flex-1">{page.path}</span>
|
| 94 |
-
</button>
|
| 95 |
-
))}
|
| 96 |
-
</>
|
| 97 |
-
)}
|
| 98 |
-
</div>
|
| 99 |
-
</main>
|
| 100 |
-
</PopoverContent>
|
| 101 |
-
</Popover>
|
| 102 |
-
{files?.map((file) => (
|
| 103 |
-
<Button
|
| 104 |
-
key={file.path}
|
| 105 |
-
size="xxs"
|
| 106 |
-
variant="bordered"
|
| 107 |
-
className="cursor-default!"
|
| 108 |
-
>
|
| 109 |
-
{getFileIcon(file.path, "size-3")}
|
| 110 |
-
{file.path}
|
| 111 |
-
<span
|
| 112 |
-
className="opacity-50 hover:opacity-80 cursor-pointer"
|
| 113 |
-
onClick={() => {
|
| 114 |
-
setFiles(files.filter((f) => f.path !== file.path));
|
| 115 |
-
}}
|
| 116 |
-
>
|
| 117 |
-
<X className="size-3.5 opacity-50 hover:opacity-80 shrink-0" />
|
| 118 |
-
</span>
|
| 119 |
-
</Button>
|
| 120 |
-
))}
|
| 121 |
-
</div>
|
| 122 |
-
);
|
| 123 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|