Spaces:
Running
Running
new UI
Browse files- .dockerignore +67 -0
- Dockerfile +91 -0
- README.md +99 -107
- README_GRADIO.md +145 -0
- backend_api.py +499 -0
- frontend/.gitignore +35 -0
- frontend/next.config.js +26 -0
- frontend/package-lock.json +0 -0
- frontend/package.json +35 -0
- frontend/postcss.config.js +7 -0
- frontend/src/app/globals.css +114 -0
- frontend/src/app/layout.tsx +23 -0
- frontend/src/app/page.tsx +278 -0
- frontend/src/components/ChatInterface.tsx +119 -0
- frontend/src/components/CodeEditor.tsx +60 -0
- frontend/src/components/ControlPanel.tsx +132 -0
- frontend/src/components/Header.tsx +179 -0
- frontend/src/types/index.ts +60 -0
- frontend/tailwind.config.js +29 -0
- frontend/tsconfig.json +27 -0
- start_backend.sh +21 -0
- start_frontend.sh +18 -0
- start_fullstack.sh +53 -0
.dockerignore
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
venv/
|
| 8 |
+
.venv/
|
| 9 |
+
ENV/
|
| 10 |
+
env/
|
| 11 |
+
*.egg-info/
|
| 12 |
+
dist/
|
| 13 |
+
build/
|
| 14 |
+
|
| 15 |
+
# Node
|
| 16 |
+
node_modules/
|
| 17 |
+
npm-debug.log*
|
| 18 |
+
yarn-debug.log*
|
| 19 |
+
yarn-error.log*
|
| 20 |
+
.pnpm-debug.log*
|
| 21 |
+
|
| 22 |
+
# Next.js
|
| 23 |
+
frontend/.next/
|
| 24 |
+
frontend/out/
|
| 25 |
+
frontend/build/
|
| 26 |
+
|
| 27 |
+
# Git
|
| 28 |
+
.git/
|
| 29 |
+
.gitignore
|
| 30 |
+
|
| 31 |
+
# IDE
|
| 32 |
+
.vscode/
|
| 33 |
+
.idea/
|
| 34 |
+
*.swp
|
| 35 |
+
*.swo
|
| 36 |
+
*~
|
| 37 |
+
|
| 38 |
+
# OS
|
| 39 |
+
.DS_Store
|
| 40 |
+
Thumbs.db
|
| 41 |
+
|
| 42 |
+
# Documentation
|
| 43 |
+
*.md
|
| 44 |
+
!README.md
|
| 45 |
+
|
| 46 |
+
# Docker
|
| 47 |
+
Dockerfile*
|
| 48 |
+
docker-compose*.yml
|
| 49 |
+
.dockerignore
|
| 50 |
+
|
| 51 |
+
# Logs
|
| 52 |
+
*.log
|
| 53 |
+
logs/
|
| 54 |
+
log/
|
| 55 |
+
|
| 56 |
+
# Generated
|
| 57 |
+
generated_projects/
|
| 58 |
+
|
| 59 |
+
# Tests
|
| 60 |
+
test/
|
| 61 |
+
tests/
|
| 62 |
+
__tests__/
|
| 63 |
+
|
| 64 |
+
# Lock files (will be regenerated)
|
| 65 |
+
uv.lock
|
| 66 |
+
poetry.lock
|
| 67 |
+
|
Dockerfile
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Multi-stage build for AnyCoder Docker Space
|
| 2 |
+
|
| 3 |
+
# Stage 1: Build frontend
|
| 4 |
+
FROM node:18-slim AS frontend-builder
|
| 5 |
+
|
| 6 |
+
WORKDIR /build
|
| 7 |
+
|
| 8 |
+
# Copy frontend package files
|
| 9 |
+
COPY frontend/package*.json ./
|
| 10 |
+
RUN npm ci
|
| 11 |
+
|
| 12 |
+
# Copy frontend source
|
| 13 |
+
COPY frontend/ ./
|
| 14 |
+
|
| 15 |
+
# Build frontend
|
| 16 |
+
RUN npm run build
|
| 17 |
+
|
| 18 |
+
# Stage 2: Production image
|
| 19 |
+
FROM python:3.11-slim
|
| 20 |
+
|
| 21 |
+
# Set up a new user named "user" with user ID 1000
|
| 22 |
+
RUN useradd -m -u 1000 user
|
| 23 |
+
|
| 24 |
+
# Switch to the "user" user
|
| 25 |
+
USER user
|
| 26 |
+
|
| 27 |
+
# Set home to the user's home directory
|
| 28 |
+
ENV HOME=/home/user \
|
| 29 |
+
PATH=/home/user/.local/bin:$PATH \
|
| 30 |
+
PYTHONUNBUFFERED=1
|
| 31 |
+
|
| 32 |
+
# Set the working directory to the user's home directory
|
| 33 |
+
WORKDIR $HOME/app
|
| 34 |
+
|
| 35 |
+
# Copy Python requirements and install dependencies
|
| 36 |
+
COPY --chown=user:user requirements.txt .
|
| 37 |
+
RUN pip install --no-cache-dir --upgrade pip && \
|
| 38 |
+
pip install --no-cache-dir -r requirements.txt
|
| 39 |
+
|
| 40 |
+
# Copy application code
|
| 41 |
+
COPY --chown=user:user anycoder_app/ ./anycoder_app/
|
| 42 |
+
COPY --chown=user:user backend_api.py .
|
| 43 |
+
COPY --chown=user:user app.py .
|
| 44 |
+
|
| 45 |
+
# Copy built frontend from builder stage
|
| 46 |
+
COPY --chown=user:user --from=frontend-builder /build/.next ./frontend/.next
|
| 47 |
+
COPY --chown=user:user --from=frontend-builder /build/public ./frontend/public
|
| 48 |
+
COPY --chown=user:user --from=frontend-builder /build/package*.json ./frontend/
|
| 49 |
+
COPY --chown=user:user --from=frontend-builder /build/next.config.js ./frontend/
|
| 50 |
+
COPY --chown=user:user --from=frontend-builder /build/node_modules ./frontend/node_modules
|
| 51 |
+
|
| 52 |
+
# Install Node.js for running frontend
|
| 53 |
+
USER root
|
| 54 |
+
RUN apt-get update && \
|
| 55 |
+
apt-get install -y --no-install-recommends nodejs npm && \
|
| 56 |
+
rm -rf /var/lib/apt/lists/*
|
| 57 |
+
USER user
|
| 58 |
+
|
| 59 |
+
# Set environment variables for the application
|
| 60 |
+
ENV BACKEND_HOST=http://localhost:8000 \
|
| 61 |
+
PORT=7860
|
| 62 |
+
|
| 63 |
+
# Create startup script that runs both services
|
| 64 |
+
# Backend on 8000, Frontend on 7860 (exposed port)
|
| 65 |
+
RUN echo '#!/bin/bash\n\
|
| 66 |
+
set -e\n\
|
| 67 |
+
\n\
|
| 68 |
+
echo "π Starting AnyCoder Docker Space..."\n\
|
| 69 |
+
\n\
|
| 70 |
+
# Start backend on port 8000 in background\n\
|
| 71 |
+
echo "π‘ Starting FastAPI backend on port 8000..."\n\
|
| 72 |
+
cd $HOME/app\n\
|
| 73 |
+
uvicorn backend_api:app --host 0.0.0.0 --port 8000 &\n\
|
| 74 |
+
BACKEND_PID=$!\n\
|
| 75 |
+
\n\
|
| 76 |
+
# Wait for backend to be ready\n\
|
| 77 |
+
echo "β³ Waiting for backend to start..."\n\
|
| 78 |
+
sleep 5\n\
|
| 79 |
+
\n\
|
| 80 |
+
# Start frontend on port 7860 (HF Spaces exposed port)\n\
|
| 81 |
+
echo "π¨ Starting Next.js frontend on port 7860..."\n\
|
| 82 |
+
cd $HOME/app/frontend\n\
|
| 83 |
+
PORT=7860 BACKEND_HOST=http://localhost:8000 npm start\n\
|
| 84 |
+
' > $HOME/app/start.sh && chmod +x $HOME/app/start.sh
|
| 85 |
+
|
| 86 |
+
# Expose port 7860 (HF Spaces default)
|
| 87 |
+
EXPOSE 7860
|
| 88 |
+
|
| 89 |
+
# Run the startup script
|
| 90 |
+
CMD ["./start.sh"]
|
| 91 |
+
|
README.md
CHANGED
|
@@ -1,143 +1,135 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
-
sdk:
|
| 7 |
-
|
| 8 |
-
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
-
disable_embedding:
|
| 11 |
hf_oauth: true
|
| 12 |
hf_oauth_scopes:
|
| 13 |
-
- manage-repos
|
| 14 |
---
|
| 15 |
|
| 16 |
-
|
|
|
|
| 17 |
|
| 18 |
-
AnyCoder
|
| 19 |
|
| 20 |
-
|
| 21 |
|
| 22 |
-
|
| 23 |
-
- Claude-Opus-4.1 (via Poe)
|
| 24 |
-
- **Flexible Input**: Describe your app in text, upload a UI design image (for multimodal models), provide a reference file (PDF, TXT, MD, CSV, DOCX, or image), or enter a website URL for redesign
|
| 25 |
-
- **Web Search Integration**: Enable real-time web search (Tavily, with advanced search depth) to enhance code generation with up-to-date information and best practices
|
| 26 |
-
- **Code Generation**: Generate code in HTML, Python, JS, and more. Special support for transformers.js apps (outputs index.html, index.js, style.css)
|
| 27 |
-
- **Live Preview**: Instantly preview generated HTML in a sandboxed iframe
|
| 28 |
-
- **Modify Existing Code**: Use search/replace block format to update generated HTML
|
| 29 |
-
- **One-Click Deployment**: Deploy your app to Hugging Face Spaces (Gradio, Streamlit, Static HTML, or Transformers.js) with OAuth login
|
| 30 |
-
- **History & Examples**: Chat-like history of all interactions and quick example prompts for fast prototyping
|
| 31 |
-
- **Minimal, Modern UI**: Built with Gradio 5.x, using only built-in theming and styling (no custom CSS)
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
1. Clone the repository:
|
| 36 |
-
```bash
|
| 37 |
-
git clone <repository-url>
|
| 38 |
-
cd anycoder
|
| 39 |
```
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
```
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
```bash
|
| 46 |
export HF_TOKEN="your_huggingface_token"
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
| 52 |
```
|
| 53 |
|
| 54 |
-
|
| 55 |
|
| 56 |
-
|
| 57 |
```bash
|
| 58 |
-
|
|
|
|
| 59 |
```
|
| 60 |
-
2. Open your browser and navigate to the provided URL
|
| 61 |
-
3. Describe your application in the text input field, or:
|
| 62 |
-
- Upload a UI design image (for multimodal models)
|
| 63 |
-
- Upload a reference file (PDF, TXT, MD, CSV, DOCX, or image)
|
| 64 |
-
- Enter a website URL for redesign (the app will extract and analyze the HTML and content)
|
| 65 |
-
- Enable web search for up-to-date information
|
| 66 |
-
- Choose a different AI model or code language
|
| 67 |
-
4. Click "Generate" to create your code
|
| 68 |
-
5. View the generated code in the Code tab or see it in action in the Preview tab
|
| 69 |
-
6. Use the History tab to review previous generations
|
| 70 |
-
7. **Deploy to Space**: Enter a title and click "π Deploy App" to publish your application (OAuth login required) - the SDK is automatically matched to your selected code language
|
| 71 |
-
|
| 72 |
-
## Supported Models
|
| 73 |
-
|
| 74 |
-
- Moonshot Kimi-K2
|
| 75 |
-
- Kimi K2 Turbo (Preview)
|
| 76 |
-
- Kimi K2 Thinking
|
| 77 |
-
- DeepSeek V3
|
| 78 |
-
- DeepSeek V3.1
|
| 79 |
-
- DeepSeek V3.1 Terminus
|
| 80 |
-
- DeepSeek V3.2-Exp
|
| 81 |
-
- DeepSeek R1
|
| 82 |
-
- MiniMax M2
|
| 83 |
-
- Qwen3-235B-A22B
|
| 84 |
-
- Qwen3-4B-Instruct-2507
|
| 85 |
-
- Qwen3-4B-Thinking-2507
|
| 86 |
-
- Qwen3-30B-A3B-Instruct-2507 (via DashScope)
|
| 87 |
-
- Qwen3-30B-A3B-Thinking-2507 (via DashScope)
|
| 88 |
-
- GPT-5 (via Poe)
|
| 89 |
-
- Grok-4 (via Poe)
|
| 90 |
-
- Claude-Opus-4.1 (via Poe)
|
| 91 |
-
- Gemini 2.5 Flash (OpenAI-compatible)
|
| 92 |
-
- Gemini 2.5 Pro (OpenAI-compatible)
|
| 93 |
|
| 94 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
-
|
| 97 |
-
- **Image Upload**: For multimodal models, upload a UI design image to generate code from visuals
|
| 98 |
-
- **File Upload**: Provide a reference file (PDF, TXT, MD, CSV, DOCX, or image) for code generation or text extraction (OCR for images)
|
| 99 |
-
- **Website URL**: Enter a URL to extract and redesign the website (HTML and content are analyzed and modernized)
|
| 100 |
|
| 101 |
-
|
|
|
|
| 102 |
|
| 103 |
-
|
| 104 |
-
- Special support for transformers.js apps (outputs index.html, index.js, style.css)
|
| 105 |
-
- Svelte apps
|
| 106 |
-
- For HTML, provides a live preview in a sandboxed iframe
|
| 107 |
-
- For modification requests, uses a search/replace block format to update existing HTML
|
| 108 |
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
-
|
| 112 |
-
- Supported SDKs: Gradio (Python), Streamlit (Python), Static (HTML), Transformers.js
|
| 113 |
-
- OAuth login with Hugging Face is required for deployment to user-owned Spaces
|
| 114 |
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
-
|
| 118 |
-
- Quick example prompts are available in the sidebar for fast prototyping
|
| 119 |
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
-
|
| 123 |
-
- Minimal, uncluttered sidebar and interface
|
| 124 |
|
| 125 |
-
|
| 126 |
|
| 127 |
-
|
| 128 |
-
- `GEMINI_API_KEY`: Your Google Gemini API key (required to use Gemini models)
|
| 129 |
-
- `MOONSHOT_API_KEY`: Your Moonshot AI API key (required to use Kimi models)
|
| 130 |
-
- `MINIMAX_API_KEY`: Your MiniMax API key (required to use MiniMax M2 model)
|
| 131 |
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
βββ README.md # This file
|
| 139 |
-
```
|
| 140 |
|
| 141 |
-
## License
|
| 142 |
|
| 143 |
-
|
|
|
|
| 1 |
---
|
| 2 |
+
title: AnyCoder
|
| 3 |
+
emoji: π
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
|
|
|
| 8 |
pinned: false
|
| 9 |
+
disable_embedding: false
|
| 10 |
hf_oauth: true
|
| 11 |
hf_oauth_scopes:
|
| 12 |
+
- manage-repos
|
| 13 |
---
|
| 14 |
|
| 15 |
+
> **Note:** This is the Docker Space configuration for the React frontend version.
|
| 16 |
+
> For the original Gradio app, see `README_GRADIO.md`.
|
| 17 |
|
| 18 |
+
# AnyCoder - AI Code Generator with React Frontend
|
| 19 |
|
| 20 |
+
AnyCoder is a full-stack AI-powered code generator with a modern React/TypeScript frontend and FastAPI backend. Generate applications by describing them in plain English, with support for multiple AI models and one-click deployment to Hugging Face Spaces.
|
| 21 |
|
| 22 |
+
## π¨ Features
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
- **Modern React UI**: Apple-inspired design with VS Code layout
|
| 25 |
+
- **Real-time Streaming**: Server-Sent Events for live code generation
|
| 26 |
+
- **Multi-Model Support**: MiniMax M2, DeepSeek V3, and more via HuggingFace InferenceClient
|
| 27 |
+
- **Multiple Languages**: HTML, Gradio, Streamlit, React, Transformers.js, ComfyUI
|
| 28 |
+
- **Authentication**: HuggingFace OAuth + Dev mode for local testing
|
| 29 |
+
- **One-Click Deployment**: Deploy generated apps directly to HF Spaces
|
| 30 |
+
|
| 31 |
+
## ποΈ Architecture
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
```
|
| 34 |
+
anycoder/
|
| 35 |
+
βββ backend_api.py # FastAPI backend with streaming
|
| 36 |
+
βββ frontend/ # Next.js React frontend
|
| 37 |
+
β βββ src/
|
| 38 |
+
β β βββ app/ # Pages (page.tsx, layout.tsx, globals.css)
|
| 39 |
+
β β βββ components/ # React components
|
| 40 |
+
β β βββ lib/ # API client, auth utilities
|
| 41 |
+
β β βββ types/ # TypeScript types
|
| 42 |
+
β βββ package.json
|
| 43 |
+
βββ anycoder_app/ # Original Gradio app modules
|
| 44 |
+
β βββ agent.py
|
| 45 |
+
β βββ config.py
|
| 46 |
+
β βββ deploy.py
|
| 47 |
+
β βββ ...
|
| 48 |
+
βββ app.py # Original Gradio interface
|
| 49 |
+
βββ requirements.txt # Python dependencies
|
| 50 |
+
βββ Dockerfile # Docker Space configuration
|
| 51 |
+
βββ start_fullstack.sh # Local development script
|
| 52 |
```
|
| 53 |
+
|
| 54 |
+
## π Quick Start
|
| 55 |
+
|
| 56 |
+
### Local Development
|
| 57 |
+
|
| 58 |
+
1. **Backend**:
|
| 59 |
```bash
|
| 60 |
export HF_TOKEN="your_huggingface_token"
|
| 61 |
+
python backend_api.py
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
2. **Frontend** (new terminal):
|
| 65 |
+
```bash
|
| 66 |
+
cd frontend
|
| 67 |
+
npm install
|
| 68 |
+
npm run dev
|
| 69 |
```
|
| 70 |
|
| 71 |
+
3. Open `http://localhost:3000`
|
| 72 |
|
| 73 |
+
### Using start script:
|
| 74 |
```bash
|
| 75 |
+
export HF_TOKEN="your_token"
|
| 76 |
+
./start_fullstack.sh
|
| 77 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
+
## π³ Docker Space Deployment
|
| 80 |
+
|
| 81 |
+
This app runs as a Docker Space on HuggingFace. The Dockerfile:
|
| 82 |
+
- Builds the Next.js frontend
|
| 83 |
+
- Runs FastAPI backend on port 7860
|
| 84 |
+
- Uses proper user permissions (UID 1000)
|
| 85 |
+
- Handles environment variables securely
|
| 86 |
|
| 87 |
+
## π Authentication
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
- **Dev Mode** (localhost): Mock login for testing
|
| 90 |
+
- **Production**: HuggingFace OAuth with manage-repos scope
|
| 91 |
|
| 92 |
+
## π Supported Languages
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
+
- `html` - Static HTML pages
|
| 95 |
+
- `gradio` - Python Gradio apps
|
| 96 |
+
- `streamlit` - Python Streamlit apps
|
| 97 |
+
- `react` - React/Next.js apps
|
| 98 |
+
- `transformers.js` - Browser ML apps
|
| 99 |
+
- `comfyui` - ComfyUI workflows
|
| 100 |
|
| 101 |
+
## π€ Available Models
|
|
|
|
|
|
|
| 102 |
|
| 103 |
+
- MiniMax M2 (via HF router with Novita)
|
| 104 |
+
- DeepSeek V3/V3.1
|
| 105 |
+
- DeepSeek R1
|
| 106 |
+
- And more via HuggingFace InferenceClient
|
| 107 |
|
| 108 |
+
## π― Usage
|
|
|
|
| 109 |
|
| 110 |
+
1. Sign in with HuggingFace (or use Dev Login locally)
|
| 111 |
+
2. Select a language and AI model
|
| 112 |
+
3. Describe your app in the chat
|
| 113 |
+
4. Watch code generate in real-time
|
| 114 |
+
5. Click **π Deploy** to publish to HF Spaces
|
| 115 |
|
| 116 |
+
## π οΈ Environment Variables
|
|
|
|
| 117 |
|
| 118 |
+
- `HF_TOKEN` - HuggingFace API token (required)
|
| 119 |
|
| 120 |
+
## π¦ Tech Stack
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
**Frontend:**
|
| 123 |
+
- Next.js 14
|
| 124 |
+
- TypeScript
|
| 125 |
+
- Tailwind CSS
|
| 126 |
+
- Monaco Editor
|
| 127 |
|
| 128 |
+
**Backend:**
|
| 129 |
+
- FastAPI
|
| 130 |
+
- HuggingFace Hub
|
| 131 |
+
- Server-Sent Events (SSE)
|
|
|
|
|
|
|
| 132 |
|
| 133 |
+
## π License
|
| 134 |
|
| 135 |
+
MIT
|
README_GRADIO.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Anycoder
|
| 3 |
+
emoji: π₯
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.49.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
disable_embedding: true
|
| 11 |
+
hf_oauth: true
|
| 12 |
+
hf_oauth_scopes:
|
| 13 |
+
- manage-repos
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# AnyCoder - AI Code Generator
|
| 17 |
+
|
| 18 |
+
> **π± New:** A React/TypeScript frontend version is now the default! This file is the backup Gradio version. For the Docker Space with modern React UI, see `README.md`.
|
| 19 |
+
|
| 20 |
+
AnyCoder is an AI-powered code generator that helps you create applications by describing them in plain English. It supports multiple AI models, multimodal input, website redesign, and one-click deployment to Hugging Face Spaces. The UI is built with Gradio theming for a minimal, modern experience.
|
| 21 |
+
|
| 22 |
+
## Features
|
| 23 |
+
|
| 24 |
+
- **Multi-Model Support**: Choose from Moonshot Kimi-K2, Kimi K2 Turbo (Preview), Kimi K2 Thinking, DeepSeek V3, DeepSeek R1, ERNIE-4.5-VL, MiniMax M2, Qwen3-235B-A22B, Qwen3-30B-A3B-Instruct-2507, Qwen3-30B-A3B-Thinking-2507, SmolLM3-3B, GLM-4.1V-9B-Thinking, Gemini 2.5 Flash and Gemini 2.5 Pro (OpenAI-compatible)
|
| 25 |
+
- Claude-Opus-4.1 (via Poe)
|
| 26 |
+
- **Flexible Input**: Describe your app in text, upload a UI design image (for multimodal models), provide a reference file (PDF, TXT, MD, CSV, DOCX, or image), or enter a website URL for redesign
|
| 27 |
+
- **Web Search Integration**: Enable real-time web search (Tavily, with advanced search depth) to enhance code generation with up-to-date information and best practices
|
| 28 |
+
- **Code Generation**: Generate code in HTML, Python, JS, and more. Special support for transformers.js apps (outputs index.html, index.js, style.css)
|
| 29 |
+
- **Live Preview**: Instantly preview generated HTML in a sandboxed iframe
|
| 30 |
+
- **Modify Existing Code**: Use search/replace block format to update generated HTML
|
| 31 |
+
- **One-Click Deployment**: Deploy your app to Hugging Face Spaces (Gradio, Streamlit, Static HTML, or Transformers.js) with OAuth login
|
| 32 |
+
- **History & Examples**: Chat-like history of all interactions and quick example prompts for fast prototyping
|
| 33 |
+
- **Minimal, Modern UI**: Built with Gradio 5.x, using only built-in theming and styling (no custom CSS)
|
| 34 |
+
|
| 35 |
+
## Installation
|
| 36 |
+
|
| 37 |
+
1. Clone the repository:
|
| 38 |
+
```bash
|
| 39 |
+
git clone <repository-url>
|
| 40 |
+
cd anycoder
|
| 41 |
+
```
|
| 42 |
+
2. Install dependencies:
|
| 43 |
+
```bash
|
| 44 |
+
pip install -r requirements.txt
|
| 45 |
+
```
|
| 46 |
+
3. Set up environment variables:
|
| 47 |
+
```bash
|
| 48 |
+
export HF_TOKEN="your_huggingface_token"
|
| 49 |
+
export DASHSCOPE_API_KEY="your_dashscope_api_key" # Required for Qwen3-30B models via DashScope
|
| 50 |
+
export POE_API_KEY="your_poe_api_key" # Required for GPT-5, Grok-4, and Grok-Code-Fast-1 via Poe
|
| 51 |
+
export GEMINI_API_KEY="your_gemini_api_key" # Required for Gemini models
|
| 52 |
+
export MOONSHOT_API_KEY="your_moonshot_api_key" # Required for Kimi models
|
| 53 |
+
export MINIMAX_API_KEY="your_minimax_api_key" # Required for MiniMax M2 model
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
## Usage
|
| 57 |
+
|
| 58 |
+
1. Run the application:
|
| 59 |
+
```bash
|
| 60 |
+
python app.py
|
| 61 |
+
```
|
| 62 |
+
2. Open your browser and navigate to the provided URL
|
| 63 |
+
3. Describe your application in the text input field, or:
|
| 64 |
+
- Upload a UI design image (for multimodal models)
|
| 65 |
+
- Upload a reference file (PDF, TXT, MD, CSV, DOCX, or image)
|
| 66 |
+
- Enter a website URL for redesign (the app will extract and analyze the HTML and content)
|
| 67 |
+
- Enable web search for up-to-date information
|
| 68 |
+
- Choose a different AI model or code language
|
| 69 |
+
4. Click "Generate" to create your code
|
| 70 |
+
5. View the generated code in the Code tab or see it in action in the Preview tab
|
| 71 |
+
6. Use the History tab to review previous generations
|
| 72 |
+
7. **Deploy to Space**: Enter a title and click "π Deploy App" to publish your application (OAuth login required) - the SDK is automatically matched to your selected code language
|
| 73 |
+
|
| 74 |
+
## Supported Models
|
| 75 |
+
|
| 76 |
+
- Moonshot Kimi-K2
|
| 77 |
+
- Kimi K2 Turbo (Preview)
|
| 78 |
+
- Kimi K2 Thinking
|
| 79 |
+
- DeepSeek V3
|
| 80 |
+
- DeepSeek V3.1
|
| 81 |
+
- DeepSeek V3.1 Terminus
|
| 82 |
+
- DeepSeek V3.2-Exp
|
| 83 |
+
- DeepSeek R1
|
| 84 |
+
- MiniMax M2
|
| 85 |
+
- Qwen3-235B-A22B
|
| 86 |
+
- Qwen3-4B-Instruct-2507
|
| 87 |
+
- Qwen3-4B-Thinking-2507
|
| 88 |
+
- Qwen3-30B-A3B-Instruct-2507 (via DashScope)
|
| 89 |
+
- Qwen3-30B-A3B-Thinking-2507 (via DashScope)
|
| 90 |
+
- GPT-5 (via Poe)
|
| 91 |
+
- Grok-4 (via Poe)
|
| 92 |
+
- Claude-Opus-4.1 (via Poe)
|
| 93 |
+
- Gemini 2.5 Flash (OpenAI-compatible)
|
| 94 |
+
- Gemini 2.5 Pro (OpenAI-compatible)
|
| 95 |
+
|
| 96 |
+
## Input Options
|
| 97 |
+
|
| 98 |
+
- **Text Prompt**: Describe your app or code requirements
|
| 99 |
+
- **Image Upload**: For multimodal models, upload a UI design image to generate code from visuals
|
| 100 |
+
- **File Upload**: Provide a reference file (PDF, TXT, MD, CSV, DOCX, or image) for code generation or text extraction (OCR for images)
|
| 101 |
+
- **Website URL**: Enter a URL to extract and redesign the website (HTML and content are analyzed and modernized)
|
| 102 |
+
|
| 103 |
+
## Code Generation & Modification
|
| 104 |
+
|
| 105 |
+
- Generates code in HTML, Python, JS, and more (selectable via dropdown)
|
| 106 |
+
- Special support for transformers.js apps (outputs index.html, index.js, style.css)
|
| 107 |
+
- Svelte apps
|
| 108 |
+
- For HTML, provides a live preview in a sandboxed iframe
|
| 109 |
+
- For modification requests, uses a search/replace block format to update existing HTML
|
| 110 |
+
|
| 111 |
+
## Deployment
|
| 112 |
+
|
| 113 |
+
- Deploy generated apps to Hugging Face Spaces directly from the UI
|
| 114 |
+
- Supported SDKs: Gradio (Python), Streamlit (Python), Static (HTML), Transformers.js
|
| 115 |
+
- OAuth login with Hugging Face is required for deployment to user-owned Spaces
|
| 116 |
+
|
| 117 |
+
## History & Examples
|
| 118 |
+
|
| 119 |
+
- Maintains a chat-like history of user/assistant interactions
|
| 120 |
+
- Quick example prompts are available in the sidebar for fast prototyping
|
| 121 |
+
|
| 122 |
+
## UI/UX
|
| 123 |
+
|
| 124 |
+
- Built with Gradio 5.x, using only Gradio's built-in theming and styling (no custom CSS)
|
| 125 |
+
- Minimal, uncluttered sidebar and interface
|
| 126 |
+
|
| 127 |
+
## Environment Variables
|
| 128 |
+
|
| 129 |
+
- `HF_TOKEN`: Your Hugging Face API token (required)
|
| 130 |
+
- `GEMINI_API_KEY`: Your Google Gemini API key (required to use Gemini models)
|
| 131 |
+
- `MOONSHOT_API_KEY`: Your Moonshot AI API key (required to use Kimi models)
|
| 132 |
+
- `MINIMAX_API_KEY`: Your MiniMax API key (required to use MiniMax M2 model)
|
| 133 |
+
|
| 134 |
+
## Project Structure
|
| 135 |
+
|
| 136 |
+
```
|
| 137 |
+
anycoder/
|
| 138 |
+
βββ app.py # Main application (all logic and UI)
|
| 139 |
+
βββ requirements.txt
|
| 140 |
+
βββ README.md # This file
|
| 141 |
+
```
|
| 142 |
+
|
| 143 |
+
## License
|
| 144 |
+
|
| 145 |
+
[Add your license information here]
|
backend_api.py
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FastAPI backend for AnyCoder - provides REST API endpoints
|
| 3 |
+
"""
|
| 4 |
+
from fastapi import FastAPI, HTTPException, Header, WebSocket, WebSocketDisconnect
|
| 5 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 6 |
+
from fastapi.responses import StreamingResponse
|
| 7 |
+
from pydantic import BaseModel
|
| 8 |
+
from typing import Optional, List, Dict, AsyncGenerator
|
| 9 |
+
import json
|
| 10 |
+
import asyncio
|
| 11 |
+
from datetime import datetime
|
| 12 |
+
|
| 13 |
+
# Import only what we need, avoiding Gradio UI imports
|
| 14 |
+
import sys
|
| 15 |
+
import os
|
| 16 |
+
from huggingface_hub import InferenceClient
|
| 17 |
+
|
| 18 |
+
# Define models and languages here to avoid importing Gradio UI
|
| 19 |
+
AVAILABLE_MODELS = [
|
| 20 |
+
{"name": "Sherlock Dash Alpha", "id": "openrouter/sherlock-dash-alpha", "description": "Sherlock Dash Alpha model via OpenRouter"},
|
| 21 |
+
{"name": "MiniMax M2", "id": "MiniMaxAI/MiniMax-M2", "description": "MiniMax M2 model via HuggingFace InferenceClient with Novita provider"},
|
| 22 |
+
{"name": "DeepSeek V3.2-Exp", "id": "deepseek-ai/DeepSeek-V3.2-Exp", "description": "DeepSeek V3.2 Experimental via HuggingFace"},
|
| 23 |
+
{"name": "DeepSeek R1", "id": "deepseek-ai/DeepSeek-R1-0528", "description": "DeepSeek R1 model for code generation"},
|
| 24 |
+
{"name": "GPT-5", "id": "gpt-5", "description": "OpenAI GPT-5 via OpenRouter"},
|
| 25 |
+
{"name": "Gemini Flash Latest", "id": "gemini-flash-latest", "description": "Google Gemini Flash via OpenRouter"},
|
| 26 |
+
{"name": "Qwen3 Max Preview", "id": "qwen3-max-preview", "description": "Qwen3 Max Preview via DashScope API"},
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
LANGUAGE_CHOICES = ["html", "gradio", "transformers.js", "streamlit", "comfyui", "react"]
|
| 30 |
+
|
| 31 |
+
app = FastAPI(title="AnyCoder API", version="1.0.0")
|
| 32 |
+
|
| 33 |
+
# Configure CORS
|
| 34 |
+
app.add_middleware(
|
| 35 |
+
CORSMiddleware,
|
| 36 |
+
allow_origins=["http://localhost:3000", "http://localhost:3001"], # Frontend URLs
|
| 37 |
+
allow_credentials=True,
|
| 38 |
+
allow_methods=["*"],
|
| 39 |
+
allow_headers=["*"],
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# Pydantic models for request/response
|
| 44 |
+
class CodeGenerationRequest(BaseModel):
|
| 45 |
+
query: str
|
| 46 |
+
language: str = "html"
|
| 47 |
+
model_id: str = "openrouter/sherlock-dash-alpha"
|
| 48 |
+
provider: str = "auto"
|
| 49 |
+
history: List[List[str]] = []
|
| 50 |
+
agent_mode: bool = False
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class DeploymentRequest(BaseModel):
|
| 54 |
+
code: str
|
| 55 |
+
space_name: str
|
| 56 |
+
language: str
|
| 57 |
+
requirements: Optional[str] = None
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class AuthStatus(BaseModel):
|
| 61 |
+
authenticated: bool
|
| 62 |
+
username: Optional[str] = None
|
| 63 |
+
message: str
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class ModelInfo(BaseModel):
|
| 67 |
+
name: str
|
| 68 |
+
id: str
|
| 69 |
+
description: str
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class CodeGenerationResponse(BaseModel):
|
| 73 |
+
code: str
|
| 74 |
+
history: List[List[str]]
|
| 75 |
+
status: str
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
# Mock authentication for development
|
| 79 |
+
# In production, integrate with HuggingFace OAuth
|
| 80 |
+
class MockAuth:
|
| 81 |
+
def __init__(self, token: Optional[str] = None):
|
| 82 |
+
self.token = token
|
| 83 |
+
# Extract username from dev token or use generic name
|
| 84 |
+
if token and token.startswith("dev_token_"):
|
| 85 |
+
# Extract username from dev token format: dev_token_<username>_<timestamp>
|
| 86 |
+
parts = token.split("_")
|
| 87 |
+
self.username = parts[2] if len(parts) > 2 else "user"
|
| 88 |
+
else:
|
| 89 |
+
self.username = "user" if token else None
|
| 90 |
+
|
| 91 |
+
def is_authenticated(self):
|
| 92 |
+
# Accept any token (for dev mode)
|
| 93 |
+
return bool(self.token)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def get_auth_from_header(authorization: Optional[str] = None):
|
| 97 |
+
"""Extract authentication from header"""
|
| 98 |
+
if not authorization:
|
| 99 |
+
return MockAuth(None)
|
| 100 |
+
|
| 101 |
+
# Handle "Bearer " prefix
|
| 102 |
+
if authorization.startswith("Bearer "):
|
| 103 |
+
token = authorization.replace("Bearer ", "")
|
| 104 |
+
else:
|
| 105 |
+
token = authorization
|
| 106 |
+
|
| 107 |
+
return MockAuth(token)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
@app.get("/")
|
| 111 |
+
async def root():
|
| 112 |
+
"""Health check endpoint"""
|
| 113 |
+
return {"status": "ok", "message": "AnyCoder API is running"}
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
@app.get("/api/models", response_model=List[ModelInfo])
|
| 117 |
+
async def get_models():
|
| 118 |
+
"""Get available AI models"""
|
| 119 |
+
return [
|
| 120 |
+
ModelInfo(
|
| 121 |
+
name=model["name"],
|
| 122 |
+
id=model["id"],
|
| 123 |
+
description=model["description"]
|
| 124 |
+
)
|
| 125 |
+
for model in AVAILABLE_MODELS
|
| 126 |
+
]
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
@app.get("/api/languages")
|
| 130 |
+
async def get_languages():
|
| 131 |
+
"""Get available programming languages/frameworks"""
|
| 132 |
+
return {"languages": LANGUAGE_CHOICES}
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
@app.get("/api/auth/status")
|
| 136 |
+
async def auth_status(authorization: Optional[str] = Header(None)):
|
| 137 |
+
"""Check authentication status"""
|
| 138 |
+
auth = get_auth_from_header(authorization)
|
| 139 |
+
if auth.is_authenticated():
|
| 140 |
+
return AuthStatus(
|
| 141 |
+
authenticated=True,
|
| 142 |
+
username=auth.username,
|
| 143 |
+
message=f"Authenticated as {auth.username}"
|
| 144 |
+
)
|
| 145 |
+
return AuthStatus(
|
| 146 |
+
authenticated=False,
|
| 147 |
+
username=None,
|
| 148 |
+
message="Not authenticated"
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
@app.get("/api/generate")
|
| 153 |
+
async def generate_code(
|
| 154 |
+
query: str,
|
| 155 |
+
language: str = "html",
|
| 156 |
+
model_id: str = "openrouter/sherlock-dash-alpha",
|
| 157 |
+
provider: str = "auto",
|
| 158 |
+
authorization: Optional[str] = Header(None)
|
| 159 |
+
):
|
| 160 |
+
"""Generate code based on user query - returns streaming response"""
|
| 161 |
+
# Dev mode: No authentication required - just use server's HF_TOKEN
|
| 162 |
+
# In production, you would check real OAuth tokens here
|
| 163 |
+
|
| 164 |
+
async def event_stream() -> AsyncGenerator[str, None]:
|
| 165 |
+
"""Stream generated code chunks"""
|
| 166 |
+
try:
|
| 167 |
+
# Find the selected model
|
| 168 |
+
selected_model = None
|
| 169 |
+
for model in AVAILABLE_MODELS:
|
| 170 |
+
if model["id"] == model_id:
|
| 171 |
+
selected_model = model
|
| 172 |
+
break
|
| 173 |
+
|
| 174 |
+
if not selected_model:
|
| 175 |
+
selected_model = AVAILABLE_MODELS[0]
|
| 176 |
+
|
| 177 |
+
# Track generated code
|
| 178 |
+
generated_code = ""
|
| 179 |
+
|
| 180 |
+
# Use a simple system prompt
|
| 181 |
+
system_prompt = "You are a helpful AI assistant that generates code based on user requirements. Generate clean, well-commented code."
|
| 182 |
+
|
| 183 |
+
# Get the real model ID
|
| 184 |
+
actual_model_id = selected_model["id"]
|
| 185 |
+
|
| 186 |
+
# Determine which provider/API to use based on model ID
|
| 187 |
+
if actual_model_id.startswith("openrouter/"):
|
| 188 |
+
# OpenRouter models - use via OpenAI-compatible API
|
| 189 |
+
api_key = os.getenv("OPENROUTER_API_KEY") or os.getenv("HF_TOKEN")
|
| 190 |
+
client = InferenceClient(api_key=api_key, provider="openai", base_url="https://openrouter.ai/api/v1")
|
| 191 |
+
# Keep the model_id as-is for OpenRouter
|
| 192 |
+
elif actual_model_id == "MiniMaxAI/MiniMax-M2":
|
| 193 |
+
# MiniMax M2 via HuggingFace with Novita provider
|
| 194 |
+
hf_token = os.getenv("HF_TOKEN")
|
| 195 |
+
if not hf_token:
|
| 196 |
+
error_data = json.dumps({
|
| 197 |
+
"type": "error",
|
| 198 |
+
"message": "HF_TOKEN environment variable not set. Please set it in your terminal.",
|
| 199 |
+
"timestamp": datetime.now().isoformat()
|
| 200 |
+
})
|
| 201 |
+
yield f"data: {error_data}\n\n"
|
| 202 |
+
return
|
| 203 |
+
|
| 204 |
+
# Use OpenAI client with HuggingFace router
|
| 205 |
+
from openai import OpenAI
|
| 206 |
+
client = OpenAI(
|
| 207 |
+
base_url="https://router.huggingface.co/v1",
|
| 208 |
+
api_key=hf_token,
|
| 209 |
+
default_headers={
|
| 210 |
+
"X-HF-Bill-To": "huggingface"
|
| 211 |
+
}
|
| 212 |
+
)
|
| 213 |
+
# Add :novita suffix for the API call
|
| 214 |
+
actual_model_id = "MiniMaxAI/MiniMax-M2:novita"
|
| 215 |
+
elif actual_model_id.startswith("deepseek-ai/"):
|
| 216 |
+
# DeepSeek models via HuggingFace
|
| 217 |
+
client = InferenceClient(token=os.getenv("HF_TOKEN"))
|
| 218 |
+
elif actual_model_id == "qwen3-max-preview":
|
| 219 |
+
# Qwen via DashScope (would need separate implementation)
|
| 220 |
+
# For now, fall back to HF
|
| 221 |
+
client = InferenceClient(token=os.getenv("HF_TOKEN"))
|
| 222 |
+
else:
|
| 223 |
+
# Default: HuggingFace models
|
| 224 |
+
client = InferenceClient(token=os.getenv("HF_TOKEN"))
|
| 225 |
+
|
| 226 |
+
# Prepare messages
|
| 227 |
+
messages = [
|
| 228 |
+
{"role": "system", "content": system_prompt},
|
| 229 |
+
{"role": "user", "content": f"Generate a {language} application: {query}"}
|
| 230 |
+
]
|
| 231 |
+
|
| 232 |
+
# Stream the response
|
| 233 |
+
try:
|
| 234 |
+
stream = client.chat.completions.create(
|
| 235 |
+
model=actual_model_id,
|
| 236 |
+
messages=messages,
|
| 237 |
+
temperature=0.7,
|
| 238 |
+
max_tokens=10000,
|
| 239 |
+
stream=True
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
for chunk in stream:
|
| 243 |
+
# Check if choices array has elements before accessing
|
| 244 |
+
if (hasattr(chunk, 'choices') and
|
| 245 |
+
chunk.choices and
|
| 246 |
+
len(chunk.choices) > 0 and
|
| 247 |
+
hasattr(chunk.choices[0], 'delta') and
|
| 248 |
+
hasattr(chunk.choices[0].delta, 'content') and
|
| 249 |
+
chunk.choices[0].delta.content):
|
| 250 |
+
content = chunk.choices[0].delta.content
|
| 251 |
+
generated_code += content
|
| 252 |
+
|
| 253 |
+
# Send chunk as Server-Sent Event
|
| 254 |
+
event_data = json.dumps({
|
| 255 |
+
"type": "chunk",
|
| 256 |
+
"content": content,
|
| 257 |
+
"timestamp": datetime.now().isoformat()
|
| 258 |
+
})
|
| 259 |
+
yield f"data: {event_data}\n\n"
|
| 260 |
+
await asyncio.sleep(0) # Allow other tasks to run
|
| 261 |
+
|
| 262 |
+
# Send completion event
|
| 263 |
+
completion_data = json.dumps({
|
| 264 |
+
"type": "complete",
|
| 265 |
+
"code": generated_code,
|
| 266 |
+
"timestamp": datetime.now().isoformat()
|
| 267 |
+
})
|
| 268 |
+
yield f"data: {completion_data}\n\n"
|
| 269 |
+
|
| 270 |
+
except Exception as e:
|
| 271 |
+
error_data = json.dumps({
|
| 272 |
+
"type": "error",
|
| 273 |
+
"message": str(e),
|
| 274 |
+
"timestamp": datetime.now().isoformat()
|
| 275 |
+
})
|
| 276 |
+
yield f"data: {error_data}\n\n"
|
| 277 |
+
|
| 278 |
+
except Exception as e:
|
| 279 |
+
error_data = json.dumps({
|
| 280 |
+
"type": "error",
|
| 281 |
+
"message": f"Generation error: {str(e)}",
|
| 282 |
+
"timestamp": datetime.now().isoformat()
|
| 283 |
+
})
|
| 284 |
+
yield f"data: {error_data}\n\n"
|
| 285 |
+
|
| 286 |
+
return StreamingResponse(
|
| 287 |
+
event_stream(),
|
| 288 |
+
media_type="text/event-stream",
|
| 289 |
+
headers={
|
| 290 |
+
"Cache-Control": "no-cache",
|
| 291 |
+
"Connection": "keep-alive",
|
| 292 |
+
"X-Accel-Buffering": "no"
|
| 293 |
+
}
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
@app.post("/api/deploy")
|
| 298 |
+
async def deploy(
|
| 299 |
+
request: DeploymentRequest,
|
| 300 |
+
authorization: Optional[str] = Header(None)
|
| 301 |
+
):
|
| 302 |
+
"""Deploy generated code to HuggingFace Spaces"""
|
| 303 |
+
auth = get_auth_from_header(authorization)
|
| 304 |
+
|
| 305 |
+
if not auth.is_authenticated():
|
| 306 |
+
raise HTTPException(status_code=401, detail="Authentication required")
|
| 307 |
+
|
| 308 |
+
# Check if this is dev mode (no real token)
|
| 309 |
+
if auth.token and auth.token.startswith("dev_token_"):
|
| 310 |
+
# In dev mode, open HF Spaces creation page
|
| 311 |
+
import urllib.parse
|
| 312 |
+
base_url = "https://huggingface.co/new-space"
|
| 313 |
+
|
| 314 |
+
# Map language to SDK
|
| 315 |
+
language_to_sdk = {
|
| 316 |
+
"gradio": "gradio",
|
| 317 |
+
"streamlit": "docker",
|
| 318 |
+
"react": "docker",
|
| 319 |
+
"html": "static",
|
| 320 |
+
"transformers.js": "static",
|
| 321 |
+
"comfyui": "static"
|
| 322 |
+
}
|
| 323 |
+
sdk = language_to_sdk.get(request.language, "gradio")
|
| 324 |
+
|
| 325 |
+
params = urllib.parse.urlencode({
|
| 326 |
+
"name": request.space_name or "my-anycoder-app",
|
| 327 |
+
"sdk": sdk
|
| 328 |
+
})
|
| 329 |
+
|
| 330 |
+
# Prepare file content based on language
|
| 331 |
+
if request.language in ["html", "transformers.js", "comfyui"]:
|
| 332 |
+
file_path = "index.html"
|
| 333 |
+
else:
|
| 334 |
+
file_path = "app.py"
|
| 335 |
+
|
| 336 |
+
files_params = urllib.parse.urlencode({
|
| 337 |
+
"files[0][path]": file_path,
|
| 338 |
+
"files[0][content]": request.code
|
| 339 |
+
})
|
| 340 |
+
|
| 341 |
+
space_url = f"{base_url}?{params}&{files_params}"
|
| 342 |
+
|
| 343 |
+
return {
|
| 344 |
+
"success": True,
|
| 345 |
+
"space_url": space_url,
|
| 346 |
+
"message": "Dev mode: Please create the space manually",
|
| 347 |
+
"dev_mode": True
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
+
# Production mode with real token
|
| 351 |
+
try:
|
| 352 |
+
from huggingface_hub import HfApi
|
| 353 |
+
import tempfile
|
| 354 |
+
import uuid
|
| 355 |
+
|
| 356 |
+
# Get user token from header or use server token
|
| 357 |
+
user_token = auth.token if auth.token else os.getenv("HF_TOKEN")
|
| 358 |
+
|
| 359 |
+
if not user_token:
|
| 360 |
+
raise HTTPException(status_code=401, detail="No HuggingFace token available")
|
| 361 |
+
|
| 362 |
+
# Create API client
|
| 363 |
+
api = HfApi(token=user_token)
|
| 364 |
+
|
| 365 |
+
# Generate space name if not provided
|
| 366 |
+
space_name = request.space_name or f"anycoder-{uuid.uuid4().hex[:8]}"
|
| 367 |
+
repo_id = f"{auth.username}/{space_name}"
|
| 368 |
+
|
| 369 |
+
# Map language to SDK
|
| 370 |
+
language_to_sdk = {
|
| 371 |
+
"gradio": "gradio",
|
| 372 |
+
"streamlit": "docker",
|
| 373 |
+
"react": "docker",
|
| 374 |
+
"html": "static",
|
| 375 |
+
"transformers.js": "static",
|
| 376 |
+
"comfyui": "static"
|
| 377 |
+
}
|
| 378 |
+
sdk = language_to_sdk.get(request.language, "gradio")
|
| 379 |
+
|
| 380 |
+
# Create the space
|
| 381 |
+
try:
|
| 382 |
+
api.create_repo(
|
| 383 |
+
repo_id=repo_id,
|
| 384 |
+
repo_type="space",
|
| 385 |
+
space_sdk=sdk,
|
| 386 |
+
exist_ok=False
|
| 387 |
+
)
|
| 388 |
+
except Exception as e:
|
| 389 |
+
if "already exists" in str(e).lower():
|
| 390 |
+
# Space exists, we'll update it
|
| 391 |
+
pass
|
| 392 |
+
else:
|
| 393 |
+
raise
|
| 394 |
+
|
| 395 |
+
# Upload the code file
|
| 396 |
+
if request.language in ["html", "transformers.js", "comfyui"]:
|
| 397 |
+
file_name = "index.html"
|
| 398 |
+
else:
|
| 399 |
+
file_name = "app.py"
|
| 400 |
+
|
| 401 |
+
with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
|
| 402 |
+
f.write(request.code)
|
| 403 |
+
temp_path = f.name
|
| 404 |
+
|
| 405 |
+
try:
|
| 406 |
+
api.upload_file(
|
| 407 |
+
path_or_fileobj=temp_path,
|
| 408 |
+
path_in_repo=file_name,
|
| 409 |
+
repo_id=repo_id,
|
| 410 |
+
repo_type="space"
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
# For Gradio apps, also upload requirements.txt if needed
|
| 414 |
+
if request.language == "gradio":
|
| 415 |
+
# Simple requirements for basic Gradio app
|
| 416 |
+
requirements = "gradio>=4.0.0\n"
|
| 417 |
+
with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as req_f:
|
| 418 |
+
req_f.write(requirements)
|
| 419 |
+
req_temp_path = req_f.name
|
| 420 |
+
|
| 421 |
+
try:
|
| 422 |
+
api.upload_file(
|
| 423 |
+
path_or_fileobj=req_temp_path,
|
| 424 |
+
path_in_repo="requirements.txt",
|
| 425 |
+
repo_id=repo_id,
|
| 426 |
+
repo_type="space"
|
| 427 |
+
)
|
| 428 |
+
finally:
|
| 429 |
+
os.unlink(req_temp_path)
|
| 430 |
+
|
| 431 |
+
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 432 |
+
|
| 433 |
+
return {
|
| 434 |
+
"success": True,
|
| 435 |
+
"space_url": space_url,
|
| 436 |
+
"message": f"β
Deployed successfully to {repo_id}!"
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
finally:
|
| 440 |
+
os.unlink(temp_path)
|
| 441 |
+
|
| 442 |
+
except Exception as e:
|
| 443 |
+
raise HTTPException(status_code=500, detail=f"Deployment failed: {str(e)}")
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
@app.websocket("/ws/generate")
|
| 447 |
+
async def websocket_generate(websocket: WebSocket):
|
| 448 |
+
"""WebSocket endpoint for real-time code generation"""
|
| 449 |
+
await websocket.accept()
|
| 450 |
+
|
| 451 |
+
try:
|
| 452 |
+
while True:
|
| 453 |
+
# Receive message from client
|
| 454 |
+
data = await websocket.receive_json()
|
| 455 |
+
|
| 456 |
+
query = data.get("query")
|
| 457 |
+
language = data.get("language", "html")
|
| 458 |
+
model_id = data.get("model_id", "openrouter/sherlock-dash-alpha")
|
| 459 |
+
|
| 460 |
+
# Send acknowledgment
|
| 461 |
+
await websocket.send_json({
|
| 462 |
+
"type": "status",
|
| 463 |
+
"message": "Generating code..."
|
| 464 |
+
})
|
| 465 |
+
|
| 466 |
+
# Mock code generation for now
|
| 467 |
+
await asyncio.sleep(0.5)
|
| 468 |
+
|
| 469 |
+
# Send generated code in chunks
|
| 470 |
+
sample_code = f"<!-- Generated {language} code -->\n<h1>Hello from AnyCoder!</h1>"
|
| 471 |
+
|
| 472 |
+
for i, char in enumerate(sample_code):
|
| 473 |
+
await websocket.send_json({
|
| 474 |
+
"type": "chunk",
|
| 475 |
+
"content": char,
|
| 476 |
+
"progress": (i + 1) / len(sample_code) * 100
|
| 477 |
+
})
|
| 478 |
+
await asyncio.sleep(0.01)
|
| 479 |
+
|
| 480 |
+
# Send completion
|
| 481 |
+
await websocket.send_json({
|
| 482 |
+
"type": "complete",
|
| 483 |
+
"code": sample_code
|
| 484 |
+
})
|
| 485 |
+
|
| 486 |
+
except WebSocketDisconnect:
|
| 487 |
+
print("Client disconnected")
|
| 488 |
+
except Exception as e:
|
| 489 |
+
await websocket.send_json({
|
| 490 |
+
"type": "error",
|
| 491 |
+
"message": str(e)
|
| 492 |
+
})
|
| 493 |
+
await websocket.close()
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
if __name__ == "__main__":
|
| 497 |
+
import uvicorn
|
| 498 |
+
uvicorn.run("backend_api:app", host="0.0.0.0", port=8000, reload=True)
|
| 499 |
+
|
frontend/.gitignore
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dependencies
|
| 2 |
+
/node_modules
|
| 3 |
+
/.pnp
|
| 4 |
+
.pnp.js
|
| 5 |
+
|
| 6 |
+
# testing
|
| 7 |
+
/coverage
|
| 8 |
+
|
| 9 |
+
# next.js
|
| 10 |
+
/.next/
|
| 11 |
+
/out/
|
| 12 |
+
|
| 13 |
+
# production
|
| 14 |
+
/build
|
| 15 |
+
|
| 16 |
+
# misc
|
| 17 |
+
.DS_Store
|
| 18 |
+
*.pem
|
| 19 |
+
|
| 20 |
+
# debug
|
| 21 |
+
npm-debug.log*
|
| 22 |
+
yarn-debug.log*
|
| 23 |
+
yarn-error.log*
|
| 24 |
+
|
| 25 |
+
# local env files
|
| 26 |
+
.env*.local
|
| 27 |
+
.env
|
| 28 |
+
|
| 29 |
+
# vercel
|
| 30 |
+
.vercel
|
| 31 |
+
|
| 32 |
+
# typescript
|
| 33 |
+
*.tsbuildinfo
|
| 34 |
+
next-env.d.ts
|
| 35 |
+
|
frontend/next.config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** @type {import('next').NextConfig} */
|
| 2 |
+
const nextConfig = {
|
| 3 |
+
output: 'standalone',
|
| 4 |
+
reactStrictMode: true,
|
| 5 |
+
env: {
|
| 6 |
+
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
|
| 7 |
+
},
|
| 8 |
+
async rewrites() {
|
| 9 |
+
// In Docker Space, proxy /api/* requests to backend on port 8000
|
| 10 |
+
// This allows frontend (7860) to communicate with backend (8000) internally
|
| 11 |
+
const API_HOST = process.env.BACKEND_HOST || 'http://localhost:8000';
|
| 12 |
+
return [
|
| 13 |
+
{
|
| 14 |
+
source: '/api/:path*',
|
| 15 |
+
destination: `${API_HOST}/api/:path*`,
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
source: '/ws/:path*',
|
| 19 |
+
destination: `${API_HOST}/ws/:path*`,
|
| 20 |
+
},
|
| 21 |
+
];
|
| 22 |
+
},
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
module.exports = nextConfig
|
| 26 |
+
|
frontend/package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "anycoder-frontend",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "next dev",
|
| 7 |
+
"build": "next build",
|
| 8 |
+
"start": "next start -p 7860",
|
| 9 |
+
"lint": "next lint"
|
| 10 |
+
},
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"next": "14.2.5",
|
| 13 |
+
"react": "^18.3.1",
|
| 14 |
+
"react-dom": "^18.3.1",
|
| 15 |
+
"axios": "^1.7.2",
|
| 16 |
+
"@monaco-editor/react": "^4.6.0",
|
| 17 |
+
"@huggingface/hub": "^0.15.1",
|
| 18 |
+
"prismjs": "^1.29.0",
|
| 19 |
+
"react-markdown": "^9.0.1",
|
| 20 |
+
"remark-gfm": "^4.0.0"
|
| 21 |
+
},
|
| 22 |
+
"devDependencies": {
|
| 23 |
+
"@types/node": "^20",
|
| 24 |
+
"@types/react": "^18",
|
| 25 |
+
"@types/react-dom": "^18",
|
| 26 |
+
"@types/prismjs": "^1.26.4",
|
| 27 |
+
"typescript": "^5",
|
| 28 |
+
"eslint": "^8",
|
| 29 |
+
"eslint-config-next": "14.2.5",
|
| 30 |
+
"autoprefixer": "^10.4.19",
|
| 31 |
+
"postcss": "^8.4.39",
|
| 32 |
+
"tailwindcss": "^3.4.4"
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
frontend/postcss.config.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module.exports = {
|
| 2 |
+
plugins: {
|
| 3 |
+
tailwindcss: {},
|
| 4 |
+
autoprefixer: {},
|
| 5 |
+
},
|
| 6 |
+
}
|
| 7 |
+
|
frontend/src/app/globals.css
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@tailwind base;
|
| 2 |
+
@tailwind components;
|
| 3 |
+
@tailwind utilities;
|
| 4 |
+
|
| 5 |
+
* {
|
| 6 |
+
box-sizing: border-box;
|
| 7 |
+
padding: 0;
|
| 8 |
+
margin: 0;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
html,
|
| 12 |
+
body {
|
| 13 |
+
height: 100%;
|
| 14 |
+
overflow: hidden;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
body {
|
| 18 |
+
color: #e5e5e7;
|
| 19 |
+
background: #1d1d1f;
|
| 20 |
+
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
|
| 21 |
+
-webkit-font-smoothing: antialiased;
|
| 22 |
+
-moz-osx-font-smoothing: grayscale;
|
| 23 |
+
letter-spacing: -0.01em;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
/* Apple-style scrollbar */
|
| 27 |
+
::-webkit-scrollbar {
|
| 28 |
+
width: 12px;
|
| 29 |
+
height: 12px;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
::-webkit-scrollbar-track {
|
| 33 |
+
background: transparent;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
::-webkit-scrollbar-thumb {
|
| 37 |
+
background: rgba(255, 255, 255, 0.2);
|
| 38 |
+
border-radius: 10px;
|
| 39 |
+
border: 3px solid #1d1d1f;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
::-webkit-scrollbar-thumb:hover {
|
| 43 |
+
background: rgba(255, 255, 255, 0.3);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
::-webkit-scrollbar-corner {
|
| 47 |
+
background: #1d1d1f;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/* Markdown styling in chat - VS Code style */
|
| 51 |
+
.prose {
|
| 52 |
+
max-width: none;
|
| 53 |
+
color: #cccccc;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
.prose code {
|
| 57 |
+
background-color: #2d2d30;
|
| 58 |
+
color: #d4d4d4;
|
| 59 |
+
padding: 0.2em 0.4em;
|
| 60 |
+
border-radius: 3px;
|
| 61 |
+
font-size: 0.875em;
|
| 62 |
+
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Courier New', monospace;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.prose pre {
|
| 66 |
+
background-color: #1e1e1e;
|
| 67 |
+
padding: 1em;
|
| 68 |
+
border-radius: 4px;
|
| 69 |
+
overflow-x: auto;
|
| 70 |
+
border: 1px solid #3e3e42;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.prose pre code {
|
| 74 |
+
background-color: transparent;
|
| 75 |
+
padding: 0;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.prose p {
|
| 79 |
+
margin: 0.5em 0;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.prose a {
|
| 83 |
+
color: #3794ff;
|
| 84 |
+
text-decoration: none;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
.prose a:hover {
|
| 88 |
+
text-decoration: underline;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
/* Selection color - Apple style */
|
| 92 |
+
::selection {
|
| 93 |
+
background-color: rgba(0, 122, 255, 0.3);
|
| 94 |
+
color: #ffffff;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
::-moz-selection {
|
| 98 |
+
background-color: rgba(0, 122, 255, 0.3);
|
| 99 |
+
color: #ffffff;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
/* Apple-style focus rings */
|
| 103 |
+
button:focus-visible,
|
| 104 |
+
input:focus-visible,
|
| 105 |
+
select:focus-visible {
|
| 106 |
+
outline: 2px solid rgba(0, 122, 255, 0.6);
|
| 107 |
+
outline-offset: 2px;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/* Smooth transitions */
|
| 111 |
+
* {
|
| 112 |
+
transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
|
| 113 |
+
}
|
| 114 |
+
|
frontend/src/app/layout.tsx
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { Metadata } from 'next';
|
| 2 |
+
import { Inter } from 'next/font/google';
|
| 3 |
+
import './globals.css';
|
| 4 |
+
|
| 5 |
+
const inter = Inter({ subsets: ['latin'] });
|
| 6 |
+
|
| 7 |
+
export const metadata: Metadata = {
|
| 8 |
+
title: 'AnyCoder - AI Code Generator',
|
| 9 |
+
description: 'Generate code with AI - supports HTML, Gradio, React, Streamlit, and more',
|
| 10 |
+
};
|
| 11 |
+
|
| 12 |
+
export default function RootLayout({
|
| 13 |
+
children,
|
| 14 |
+
}: {
|
| 15 |
+
children: React.ReactNode;
|
| 16 |
+
}) {
|
| 17 |
+
return (
|
| 18 |
+
<html lang="en">
|
| 19 |
+
<body className={inter.className}>{children}</body>
|
| 20 |
+
</html>
|
| 21 |
+
);
|
| 22 |
+
}
|
| 23 |
+
|
frontend/src/app/page.tsx
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { useState, useEffect } from 'react';
|
| 4 |
+
import Header from '@/components/Header';
|
| 5 |
+
import ChatInterface from '@/components/ChatInterface';
|
| 6 |
+
import CodeEditor from '@/components/CodeEditor';
|
| 7 |
+
import ControlPanel from '@/components/ControlPanel';
|
| 8 |
+
import { apiClient } from '@/lib/api';
|
| 9 |
+
import { isAuthenticated as checkIsAuthenticated, getStoredToken } from '@/lib/auth';
|
| 10 |
+
import type { Message, Language, CodeGenerationRequest } from '@/types';
|
| 11 |
+
|
| 12 |
+
export default function Home() {
|
| 13 |
+
const [messages, setMessages] = useState<Message[]>([]);
|
| 14 |
+
const [generatedCode, setGeneratedCode] = useState('');
|
| 15 |
+
const [selectedLanguage, setSelectedLanguage] = useState<Language>('html');
|
| 16 |
+
const [selectedModel, setSelectedModel] = useState('openrouter/sherlock-dash-alpha');
|
| 17 |
+
const [isGenerating, setIsGenerating] = useState(false);
|
| 18 |
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
| 19 |
+
|
| 20 |
+
useEffect(() => {
|
| 21 |
+
checkAuth();
|
| 22 |
+
// Check auth status every second to catch OAuth redirects
|
| 23 |
+
const interval = setInterval(checkAuth, 1000);
|
| 24 |
+
return () => clearInterval(interval);
|
| 25 |
+
}, []);
|
| 26 |
+
|
| 27 |
+
const checkAuth = () => {
|
| 28 |
+
const authenticated = checkIsAuthenticated();
|
| 29 |
+
setIsAuthenticated(authenticated);
|
| 30 |
+
|
| 31 |
+
// Make sure API client has the token
|
| 32 |
+
if (authenticated) {
|
| 33 |
+
const token = getStoredToken();
|
| 34 |
+
if (token) {
|
| 35 |
+
apiClient.setToken(token);
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
const handleSendMessage = async (message: string) => {
|
| 41 |
+
if (!isAuthenticated) {
|
| 42 |
+
alert('Please sign in with HuggingFace first! Click the "Sign in with Hugging Face" button in the header.');
|
| 43 |
+
return;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
// Add user message
|
| 47 |
+
const userMessage: Message = {
|
| 48 |
+
role: 'user',
|
| 49 |
+
content: message,
|
| 50 |
+
timestamp: new Date().toISOString(),
|
| 51 |
+
};
|
| 52 |
+
setMessages((prev) => [...prev, userMessage]);
|
| 53 |
+
setIsGenerating(true);
|
| 54 |
+
|
| 55 |
+
// Prepare request
|
| 56 |
+
const request: CodeGenerationRequest = {
|
| 57 |
+
query: message,
|
| 58 |
+
language: selectedLanguage,
|
| 59 |
+
model_id: selectedModel,
|
| 60 |
+
provider: 'auto',
|
| 61 |
+
history: messages.map((m) => [m.role, m.content]),
|
| 62 |
+
agent_mode: false,
|
| 63 |
+
};
|
| 64 |
+
|
| 65 |
+
let generatedCodeBuffer = '';
|
| 66 |
+
const assistantMessage: Message = {
|
| 67 |
+
role: 'assistant',
|
| 68 |
+
content: 'β³ Generating code...',
|
| 69 |
+
timestamp: new Date().toISOString(),
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
// Add placeholder for assistant message
|
| 73 |
+
setMessages((prev) => [...prev, assistantMessage]);
|
| 74 |
+
|
| 75 |
+
// Stream the response
|
| 76 |
+
try {
|
| 77 |
+
apiClient.generateCodeStream(
|
| 78 |
+
request,
|
| 79 |
+
// onChunk - Update code editor in real-time, NOT the chat
|
| 80 |
+
(chunk: string) => {
|
| 81 |
+
generatedCodeBuffer += chunk;
|
| 82 |
+
setGeneratedCode(generatedCodeBuffer);
|
| 83 |
+
},
|
| 84 |
+
// onComplete
|
| 85 |
+
(code: string) => {
|
| 86 |
+
setGeneratedCode(code);
|
| 87 |
+
setIsGenerating(false);
|
| 88 |
+
|
| 89 |
+
// Update final message - just show success, not the code
|
| 90 |
+
setMessages((prev) => {
|
| 91 |
+
const newMessages = [...prev];
|
| 92 |
+
newMessages[newMessages.length - 1] = {
|
| 93 |
+
...assistantMessage,
|
| 94 |
+
content: 'β
Code generated successfully! Check the editor β',
|
| 95 |
+
};
|
| 96 |
+
return newMessages;
|
| 97 |
+
});
|
| 98 |
+
},
|
| 99 |
+
// onError
|
| 100 |
+
(error: string) => {
|
| 101 |
+
setIsGenerating(false);
|
| 102 |
+
setMessages((prev) => {
|
| 103 |
+
const newMessages = [...prev];
|
| 104 |
+
newMessages[newMessages.length - 1] = {
|
| 105 |
+
...assistantMessage,
|
| 106 |
+
content: `β Error: ${error}`,
|
| 107 |
+
};
|
| 108 |
+
return newMessages;
|
| 109 |
+
});
|
| 110 |
+
}
|
| 111 |
+
);
|
| 112 |
+
} catch (error) {
|
| 113 |
+
setIsGenerating(false);
|
| 114 |
+
setMessages((prev) => {
|
| 115 |
+
const newMessages = [...prev];
|
| 116 |
+
newMessages[newMessages.length - 1] = {
|
| 117 |
+
...assistantMessage,
|
| 118 |
+
content: `β Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
| 119 |
+
};
|
| 120 |
+
return newMessages;
|
| 121 |
+
});
|
| 122 |
+
}
|
| 123 |
+
};
|
| 124 |
+
|
| 125 |
+
const handleDeploy = async () => {
|
| 126 |
+
if (!generatedCode) {
|
| 127 |
+
alert('No code to deploy! Generate some code first.');
|
| 128 |
+
return;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
const spaceName = prompt('Enter HuggingFace Space name (or leave empty for auto-generated):');
|
| 132 |
+
if (spaceName === null) return; // User cancelled
|
| 133 |
+
|
| 134 |
+
try {
|
| 135 |
+
const response = await apiClient.deploy({
|
| 136 |
+
code: generatedCode,
|
| 137 |
+
space_name: spaceName || undefined,
|
| 138 |
+
language: selectedLanguage,
|
| 139 |
+
});
|
| 140 |
+
|
| 141 |
+
if (response.success) {
|
| 142 |
+
// Open the space URL in a new tab
|
| 143 |
+
window.open(response.space_url, '_blank');
|
| 144 |
+
|
| 145 |
+
// Show success message
|
| 146 |
+
const isDev = response.dev_mode;
|
| 147 |
+
const message = isDev
|
| 148 |
+
? 'π Opening HuggingFace Spaces creation page...\nPlease complete the space setup in the new tab.'
|
| 149 |
+
: `β
Deployed successfully!\n\nOpening: ${response.space_url}`;
|
| 150 |
+
alert(message);
|
| 151 |
+
} else {
|
| 152 |
+
alert(`Deployment failed: ${response.message}`);
|
| 153 |
+
}
|
| 154 |
+
} catch (error) {
|
| 155 |
+
alert(`Deployment error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
| 156 |
+
}
|
| 157 |
+
};
|
| 158 |
+
|
| 159 |
+
const handleClear = () => {
|
| 160 |
+
if (confirm('Clear all messages and code?')) {
|
| 161 |
+
setMessages([]);
|
| 162 |
+
setGeneratedCode('');
|
| 163 |
+
}
|
| 164 |
+
};
|
| 165 |
+
|
| 166 |
+
return (
|
| 167 |
+
<div className="h-screen flex flex-col bg-[#1d1d1f]">
|
| 168 |
+
<Header />
|
| 169 |
+
|
| 170 |
+
{/* VS Code layout with Apple styling */}
|
| 171 |
+
<main className="flex-1 flex overflow-hidden">
|
| 172 |
+
{/* Left Sidebar - Chat Panel */}
|
| 173 |
+
<div className="w-80 bg-[#28282a] border-r border-[#48484a] flex flex-col shadow-xl">
|
| 174 |
+
{/* Panel Header */}
|
| 175 |
+
<div className="flex items-center px-5 py-4 bg-[#28282a] border-b border-[#48484a]">
|
| 176 |
+
<div className="flex space-x-2">
|
| 177 |
+
<div className="w-3 h-3 rounded-full bg-[#ff5f57] shadow-sm"></div>
|
| 178 |
+
<div className="w-3 h-3 rounded-full bg-[#ffbd2e] shadow-sm"></div>
|
| 179 |
+
<div className="w-3 h-3 rounded-full bg-[#28ca41] shadow-sm"></div>
|
| 180 |
+
</div>
|
| 181 |
+
<span className="ml-4 text-sm font-semibold text-[#e5e5e7] tracking-tight">Chat</span>
|
| 182 |
+
</div>
|
| 183 |
+
|
| 184 |
+
{/* Chat Panel */}
|
| 185 |
+
<div className="flex-1 overflow-hidden">
|
| 186 |
+
<ChatInterface
|
| 187 |
+
messages={messages}
|
| 188 |
+
onSendMessage={handleSendMessage}
|
| 189 |
+
isGenerating={isGenerating}
|
| 190 |
+
isAuthenticated={isAuthenticated}
|
| 191 |
+
/>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
|
| 195 |
+
{/* Center - Editor Group */}
|
| 196 |
+
<div className="flex-1 flex flex-col bg-[#1d1d1f]">
|
| 197 |
+
{/* Tab Bar */}
|
| 198 |
+
<div className="flex items-center px-5 h-11 bg-[#28282a] border-b border-[#48484a]">
|
| 199 |
+
<div className="flex items-center space-x-2">
|
| 200 |
+
<div className="px-4 py-1.5 bg-[#1d1d1f] border-t-2 border-[#007aff] text-sm text-[#e5e5e7] rounded-t-lg shadow-sm font-medium">
|
| 201 |
+
{selectedLanguage}.{selectedLanguage === 'html' ? 'html' : selectedLanguage === 'python' ? 'py' : 'js'}
|
| 202 |
+
</div>
|
| 203 |
+
</div>
|
| 204 |
+
<div className="ml-auto flex items-center space-x-3 text-xs text-[#a1a1a6]">
|
| 205 |
+
{isGenerating && (
|
| 206 |
+
<span className="flex items-center space-x-1.5 animate-pulse">
|
| 207 |
+
<div className="w-2 h-2 bg-[#007aff] rounded-full shadow-lg"></div>
|
| 208 |
+
<span className="font-medium">Generating...</span>
|
| 209 |
+
</span>
|
| 210 |
+
)}
|
| 211 |
+
<span className="font-semibold tracking-wide">{selectedLanguage.toUpperCase()}</span>
|
| 212 |
+
</div>
|
| 213 |
+
</div>
|
| 214 |
+
|
| 215 |
+
{/* Editor */}
|
| 216 |
+
<div className="flex-1">
|
| 217 |
+
<CodeEditor
|
| 218 |
+
code={generatedCode || '// Your generated code will appear here...\n// Select a model and start chatting to generate code'}
|
| 219 |
+
language={selectedLanguage}
|
| 220 |
+
onChange={setGeneratedCode}
|
| 221 |
+
readOnly={isGenerating}
|
| 222 |
+
/>
|
| 223 |
+
</div>
|
| 224 |
+
</div>
|
| 225 |
+
|
| 226 |
+
{/* Right Sidebar - Configuration Panel */}
|
| 227 |
+
<div className="w-72 bg-[#28282a] border-l border-[#48484a] overflow-y-auto shadow-xl">
|
| 228 |
+
<ControlPanel
|
| 229 |
+
selectedLanguage={selectedLanguage}
|
| 230 |
+
selectedModel={selectedModel}
|
| 231 |
+
onLanguageChange={setSelectedLanguage}
|
| 232 |
+
onModelChange={setSelectedModel}
|
| 233 |
+
onDeploy={handleDeploy}
|
| 234 |
+
onClear={handleClear}
|
| 235 |
+
isGenerating={isGenerating}
|
| 236 |
+
/>
|
| 237 |
+
</div>
|
| 238 |
+
</main>
|
| 239 |
+
|
| 240 |
+
{/* Status Bar - Apple style */}
|
| 241 |
+
<footer className="h-7 bg-[#28282a] border-t border-[#48484a] text-[#a1a1a6] text-xs flex items-center px-5 justify-between font-medium">
|
| 242 |
+
<div className="flex items-center space-x-5">
|
| 243 |
+
<span className="flex items-center space-x-1.5">
|
| 244 |
+
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 16 16">
|
| 245 |
+
<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0zM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0z"/>
|
| 246 |
+
</svg>
|
| 247 |
+
<span>AnyCoder</span>
|
| 248 |
+
</span>
|
| 249 |
+
<span className="flex items-center space-x-1.5">
|
| 250 |
+
{isAuthenticated ? (
|
| 251 |
+
<>
|
| 252 |
+
<span className="w-1.5 h-1.5 bg-[#30d158] rounded-full"></span>
|
| 253 |
+
<span>Connected</span>
|
| 254 |
+
</>
|
| 255 |
+
) : (
|
| 256 |
+
<>
|
| 257 |
+
<span className="w-1.5 h-1.5 bg-[#ff9f0a] rounded-full"></span>
|
| 258 |
+
<span>Not authenticated</span>
|
| 259 |
+
</>
|
| 260 |
+
)}
|
| 261 |
+
</span>
|
| 262 |
+
</div>
|
| 263 |
+
<div className="flex items-center space-x-5">
|
| 264 |
+
<span>{messages.length} messages</span>
|
| 265 |
+
<a
|
| 266 |
+
href="https://huggingface.co/spaces/akhaliq/anycoder"
|
| 267 |
+
target="_blank"
|
| 268 |
+
rel="noopener noreferrer"
|
| 269 |
+
className="hover:text-[#e5e5e7] transition-colors"
|
| 270 |
+
>
|
| 271 |
+
Built with anycoder
|
| 272 |
+
</a>
|
| 273 |
+
</div>
|
| 274 |
+
</footer>
|
| 275 |
+
</div>
|
| 276 |
+
);
|
| 277 |
+
}
|
| 278 |
+
|
frontend/src/components/ChatInterface.tsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { useState, useRef, useEffect } from 'react';
|
| 4 |
+
import type { Message } from '@/types';
|
| 5 |
+
import ReactMarkdown from 'react-markdown';
|
| 6 |
+
import remarkGfm from 'remark-gfm';
|
| 7 |
+
|
| 8 |
+
interface ChatInterfaceProps {
|
| 9 |
+
messages: Message[];
|
| 10 |
+
onSendMessage: (message: string) => void;
|
| 11 |
+
isGenerating: boolean;
|
| 12 |
+
isAuthenticated?: boolean;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
export default function ChatInterface({ messages, onSendMessage, isGenerating, isAuthenticated = false }: ChatInterfaceProps) {
|
| 16 |
+
const [input, setInput] = useState('');
|
| 17 |
+
const messagesEndRef = useRef<HTMLDivElement>(null);
|
| 18 |
+
|
| 19 |
+
const scrollToBottom = () => {
|
| 20 |
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
useEffect(() => {
|
| 24 |
+
scrollToBottom();
|
| 25 |
+
}, [messages]);
|
| 26 |
+
|
| 27 |
+
const handleSubmit = (e: React.FormEvent) => {
|
| 28 |
+
e.preventDefault();
|
| 29 |
+
if (input.trim() && !isGenerating) {
|
| 30 |
+
onSendMessage(input);
|
| 31 |
+
setInput('');
|
| 32 |
+
}
|
| 33 |
+
};
|
| 34 |
+
|
| 35 |
+
return (
|
| 36 |
+
<div className="flex flex-col h-full bg-[#1d1d1f]">
|
| 37 |
+
{/* Messages */}
|
| 38 |
+
<div className="flex-1 overflow-y-auto p-5 space-y-4">
|
| 39 |
+
{messages.length === 0 ? (
|
| 40 |
+
<div className="text-center text-[#a1a1a6] mt-12">
|
| 41 |
+
<div className="text-5xl mb-5">π¬</div>
|
| 42 |
+
{isAuthenticated ? (
|
| 43 |
+
<>
|
| 44 |
+
<p className="text-lg font-semibold text-[#e5e5e7] tracking-tight">Start a conversation</p>
|
| 45 |
+
<p className="text-sm mt-3 text-[#86868b] leading-relaxed">Describe what you want to build and I'll generate the code</p>
|
| 46 |
+
</>
|
| 47 |
+
) : (
|
| 48 |
+
<>
|
| 49 |
+
<p className="text-lg font-semibold text-[#ff9f0a]">π Sign in to get started</p>
|
| 50 |
+
<p className="text-sm mt-3 text-[#86868b] leading-relaxed">Use Dev Login or sign in with Hugging Face</p>
|
| 51 |
+
</>
|
| 52 |
+
)}
|
| 53 |
+
</div>
|
| 54 |
+
) : (
|
| 55 |
+
messages.map((message, index) => (
|
| 56 |
+
<div
|
| 57 |
+
key={index}
|
| 58 |
+
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
| 59 |
+
>
|
| 60 |
+
<div
|
| 61 |
+
className={`max-w-[85%] rounded-2xl p-4 shadow-sm ${
|
| 62 |
+
message.role === 'user'
|
| 63 |
+
? 'bg-[#007aff] text-white'
|
| 64 |
+
: 'bg-[#2c2c2e] text-[#e5e5e7] border border-[#48484a]'
|
| 65 |
+
}`}
|
| 66 |
+
>
|
| 67 |
+
<div className="flex items-start space-x-3">
|
| 68 |
+
<div className="text-base flex-shrink-0">
|
| 69 |
+
{message.role === 'user' ? 'π€' : 'π€'}
|
| 70 |
+
</div>
|
| 71 |
+
<div className="flex-1 text-sm leading-relaxed">
|
| 72 |
+
{message.role === 'assistant' ? (
|
| 73 |
+
<ReactMarkdown
|
| 74 |
+
remarkPlugins={[remarkGfm]}
|
| 75 |
+
className="prose prose-invert prose-sm max-w-none"
|
| 76 |
+
>
|
| 77 |
+
{message.content}
|
| 78 |
+
</ReactMarkdown>
|
| 79 |
+
) : (
|
| 80 |
+
<p className="whitespace-pre-wrap font-medium">{message.content}</p>
|
| 81 |
+
)}
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
{message.timestamp && (
|
| 85 |
+
<div className="text-xs opacity-50 mt-2 text-right font-medium">
|
| 86 |
+
{new Date(message.timestamp).toLocaleTimeString()}
|
| 87 |
+
</div>
|
| 88 |
+
)}
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
))
|
| 92 |
+
)}
|
| 93 |
+
<div ref={messagesEndRef} />
|
| 94 |
+
</div>
|
| 95 |
+
|
| 96 |
+
{/* Input */}
|
| 97 |
+
<div className="border-t border-[#48484a] p-4 bg-[#28282a]">
|
| 98 |
+
<form onSubmit={handleSubmit} className="flex space-x-3">
|
| 99 |
+
<input
|
| 100 |
+
type="text"
|
| 101 |
+
value={input}
|
| 102 |
+
onChange={(e) => setInput(e.target.value)}
|
| 103 |
+
placeholder={isAuthenticated ? "Message AnyCoder..." : "π Please sign in first..."}
|
| 104 |
+
disabled={isGenerating || !isAuthenticated}
|
| 105 |
+
className="flex-1 px-4 py-3 bg-[#3a3a3c] text-[#e5e5e7] text-sm border border-[#48484a] rounded-xl focus:outline-none focus:ring-2 focus:ring-[#007aff] focus:border-transparent disabled:opacity-50 disabled:cursor-not-allowed placeholder-[#86868b] font-medium shadow-sm"
|
| 106 |
+
/>
|
| 107 |
+
<button
|
| 108 |
+
type="submit"
|
| 109 |
+
disabled={isGenerating || !input.trim() || !isAuthenticated}
|
| 110 |
+
className="px-5 py-3 bg-[#007aff] text-white text-sm rounded-xl hover:bg-[#0051d5] disabled:bg-[#48484a] disabled:cursor-not-allowed transition-all font-semibold shadow-md disabled:shadow-none active:scale-95"
|
| 111 |
+
>
|
| 112 |
+
{isGenerating ? 'β³' : 'β'}
|
| 113 |
+
</button>
|
| 114 |
+
</form>
|
| 115 |
+
</div>
|
| 116 |
+
</div>
|
| 117 |
+
);
|
| 118 |
+
}
|
| 119 |
+
|
frontend/src/components/CodeEditor.tsx
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { useEffect, useRef } from 'react';
|
| 4 |
+
import Editor from '@monaco-editor/react';
|
| 5 |
+
|
| 6 |
+
interface CodeEditorProps {
|
| 7 |
+
code: string;
|
| 8 |
+
language: string;
|
| 9 |
+
onChange?: (value: string) => void;
|
| 10 |
+
readOnly?: boolean;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
export default function CodeEditor({ code, language, onChange, readOnly = false }: CodeEditorProps) {
|
| 14 |
+
const editorRef = useRef<any>(null);
|
| 15 |
+
|
| 16 |
+
// Map our language names to Monaco language IDs
|
| 17 |
+
const getMonacoLanguage = (lang: string): string => {
|
| 18 |
+
const languageMap: Record<string, string> = {
|
| 19 |
+
'html': 'html',
|
| 20 |
+
'gradio': 'python',
|
| 21 |
+
'streamlit': 'python',
|
| 22 |
+
'transformers.js': 'javascript',
|
| 23 |
+
'react': 'typescript',
|
| 24 |
+
'comfyui': 'json',
|
| 25 |
+
};
|
| 26 |
+
return languageMap[lang] || 'plaintext';
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
const handleEditorDidMount = (editor: any) => {
|
| 30 |
+
editorRef.current = editor;
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
return (
|
| 34 |
+
<div className="h-full overflow-hidden">
|
| 35 |
+
<Editor
|
| 36 |
+
height="100%"
|
| 37 |
+
language={getMonacoLanguage(language)}
|
| 38 |
+
value={code}
|
| 39 |
+
onChange={(value) => onChange && onChange(value || '')}
|
| 40 |
+
theme="vs-dark"
|
| 41 |
+
options={{
|
| 42 |
+
readOnly,
|
| 43 |
+
minimap: { enabled: true },
|
| 44 |
+
fontSize: 14,
|
| 45 |
+
fontFamily: "'SF Mono', 'JetBrains Mono', 'Menlo', 'Monaco', 'Courier New', monospace",
|
| 46 |
+
wordWrap: 'on',
|
| 47 |
+
lineNumbers: 'on',
|
| 48 |
+
scrollBeyondLastLine: false,
|
| 49 |
+
automaticLayout: true,
|
| 50 |
+
tabSize: 2,
|
| 51 |
+
padding: { top: 16, bottom: 16 },
|
| 52 |
+
lineHeight: 22,
|
| 53 |
+
letterSpacing: 0.5,
|
| 54 |
+
}}
|
| 55 |
+
onMount={handleEditorDidMount}
|
| 56 |
+
/>
|
| 57 |
+
</div>
|
| 58 |
+
);
|
| 59 |
+
}
|
| 60 |
+
|
frontend/src/components/ControlPanel.tsx
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { useState, useEffect } from 'react';
|
| 4 |
+
import { apiClient } from '@/lib/api';
|
| 5 |
+
import type { Model, Language } from '@/types';
|
| 6 |
+
|
| 7 |
+
interface ControlPanelProps {
|
| 8 |
+
selectedLanguage: Language;
|
| 9 |
+
selectedModel: string;
|
| 10 |
+
onLanguageChange: (language: Language) => void;
|
| 11 |
+
onModelChange: (modelId: string) => void;
|
| 12 |
+
onDeploy: () => void;
|
| 13 |
+
onClear: () => void;
|
| 14 |
+
isGenerating: boolean;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export default function ControlPanel({
|
| 18 |
+
selectedLanguage,
|
| 19 |
+
selectedModel,
|
| 20 |
+
onLanguageChange,
|
| 21 |
+
onModelChange,
|
| 22 |
+
onDeploy,
|
| 23 |
+
onClear,
|
| 24 |
+
isGenerating,
|
| 25 |
+
}: ControlPanelProps) {
|
| 26 |
+
const [models, setModels] = useState<Model[]>([]);
|
| 27 |
+
const [languages, setLanguages] = useState<Language[]>([]);
|
| 28 |
+
|
| 29 |
+
useEffect(() => {
|
| 30 |
+
loadModels();
|
| 31 |
+
loadLanguages();
|
| 32 |
+
}, []);
|
| 33 |
+
|
| 34 |
+
const loadModels = async () => {
|
| 35 |
+
try {
|
| 36 |
+
const modelsList = await apiClient.getModels();
|
| 37 |
+
setModels(modelsList);
|
| 38 |
+
} catch (error) {
|
| 39 |
+
console.error('Failed to load models:', error);
|
| 40 |
+
}
|
| 41 |
+
};
|
| 42 |
+
|
| 43 |
+
const loadLanguages = async () => {
|
| 44 |
+
try {
|
| 45 |
+
const { languages: languagesList } = await apiClient.getLanguages();
|
| 46 |
+
setLanguages(languagesList);
|
| 47 |
+
} catch (error) {
|
| 48 |
+
console.error('Failed to load languages:', error);
|
| 49 |
+
}
|
| 50 |
+
};
|
| 51 |
+
|
| 52 |
+
return (
|
| 53 |
+
<div className="bg-[#28282a] p-5 space-y-6 h-full">
|
| 54 |
+
<h3 className="text-base font-semibold text-[#e5e5e7] tracking-tight mb-2">Configuration</h3>
|
| 55 |
+
|
| 56 |
+
{/* Language Selection */}
|
| 57 |
+
<div>
|
| 58 |
+
<label className="block text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">
|
| 59 |
+
Language
|
| 60 |
+
</label>
|
| 61 |
+
<select
|
| 62 |
+
value={selectedLanguage}
|
| 63 |
+
onChange={(e) => onLanguageChange(e.target.value as Language)}
|
| 64 |
+
disabled={isGenerating}
|
| 65 |
+
className="w-full px-4 py-3 bg-[#3a3a3c] text-[#e5e5e7] text-sm border border-[#48484a] rounded-xl focus:outline-none focus:ring-2 focus:ring-[#007aff] focus:border-transparent disabled:opacity-50 font-medium shadow-sm"
|
| 66 |
+
>
|
| 67 |
+
{languages.map((lang) => (
|
| 68 |
+
<option key={lang} value={lang} className="bg-[#3a3a3c]">
|
| 69 |
+
{lang.charAt(0).toUpperCase() + lang.slice(1)}
|
| 70 |
+
</option>
|
| 71 |
+
))}
|
| 72 |
+
</select>
|
| 73 |
+
</div>
|
| 74 |
+
|
| 75 |
+
{/* Model Selection */}
|
| 76 |
+
<div>
|
| 77 |
+
<label className="block text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">
|
| 78 |
+
AI Model
|
| 79 |
+
</label>
|
| 80 |
+
<select
|
| 81 |
+
value={selectedModel}
|
| 82 |
+
onChange={(e) => onModelChange(e.target.value)}
|
| 83 |
+
disabled={isGenerating}
|
| 84 |
+
className="w-full px-4 py-3 bg-[#3a3a3c] text-[#e5e5e7] text-sm border border-[#48484a] rounded-xl focus:outline-none focus:ring-2 focus:ring-[#007aff] focus:border-transparent disabled:opacity-50 font-medium shadow-sm"
|
| 85 |
+
>
|
| 86 |
+
{models.map((model) => (
|
| 87 |
+
<option key={model.id} value={model.id} className="bg-[#3a3a3c]">
|
| 88 |
+
{model.name}
|
| 89 |
+
</option>
|
| 90 |
+
))}
|
| 91 |
+
</select>
|
| 92 |
+
{models.find(m => m.id === selectedModel) && (
|
| 93 |
+
<p className="text-xs text-[#86868b] mt-3 leading-relaxed">
|
| 94 |
+
{models.find(m => m.id === selectedModel)?.description}
|
| 95 |
+
</p>
|
| 96 |
+
)}
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
{/* Action Buttons */}
|
| 100 |
+
<div className="flex flex-col space-y-3 pt-4">
|
| 101 |
+
<button
|
| 102 |
+
onClick={onDeploy}
|
| 103 |
+
disabled={isGenerating}
|
| 104 |
+
className="w-full px-4 py-3.5 bg-[#007aff] text-white text-sm rounded-xl hover:bg-[#0051d5] disabled:opacity-50 disabled:cursor-not-allowed transition-all font-semibold flex items-center justify-center space-x-2 shadow-md active:scale-95"
|
| 105 |
+
>
|
| 106 |
+
<span>π</span>
|
| 107 |
+
<span>Deploy</span>
|
| 108 |
+
</button>
|
| 109 |
+
<button
|
| 110 |
+
onClick={onClear}
|
| 111 |
+
disabled={isGenerating}
|
| 112 |
+
className="w-full px-4 py-3.5 bg-[#3a3a3c] text-[#e5e5e7] text-sm rounded-xl hover:bg-[#48484a] disabled:opacity-50 disabled:cursor-not-allowed transition-all font-semibold border border-[#48484a] flex items-center justify-center space-x-2 shadow-sm active:scale-95"
|
| 113 |
+
>
|
| 114 |
+
<span>ποΈ</span>
|
| 115 |
+
<span>Clear</span>
|
| 116 |
+
</button>
|
| 117 |
+
</div>
|
| 118 |
+
|
| 119 |
+
{/* Info Panel */}
|
| 120 |
+
<div className="mt-6 p-4 bg-[#2c2c2e] border border-[#48484a] rounded-xl shadow-sm">
|
| 121 |
+
<h4 className="text-sm font-semibold text-[#e5e5e7] mb-3 tracking-tight">π‘ Tips</h4>
|
| 122 |
+
<ul className="text-xs text-[#86868b] space-y-2 leading-relaxed">
|
| 123 |
+
<li>β’ Be specific in your requirements</li>
|
| 124 |
+
<li>β’ Try different AI models</li>
|
| 125 |
+
<li>β’ Edit code in the editor</li>
|
| 126 |
+
<li>β’ Deploy to HF Spaces</li>
|
| 127 |
+
</ul>
|
| 128 |
+
</div>
|
| 129 |
+
</div>
|
| 130 |
+
);
|
| 131 |
+
}
|
| 132 |
+
|
frontend/src/components/Header.tsx
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'use client';
|
| 2 |
+
|
| 3 |
+
import { useState, useEffect } from 'react';
|
| 4 |
+
import {
|
| 5 |
+
initializeOAuth,
|
| 6 |
+
loginWithHuggingFace,
|
| 7 |
+
loginDevMode,
|
| 8 |
+
logout,
|
| 9 |
+
getStoredUserInfo,
|
| 10 |
+
isAuthenticated,
|
| 11 |
+
isDevelopmentMode
|
| 12 |
+
} from '@/lib/auth';
|
| 13 |
+
import { apiClient } from '@/lib/api';
|
| 14 |
+
import type { OAuthUserInfo } from '@/lib/auth';
|
| 15 |
+
|
| 16 |
+
export default function Header() {
|
| 17 |
+
const [userInfo, setUserInfo] = useState<OAuthUserInfo | null>(null);
|
| 18 |
+
const [isLoading, setIsLoading] = useState(true);
|
| 19 |
+
const [showDevLogin, setShowDevLogin] = useState(false);
|
| 20 |
+
const [devUsername, setDevUsername] = useState('');
|
| 21 |
+
const isDevMode = isDevelopmentMode();
|
| 22 |
+
|
| 23 |
+
useEffect(() => {
|
| 24 |
+
handleOAuthInit();
|
| 25 |
+
}, []);
|
| 26 |
+
|
| 27 |
+
const handleOAuthInit = async () => {
|
| 28 |
+
setIsLoading(true);
|
| 29 |
+
try {
|
| 30 |
+
const oauthResult = await initializeOAuth();
|
| 31 |
+
|
| 32 |
+
if (oauthResult) {
|
| 33 |
+
setUserInfo(oauthResult.userInfo);
|
| 34 |
+
// Set token in API client
|
| 35 |
+
apiClient.setToken(oauthResult.accessToken);
|
| 36 |
+
} else {
|
| 37 |
+
// Check if we have stored user info
|
| 38 |
+
const storedUserInfo = getStoredUserInfo();
|
| 39 |
+
if (storedUserInfo) {
|
| 40 |
+
setUserInfo(storedUserInfo);
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
} catch (error) {
|
| 44 |
+
console.error('OAuth initialization error:', error);
|
| 45 |
+
} finally {
|
| 46 |
+
setIsLoading(false);
|
| 47 |
+
}
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const handleLogin = async () => {
|
| 51 |
+
try {
|
| 52 |
+
await loginWithHuggingFace();
|
| 53 |
+
} catch (error) {
|
| 54 |
+
console.error('Login failed:', error);
|
| 55 |
+
alert('Failed to start login process. Please try again.');
|
| 56 |
+
}
|
| 57 |
+
};
|
| 58 |
+
|
| 59 |
+
const handleLogout = () => {
|
| 60 |
+
logout();
|
| 61 |
+
apiClient.logout();
|
| 62 |
+
setUserInfo(null);
|
| 63 |
+
// Reload page to clear state
|
| 64 |
+
window.location.reload();
|
| 65 |
+
};
|
| 66 |
+
|
| 67 |
+
const handleDevLogin = () => {
|
| 68 |
+
if (!devUsername.trim()) {
|
| 69 |
+
alert('Please enter a username');
|
| 70 |
+
return;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
try {
|
| 74 |
+
const result = loginDevMode(devUsername);
|
| 75 |
+
setUserInfo(result.userInfo);
|
| 76 |
+
apiClient.setToken(result.accessToken);
|
| 77 |
+
setShowDevLogin(false);
|
| 78 |
+
setDevUsername('');
|
| 79 |
+
} catch (error) {
|
| 80 |
+
console.error('Dev login failed:', error);
|
| 81 |
+
alert('Failed to login in dev mode');
|
| 82 |
+
}
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
return (
|
| 86 |
+
<header className="bg-[#28282a] text-white border-b border-[#48484a]">
|
| 87 |
+
<div className="flex items-center justify-between px-5 h-14">
|
| 88 |
+
<div className="flex items-center space-x-3">
|
| 89 |
+
<div className="text-2xl">π</div>
|
| 90 |
+
<h1 className="text-base font-semibold text-[#e5e5e7] tracking-tight">AnyCoder</h1>
|
| 91 |
+
</div>
|
| 92 |
+
|
| 93 |
+
<div className="flex items-center space-x-4">
|
| 94 |
+
{isLoading ? (
|
| 95 |
+
<div className="px-4 py-2">
|
| 96 |
+
<span className="text-xs text-[#86868b] font-medium">Loading...</span>
|
| 97 |
+
</div>
|
| 98 |
+
) : userInfo ? (
|
| 99 |
+
<div className="flex items-center space-x-3">
|
| 100 |
+
{userInfo.avatarUrl && (
|
| 101 |
+
<img
|
| 102 |
+
src={userInfo.avatarUrl}
|
| 103 |
+
alt={userInfo.name}
|
| 104 |
+
className="w-7 h-7 rounded-full ring-2 ring-[#48484a]"
|
| 105 |
+
/>
|
| 106 |
+
)}
|
| 107 |
+
<span className="text-sm text-[#e5e5e7] font-medium">
|
| 108 |
+
{userInfo.preferredUsername || userInfo.name}
|
| 109 |
+
</span>
|
| 110 |
+
<button
|
| 111 |
+
onClick={handleLogout}
|
| 112 |
+
className="px-4 py-2 bg-[#3a3a3c] text-[#e5e5e7] text-xs rounded-lg hover:bg-[#48484a] transition-all border border-[#48484a] font-semibold shadow-sm active:scale-95"
|
| 113 |
+
>
|
| 114 |
+
Logout
|
| 115 |
+
</button>
|
| 116 |
+
</div>
|
| 117 |
+
) : (
|
| 118 |
+
<div className="flex items-center space-x-3">
|
| 119 |
+
{/* Dev Mode Login (only on localhost) */}
|
| 120 |
+
{isDevMode && (
|
| 121 |
+
<>
|
| 122 |
+
{showDevLogin ? (
|
| 123 |
+
<div className="flex items-center space-x-2 bg-[#3a3a3c] px-3 py-2 rounded-lg border border-[#ff9f0a] shadow-lg">
|
| 124 |
+
<span className="text-xs text-[#ff9f0a] font-semibold">DEV</span>
|
| 125 |
+
<input
|
| 126 |
+
type="text"
|
| 127 |
+
value={devUsername}
|
| 128 |
+
onChange={(e) => setDevUsername(e.target.value)}
|
| 129 |
+
onKeyPress={(e) => e.key === 'Enter' && handleDevLogin()}
|
| 130 |
+
placeholder="username"
|
| 131 |
+
className="px-3 py-1.5 rounded-lg text-xs bg-[#2c2c2e] text-[#e5e5e7] border border-[#48484a] focus:outline-none focus:ring-2 focus:ring-[#ff9f0a] focus:border-transparent w-28 font-medium"
|
| 132 |
+
autoFocus
|
| 133 |
+
/>
|
| 134 |
+
<button
|
| 135 |
+
onClick={handleDevLogin}
|
| 136 |
+
className="px-3 py-1.5 bg-[#ff9f0a] text-white rounded-lg hover:bg-[#ff8800] text-xs font-semibold shadow-sm active:scale-95"
|
| 137 |
+
>
|
| 138 |
+
OK
|
| 139 |
+
</button>
|
| 140 |
+
<button
|
| 141 |
+
onClick={() => {
|
| 142 |
+
setShowDevLogin(false);
|
| 143 |
+
setDevUsername('');
|
| 144 |
+
}}
|
| 145 |
+
className="text-[#86868b] hover:text-[#e5e5e7] text-sm transition-colors"
|
| 146 |
+
>
|
| 147 |
+
β
|
| 148 |
+
</button>
|
| 149 |
+
</div>
|
| 150 |
+
) : (
|
| 151 |
+
<button
|
| 152 |
+
onClick={() => setShowDevLogin(true)}
|
| 153 |
+
className="px-4 py-2 bg-[#ff9f0a] text-white rounded-lg hover:bg-[#ff8800] transition-all text-xs flex items-center space-x-2 font-semibold shadow-sm active:scale-95"
|
| 154 |
+
title="Dev Mode (localhost)"
|
| 155 |
+
>
|
| 156 |
+
<span>π§</span>
|
| 157 |
+
<span>Dev Login</span>
|
| 158 |
+
</button>
|
| 159 |
+
)}
|
| 160 |
+
<span className="text-[#86868b] text-xs font-medium">or</span>
|
| 161 |
+
</>
|
| 162 |
+
)}
|
| 163 |
+
|
| 164 |
+
{/* OAuth Login */}
|
| 165 |
+
<button
|
| 166 |
+
onClick={handleLogin}
|
| 167 |
+
className="px-4 py-2 bg-[#007aff] text-white rounded-lg hover:bg-[#0051d5] transition-all text-xs flex items-center space-x-2 font-semibold shadow-md active:scale-95"
|
| 168 |
+
>
|
| 169 |
+
<span>π€</span>
|
| 170 |
+
<span>Sign in</span>
|
| 171 |
+
</button>
|
| 172 |
+
</div>
|
| 173 |
+
)}
|
| 174 |
+
</div>
|
| 175 |
+
</div>
|
| 176 |
+
</header>
|
| 177 |
+
);
|
| 178 |
+
}
|
| 179 |
+
|
frontend/src/types/index.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Type definitions for AnyCoder frontend
|
| 2 |
+
|
| 3 |
+
export interface Model {
|
| 4 |
+
name: string;
|
| 5 |
+
id: string;
|
| 6 |
+
description: string;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
export interface Message {
|
| 10 |
+
role: 'user' | 'assistant' | 'system';
|
| 11 |
+
content: string;
|
| 12 |
+
timestamp?: string;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
export interface CodeGenerationRequest {
|
| 16 |
+
query: string;
|
| 17 |
+
language: string;
|
| 18 |
+
model_id: string;
|
| 19 |
+
provider: string;
|
| 20 |
+
history: string[][];
|
| 21 |
+
agent_mode: boolean;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
export interface CodeGenerationResponse {
|
| 25 |
+
code: string;
|
| 26 |
+
history: string[][];
|
| 27 |
+
status: string;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
export interface StreamChunk {
|
| 31 |
+
type: 'chunk' | 'complete' | 'error' | 'status';
|
| 32 |
+
content?: string;
|
| 33 |
+
code?: string;
|
| 34 |
+
message?: string;
|
| 35 |
+
progress?: number;
|
| 36 |
+
timestamp?: string;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
export interface AuthStatus {
|
| 40 |
+
authenticated: boolean;
|
| 41 |
+
username?: string;
|
| 42 |
+
message: string;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
export interface DeploymentRequest {
|
| 46 |
+
code: string;
|
| 47 |
+
space_name?: string;
|
| 48 |
+
language: string;
|
| 49 |
+
requirements?: string;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
export interface DeploymentResponse {
|
| 53 |
+
success: boolean;
|
| 54 |
+
space_url?: string;
|
| 55 |
+
message: string;
|
| 56 |
+
dev_mode?: boolean;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
export type Language = 'html' | 'gradio' | 'transformers.js' | 'streamlit' | 'comfyui' | 'react';
|
| 60 |
+
|
frontend/tailwind.config.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** @type {import('tailwindcss').Config} */
|
| 2 |
+
module.exports = {
|
| 3 |
+
content: [
|
| 4 |
+
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
| 5 |
+
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
| 6 |
+
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
| 7 |
+
],
|
| 8 |
+
theme: {
|
| 9 |
+
extend: {
|
| 10 |
+
colors: {
|
| 11 |
+
primary: {
|
| 12 |
+
50: '#eff6ff',
|
| 13 |
+
100: '#dbeafe',
|
| 14 |
+
200: '#bfdbfe',
|
| 15 |
+
300: '#93c5fd',
|
| 16 |
+
400: '#60a5fa',
|
| 17 |
+
500: '#3b82f6',
|
| 18 |
+
600: '#2563eb',
|
| 19 |
+
700: '#1d4ed8',
|
| 20 |
+
800: '#1e40af',
|
| 21 |
+
900: '#1e3a8a',
|
| 22 |
+
},
|
| 23 |
+
},
|
| 24 |
+
},
|
| 25 |
+
},
|
| 26 |
+
plugins: [],
|
| 27 |
+
darkMode: 'class',
|
| 28 |
+
}
|
| 29 |
+
|
frontend/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"lib": ["dom", "dom.iterable", "esnext"],
|
| 4 |
+
"allowJs": true,
|
| 5 |
+
"skipLibCheck": true,
|
| 6 |
+
"strict": true,
|
| 7 |
+
"noEmit": true,
|
| 8 |
+
"esModuleInterop": true,
|
| 9 |
+
"module": "esnext",
|
| 10 |
+
"moduleResolution": "bundler",
|
| 11 |
+
"resolveJsonModule": true,
|
| 12 |
+
"isolatedModules": true,
|
| 13 |
+
"jsx": "preserve",
|
| 14 |
+
"incremental": true,
|
| 15 |
+
"plugins": [
|
| 16 |
+
{
|
| 17 |
+
"name": "next"
|
| 18 |
+
}
|
| 19 |
+
],
|
| 20 |
+
"paths": {
|
| 21 |
+
"@/*": ["./src/*"]
|
| 22 |
+
}
|
| 23 |
+
},
|
| 24 |
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
| 25 |
+
"exclude": ["node_modules"]
|
| 26 |
+
}
|
| 27 |
+
|
start_backend.sh
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Start the FastAPI backend
|
| 3 |
+
|
| 4 |
+
echo "Starting AnyCoder FastAPI Backend..."
|
| 5 |
+
echo "API will be available at: http://localhost:8000"
|
| 6 |
+
echo "API docs at: http://localhost:8000/docs"
|
| 7 |
+
echo ""
|
| 8 |
+
|
| 9 |
+
# Check if HF_TOKEN is set
|
| 10 |
+
if [ -z "$HF_TOKEN" ]; then
|
| 11 |
+
echo "β οΈ WARNING: HF_TOKEN environment variable is not set!"
|
| 12 |
+
echo "Please set it with: export HF_TOKEN=your_token_here"
|
| 13 |
+
echo ""
|
| 14 |
+
fi
|
| 15 |
+
|
| 16 |
+
# Activate virtual environment
|
| 17 |
+
source /Users/ahsenkhaliq/anycoder/.venv/bin/activate
|
| 18 |
+
|
| 19 |
+
# Start the backend
|
| 20 |
+
python backend_api.py
|
| 21 |
+
|
start_frontend.sh
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Start the Next.js frontend
|
| 3 |
+
|
| 4 |
+
echo "Starting AnyCoder Frontend..."
|
| 5 |
+
echo "Frontend will be available at: http://localhost:3000"
|
| 6 |
+
echo ""
|
| 7 |
+
|
| 8 |
+
cd frontend
|
| 9 |
+
|
| 10 |
+
# Install dependencies if node_modules doesn't exist
|
| 11 |
+
if [ ! -d "node_modules" ]; then
|
| 12 |
+
echo "Installing dependencies..."
|
| 13 |
+
npm install
|
| 14 |
+
fi
|
| 15 |
+
|
| 16 |
+
# Start the development server
|
| 17 |
+
npm run dev
|
| 18 |
+
|
start_fullstack.sh
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Start both backend and frontend in separate terminal windows
|
| 3 |
+
|
| 4 |
+
echo "π Starting AnyCoder Full-Stack Application..."
|
| 5 |
+
echo ""
|
| 6 |
+
|
| 7 |
+
# Function to check if a command exists
|
| 8 |
+
command_exists() {
|
| 9 |
+
command -v "$1" >/dev/null 2>&1
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
# Check for required tools
|
| 13 |
+
if ! command_exists python3; then
|
| 14 |
+
echo "β Python 3 is not installed"
|
| 15 |
+
exit 1
|
| 16 |
+
fi
|
| 17 |
+
|
| 18 |
+
if ! command_exists node; then
|
| 19 |
+
echo "β Node.js is not installed"
|
| 20 |
+
exit 1
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
# Make scripts executable
|
| 24 |
+
chmod +x start_backend.sh
|
| 25 |
+
chmod +x start_frontend.sh
|
| 26 |
+
|
| 27 |
+
echo "π¦ Starting Backend..."
|
| 28 |
+
# Start backend in background with venv activated
|
| 29 |
+
(source /Users/ahsenkhaliq/anycoder/.venv/bin/activate && python backend_api.py) &
|
| 30 |
+
BACKEND_PID=$!
|
| 31 |
+
|
| 32 |
+
# Wait for backend to start
|
| 33 |
+
sleep 3
|
| 34 |
+
|
| 35 |
+
echo ""
|
| 36 |
+
echo "π¨ Starting Frontend..."
|
| 37 |
+
# Start frontend in background
|
| 38 |
+
./start_frontend.sh &
|
| 39 |
+
FRONTEND_PID=$!
|
| 40 |
+
|
| 41 |
+
echo ""
|
| 42 |
+
echo "β
Full-stack application started!"
|
| 43 |
+
echo ""
|
| 44 |
+
echo "π Backend API: http://localhost:8000"
|
| 45 |
+
echo "π API Docs: http://localhost:8000/docs"
|
| 46 |
+
echo "π Frontend: http://localhost:3000"
|
| 47 |
+
echo ""
|
| 48 |
+
echo "Press Ctrl+C to stop both services"
|
| 49 |
+
|
| 50 |
+
# Wait for Ctrl+C
|
| 51 |
+
trap "kill $BACKEND_PID $FRONTEND_PID; exit" INT
|
| 52 |
+
wait
|
| 53 |
+
|